Turn all function static non-POD variables into global POD variables

Function static non-POD data causes problems with DLL lifetime.
This pull request turns all static info tables into strict POD
tables. Specifically, the capabilities/extensions field of
opcode/operand/extended-instruction table are turned into two
fields, one for the count and the other a pointer to an array of
capabilities/extensions. CapabilitySet/EnumSet are not used in
the static table anymore, but they are still used for checking
inclusion by constructing on the fly, which should be cheap for
the majority cases.

Also moves all these tables into the global namespace to avoid
C++11 function static thread-safe initialization overhead.
This commit is contained in:
Lei Zhang 2017-10-25 12:15:51 -04:00
parent 90862fe4b1
commit 063dbea0f1
13 changed files with 297 additions and 229 deletions

View File

@ -46,6 +46,9 @@ class EnumSet {
EnumSet(std::initializer_list<EnumType> cs) {
for (auto c : cs) Add(c);
}
EnumSet(uint32_t count, const EnumType* ptr) {
for (uint32_t i = 0; i < count; ++i) Add(ptr[i]);
}
// Copy constructor.
EnumSet(const EnumSet& other) { *this = other; }
// Move constructor. The moved-from set is emptied.
@ -95,15 +98,12 @@ class EnumSet {
bool HasAnyOf(const EnumSet<EnumType>& in_set) const {
if (in_set.IsEmpty()) return true;
if (mask_ & in_set.mask_)
return true;
if (mask_ & in_set.mask_) return true;
if (!overflow_ || !in_set.overflow_)
return false;
if (!overflow_ || !in_set.overflow_) return false;
for (uint32_t item : *in_set.overflow_) {
if (overflow_->find(item) != overflow_->end())
return true;
if (overflow_->find(item) != overflow_->end()) return true;
}
return false;

View File

@ -19,9 +19,9 @@
#include "spirv/1.1/spirv.h"
namespace libspirv {
#include "extensions.h"
enum class Extension;
namespace libspirv {
// Finds Extension enum corresponding to |str|. Returns false if not found.
bool GetExtensionFromString(const std::string& str, Extension* extension);

View File

@ -23,40 +23,17 @@
#include "macro.h"
spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
spv_target_env env) {
if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER;
#include "glsl.std.450.insts-1.0.inc" // defines glsl_entries
#include "opencl.std.insts-1.0.inc" // defines opencl_entries
static const spv_ext_inst_desc_t glslStd450Entries_1_0[] = {
#include "glsl.std.450.insts-1.0.inc"
};
static const spv_ext_inst_desc_t openclEntries_1_0[] = {
#include "opencl.std.insts-1.0.inc"
};
static const spv_ext_inst_desc_t
spv_amd_shader_explicit_vertex_parameter_entries[] = {
#include "spv-amd-shader-explicit-vertex-parameter.insts.inc"
};
static const spv_ext_inst_desc_t spv_amd_shader_trinary_minmax_entries[] = {
#include "spv-amd-shader-trinary-minmax.insts.inc"
};
static const spv_ext_inst_desc_t spv_amd_gcn_shader_entries[] = {
#include "spv-amd-gcn-shader.insts.inc"
};
static const spv_ext_inst_desc_t spv_amd_shader_ballot_entries[] = {
#include "spv-amd-shader-ballot.insts.inc"
};
#include "spv-amd-shader-explicit-vertex-parameter.insts.inc"
#include "spv-amd-shader-trinary-minmax.insts.inc"
static const spv_ext_inst_group_t groups_1_0[] = {
{SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glslStd450Entries_1_0),
glslStd450Entries_1_0},
{SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(openclEntries_1_0),
openclEntries_1_0},
static const spv_ext_inst_group_t kGroups_1_0[] = {
{SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries},
{SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries},
{SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER,
ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries),
spv_amd_shader_explicit_vertex_parameter_entries},
@ -66,12 +43,15 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
{SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries},
{SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
ARRAY_SIZE(spv_amd_shader_ballot_entries),
spv_amd_shader_ballot_entries},
};
ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
};
static const spv_ext_inst_table_t table_1_0 = {ARRAY_SIZE(groups_1_0),
groups_1_0};
static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
kGroups_1_0};
spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
spv_target_env env) {
if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER;
switch (env) {
// The extended instruction sets are all version 1.0 so far.
@ -86,7 +66,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
case SPV_ENV_OPENGL_4_2:
case SPV_ENV_OPENGL_4_3:
case SPV_ENV_OPENGL_4_5:
*pExtInstTable = &table_1_0;
*pExtInstTable = &kTable_1_0;
return SPV_SUCCESS;
default:
assert(0 && "Unknown spv_target_env in spvExtInstTableGet()");

View File

@ -23,7 +23,7 @@
namespace libspirv {
// The known SPIR-V extensions.
enum class Extension {
enum Extension {
#include "extension_enum.inc"
};

View File

@ -32,13 +32,16 @@ struct OpcodeDescPtrLen {
uint32_t len;
};
OpcodeDescPtrLen getOpcodeTableEntries_1_2() {
static const spv_opcode_desc_t opcodeTableEntries_1_2[] = {
#include "core.insts-1.2.inc"
};
#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
return {opcodeTableEntries_1_2, ARRAY_SIZE(opcodeTableEntries_1_2)};
}
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};
// Represents a vendor tool entry in the SPIR-V XML Regsitry.
struct VendorTool {
@ -84,20 +87,6 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable,
// Descriptions of each opcode. Each entry describes the format of the
// instruction that follows a particular opcode.
static const spv_opcode_desc_t opcodeTableEntries_1_0[] = {
#include "core.insts-1.0.inc"
};
static const spv_opcode_desc_t opcodeTableEntries_1_1[] = {
#include "core.insts-1.1.inc"
};
const auto ptr_len = getOpcodeTableEntries_1_2();
static const spv_opcode_table_t table_1_0 = {
ARRAY_SIZE(opcodeTableEntries_1_0), opcodeTableEntries_1_0};
static const spv_opcode_table_t table_1_1 = {
ARRAY_SIZE(opcodeTableEntries_1_1), opcodeTableEntries_1_1};
static const spv_opcode_table_t table_1_2 = {ptr_len.len, ptr_len.ptr};
switch (env) {
case SPV_ENV_UNIVERSAL_1_0:
@ -108,14 +97,14 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable,
case SPV_ENV_OPENGL_4_2:
case SPV_ENV_OPENGL_4_3:
case SPV_ENV_OPENGL_4_5:
*pInstTable = &table_1_0;
*pInstTable = &kTable_1_0;
return SPV_SUCCESS;
case SPV_ENV_UNIVERSAL_1_1:
*pInstTable = &table_1_1;
*pInstTable = &kTable_1_1;
return SPV_SUCCESS;
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_OPENCL_2_2:
*pInstTable = &table_1_2;
*pInstTable = &kTable_1_2;
return SPV_SUCCESS;
}
assert(0 && "Unknown spv_target_env in spvOpcodeTableGet()");
@ -183,10 +172,9 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
const char* spvOpcodeString(const SpvOp opcode) {
// Use the latest SPIR-V version, which should be backward-compatible with all
// previous ones.
const auto entries = getOpcodeTableEntries_1_2();
for (uint32_t i = 0; i < entries.len; ++i) {
if (entries.ptr[i].opcode == opcode) return entries.ptr[i].name;
for (uint32_t i = 0; i < ARRAY_SIZE(kOpcodeTableEntries_1_2); ++i) {
if (kOpcodeTableEntries_1_2[i].opcode == opcode)
return kOpcodeTableEntries_1_2[i].name;
}
assert(0 && "Unreachable!");

View File

@ -20,24 +20,24 @@
#include "macro.h"
spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
spv_target_env env) {
if (!pOperandTable) return SPV_ERROR_INVALID_POINTER;
#include "operand.kinds-1.0.inc"
#include "operand.kinds-1.1.inc"
#include "operand.kinds-1.2.inc"
static const spv_operand_table_t table_1_0 = {
static const spv_operand_table_t kTable_1_0 = {
ARRAY_SIZE(pygen_variable_OperandInfoTable_1_0),
pygen_variable_OperandInfoTable_1_0};
static const spv_operand_table_t table_1_1 = {
static const spv_operand_table_t kTable_1_1 = {
ARRAY_SIZE(pygen_variable_OperandInfoTable_1_1),
pygen_variable_OperandInfoTable_1_1};
static const spv_operand_table_t table_1_2 = {
static const spv_operand_table_t kTable_1_2 = {
ARRAY_SIZE(pygen_variable_OperandInfoTable_1_2),
pygen_variable_OperandInfoTable_1_2};
spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
spv_target_env env) {
if (!pOperandTable) return SPV_ERROR_INVALID_POINTER;
switch (env) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_VULKAN_1_0:
@ -47,14 +47,14 @@ spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
case SPV_ENV_OPENGL_4_2:
case SPV_ENV_OPENGL_4_3:
case SPV_ENV_OPENGL_4_5:
*pOperandTable = &table_1_0;
*pOperandTable = &kTable_1_0;
return SPV_SUCCESS;
case SPV_ENV_UNIVERSAL_1_1:
*pOperandTable = &table_1_1;
*pOperandTable = &kTable_1_1;
return SPV_SUCCESS;
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_OPENCL_2_2:
*pOperandTable = &table_1_2;
*pOperandTable = &kTable_1_2;
return SPV_SUCCESS;
}
assert(0 && "Unknown spv_target_env in spvOperandTableGet()");

View File

@ -24,7 +24,8 @@
typedef struct spv_opcode_desc_t {
const char* name;
const SpvOp opcode;
const libspirv::CapabilitySet capabilities;
const uint32_t numCapabilities;
const SpvCapability* capabilities;
// operandTypes[0..numTypes-1] describe logical operands for the instruction.
// The operand types include result id and result-type id, followed by
// the types of arguments.
@ -37,12 +38,14 @@ typedef struct spv_opcode_desc_t {
typedef struct spv_operand_desc_t {
const char* name;
const uint32_t value;
const libspirv::CapabilitySet capabilities;
const uint32_t numCapabilities;
const SpvCapability* capabilities;
// A set of extensions that enable this feature. If empty then this operand
// value is always enabled, i.e. it's in core. The assembler, binary parser,
// and disassembler ignore this rule, so you can freely process invalid
// modules.
const libspirv::ExtensionSet extensions;
const uint32_t numExtensions;
const libspirv::Extension* extensions;
const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger?
} spv_operand_desc_t;
@ -55,7 +58,8 @@ typedef struct spv_operand_desc_group_t {
typedef struct spv_ext_inst_desc_t {
const char* name;
const uint32_t ext_inst;
const libspirv::CapabilitySet capabilities;
const uint32_t numCapabilities;
const SpvCapability* capabilities;
const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger?
} spv_ext_inst_desc_t;

View File

@ -215,15 +215,13 @@ bool ValidationState_t::IsDefinedId(uint32_t id) const {
const Instruction* ValidationState_t::FindDef(uint32_t id) const {
auto it = all_definitions_.find(id);
if (it == all_definitions_.end())
return nullptr;
if (it == all_definitions_.end()) return nullptr;
return it->second;
}
Instruction* ValidationState_t::FindDef(uint32_t id) {
auto it = all_definitions_.find(id);
if (it == all_definitions_.end())
return nullptr;
if (it == all_definitions_.end()) return nullptr;
return it->second;
}
@ -283,8 +281,8 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
spv_operand_desc desc;
if (SPV_SUCCESS ==
grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) {
desc->capabilities.ForEach(
[this](SpvCapability c) { RegisterCapability(c); });
CapabilitySet(desc->numCapabilities, desc->capabilities)
.ForEach([this](SpvCapability c) { RegisterCapability(c); });
}
switch (cap) {
@ -464,8 +462,7 @@ uint32_t ValidationState_t::GetComponentType(uint32_t id) const {
break;
}
if (inst->type_id())
return GetComponentType(inst->type_id());
if (inst->type_id()) return GetComponentType(inst->type_id());
assert(0);
return 0;
@ -489,8 +486,7 @@ uint32_t ValidationState_t::GetDimension(uint32_t id) const {
break;
}
if (inst->type_id())
return GetDimension(inst->type_id());
if (inst->type_id()) return GetDimension(inst->type_id());
assert(0);
return 0;
@ -504,8 +500,7 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const {
if (inst->opcode() == SpvOpTypeFloat || inst->opcode() == SpvOpTypeInt)
return inst->word(2);
if (inst->opcode() == SpvOpTypeBool)
return 1;
if (inst->opcode() == SpvOpTypeBool) return 1;
assert(0);
return 0;
@ -607,16 +602,15 @@ bool ValidationState_t::IsFloatMatrixType(uint32_t id) const {
return false;
}
bool ValidationState_t::GetMatrixTypeInfo(
uint32_t id, uint32_t* num_rows, uint32_t* num_cols,
uint32_t* column_type, uint32_t* component_type) const {
if (!id)
return false;
bool ValidationState_t::GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows,
uint32_t* num_cols,
uint32_t* column_type,
uint32_t* component_type) const {
if (!id) return false;
const Instruction* mat_inst = FindDef(id);
assert(mat_inst);
if (mat_inst->opcode() != SpvOpTypeMatrix)
return false;
if (mat_inst->opcode() != SpvOpTypeMatrix) return false;
const uint32_t vec_type = mat_inst->word(2);
const Instruction* vec_inst = FindDef(vec_type);
@ -638,19 +632,16 @@ bool ValidationState_t::GetMatrixTypeInfo(
bool ValidationState_t::GetStructMemberTypes(
uint32_t struct_type_id, std::vector<uint32_t>* member_types) const {
member_types->clear();
if (!struct_type_id)
return false;
if (!struct_type_id) return false;
const Instruction* inst = FindDef(struct_type_id);
assert(inst);
if (inst->opcode() != SpvOpTypeStruct)
return false;
if (inst->opcode() != SpvOpTypeStruct) return false;
*member_types = std::vector<uint32_t>(inst->words().cbegin() + 2,
inst->words().cend());
*member_types =
std::vector<uint32_t>(inst->words().cbegin() + 2, inst->words().cend());
if (member_types->empty())
return false;
if (member_types->empty()) return false;
return true;
}
@ -661,15 +652,13 @@ bool ValidationState_t::IsPointerType(uint32_t id) const {
return inst->opcode() == SpvOpTypePointer;
}
bool ValidationState_t::GetPointerTypeInfo(
uint32_t id, uint32_t* data_type, uint32_t* storage_class) const {
if (!id)
return false;
bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
uint32_t* storage_class) const {
if (!id) return false;
const Instruction* inst = FindDef(id);
assert(inst);
if (inst->opcode() != SpvOpTypePointer)
return false;
if (inst->opcode() != SpvOpTypePointer) return false;
*storage_class = inst->word(2);
*data_type = inst->word(3);
@ -677,12 +666,11 @@ bool ValidationState_t::GetPointerTypeInfo(
}
uint32_t ValidationState_t::GetOperandTypeId(
const spv_parsed_instruction_t* inst,
size_t operand_index) const {
const spv_parsed_instruction_t* inst, size_t operand_index) const {
assert(operand_index < inst->num_operands);
const spv_parsed_operand_t& operand = inst->operands[operand_index];
assert(operand.num_words == 1);
return GetTypeId(inst->words[operand.offset]);
}
} /// namespace libspirv
} // namespace libspirv

View File

@ -80,17 +80,18 @@ bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
// Checks if |capability| was enabled by extension.
bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
spv_operand_desc operand_desc = nullptr;
_.grammar().lookupOperand(
SPV_OPERAND_TYPE_CAPABILITY, capability, &operand_desc);
_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
&operand_desc);
// operand_desc is expected to be not null, otherwise validator would have
// failed at an earlier stage. This 'assert' is 'just in case'.
assert(operand_desc);
if (operand_desc->extensions.IsEmpty())
return false;
ExtensionSet operand_exts(operand_desc->numExtensions,
operand_desc->extensions);
if (operand_exts.IsEmpty()) return false;
return _.HasAnyOfExtensions(operand_desc->extensions);
return _.HasAnyOfExtensions(operand_exts);
}
} // namespace
@ -100,8 +101,7 @@ bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
spv_result_t CapabilityPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
if (opcode != SpvOpCapability)
return SPV_SUCCESS;
if (opcode != SpvOpCapability) return SPV_SUCCESS;
assert(inst->num_operands == 1);

View File

@ -94,7 +94,8 @@ CapabilitySet RequiredCapabilities(const ValidationState_t& state,
spv_operand_desc operand_desc;
const auto ret = state.grammar().lookupOperand(type, operand, &operand_desc);
if (ret == SPV_SUCCESS) {
CapabilitySet result = operand_desc->capabilities;
CapabilitySet result(operand_desc->numCapabilities,
operand_desc->capabilities);
// Allow FPRoundingMode decoration if requested
if (state.features().free_fp_rounding_mode &&
@ -115,7 +116,7 @@ ExtensionSet RequiredExtensions(const ValidationState_t& state,
if (state.grammar().lookupOperand(type, operand, &operand_desc) ==
SPV_SUCCESS) {
assert(operand_desc);
return operand_desc->extensions;
return {operand_desc->numExtensions, operand_desc->extensions};
}
return ExtensionSet();
@ -127,14 +128,17 @@ namespace libspirv {
spv_result_t CapabilityCheck(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
spv_opcode_desc opcode_desc;
spv_opcode_desc opcode_desc = {};
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
if (SPV_SUCCESS == _.grammar().lookupOpcode(opcode, &opcode_desc) &&
!_.HasAnyOfCapabilities(opcode_desc->capabilities))
if (SPV_SUCCESS == _.grammar().lookupOpcode(opcode, &opcode_desc)) {
CapabilitySet opcode_caps(opcode_desc->numCapabilities,
opcode_desc->capabilities);
if (!_.HasAnyOfCapabilities(opcode_caps))
return _.diag(SPV_ERROR_INVALID_CAPABILITY)
<< "Opcode " << spvOpcodeString(opcode)
<< " requires one of these capabilities: "
<< ToString(opcode_desc->capabilities, _.grammar());
<< ToString(opcode_caps, _.grammar());
}
for (int i = 0; i < inst->num_operands; ++i) {
const auto& operand = inst->operands[i];
const auto word = inst->words[operand.offset];
@ -194,8 +198,8 @@ spv_result_t ReservedCheck(ValidationState_t& _,
case SpvOpImageSparseSampleProjExplicitLod:
case SpvOpImageSparseSampleProjDrefImplicitLod:
case SpvOpImageSparseSampleProjDrefExplicitLod:
return _.diag(SPV_ERROR_INVALID_VALUE) << spvOpcodeString(opcode)
<< " is reserved for future use.";
return _.diag(SPV_ERROR_INVALID_VALUE)
<< spvOpcodeString(opcode) << " is reserved for future use.";
default:
return SPV_SUCCESS;
}
@ -393,8 +397,7 @@ void CheckIfKnownExtension(ValidationState_t& _,
spv_result_t InstructionPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
if (opcode == SpvOpExtension)
CheckIfKnownExtension(_, inst);
if (opcode == SpvOpExtension) CheckIfKnownExtension(_, inst);
if (opcode == SpvOpCapability) {
_.RegisterCapability(
static_cast<SpvCapability>(inst->words[inst->operands[0].offset]));

View File

@ -37,8 +37,9 @@ TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) {
spv_opcode_desc entry;
ASSERT_EQ(SPV_SUCCESS,
spvOpcodeTableValueLookup(opcodeTable, GetParam().opcode, &entry));
EXPECT_EQ(ElementsIn(GetParam().capabilities),
ElementsIn(entry->capabilities));
EXPECT_EQ(
ElementsIn(GetParam().capabilities),
ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities)));
}
INSTANTIATE_TEST_CASE_P(

View File

@ -51,7 +51,8 @@ TEST_P(EnumCapabilityTest, Sample) {
ASSERT_EQ(SPV_SUCCESS,
spvOperandTableValueLookup(operandTable, get<1>(GetParam()).type,
get<1>(GetParam()).value, &entry));
EXPECT_THAT(ElementsIn(entry->capabilities),
EXPECT_THAT(
ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities)),
Eq(ElementsIn(get<1>(GetParam()).expected_capabilities)))
<< " capability value " << get<1>(GetParam()).value;
}
@ -279,8 +280,7 @@ INSTANTIATE_TEST_CASE_P(
// See SPIR-V Section 3.12 Image Channel Order
INSTANTIATE_TEST_CASE_P(
ImageChannelOrder, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel),
@ -296,7 +296,8 @@ INSTANTIATE_TEST_CASE_P(
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil,
Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel),

View File

@ -24,7 +24,7 @@ import re
# Prefix for all C variables generated by this script.
PYGEN_VARIABLE_PREFIX = 'pygen_variable'
# Extensions to recognize, but which don't necessarily come from the SPIRV-V
# Extensions to recognize, but which don't necessarily come from the SPIR-V
# core grammar. Get this list from the SPIR-V registery web page.
EXTENSIONS_FROM_SPIRV_REGISTRY = """
SPV_AMD_shader_explicit_vertex_parameter
@ -56,7 +56,6 @@ SPV_AMD_shader_fragment_mask
"""
def make_path_to_file(f):
"""Makes all ancestor directories to the given file, if they
don't yet exist.
@ -86,6 +85,33 @@ def compose_capability_list(caps):
return "{" + ", ".join(['SpvCapability{}'.format(c) for c in caps]) + "}"
def get_capability_array_name(caps, version):
"""Returns the name of the array containing all the given capabilities.
Args:
- caps: a sequence of capability names
"""
if not caps:
return 'nullptr'
else:
return '{}_caps_{}_{}'.format(
PYGEN_VARIABLE_PREFIX, ''.join(caps), version)
def generate_capability_arrays(caps, version):
"""Returns the arrays of capabilities.
Arguments:
- caps: a sequence of sequence of capability names
"""
caps = sorted(set([tuple(c) for c in caps if c]))
arrays = [
'static const SpvCapability {}[] = {};'.format(
get_capability_array_name(c, version), compose_capability_list(c))
for c in caps]
return '\n'.join(arrays)
def compose_extension_list(exts):
"""Returns a string containing a braced list of extensions as enums.
@ -99,6 +125,33 @@ def compose_extension_list(exts):
['libspirv::Extension::k{}'.format(e) for e in exts]) + "}"
def get_extension_array_name(extensions, version):
"""Returns the name of the array containing all the given extensions.
Args:
- extensions: a sequence of extension names
"""
if not extensions:
return 'nullptr'
else:
return '{}_exts_{}_{}'.format(
PYGEN_VARIABLE_PREFIX, ''.join(extensions), version)
def generate_extension_arrays(extensions, version):
"""Returns the arrays of extensions.
Arguments:
- caps: a sequence of sequence of extension names
"""
extensions = sorted(set([tuple(e) for e in extensions if e]))
arrays = [
'static const libspirv::Extension {}[] = {};'.format(
get_extension_array_name(e, version), compose_extension_list(e))
for e in extensions]
return '\n'.join(arrays)
def convert_operand_kind(operand_tuple):
"""Returns the corresponding operand type used in spirv-tools for
the given operand kind and quantifier used in the JSON grammar.
@ -166,17 +219,19 @@ class InstInitializer(object):
"""Instances holds a SPIR-V instruction suitable for printing as
the initializer for spv_opcode_desc_t."""
def __init__(self, opname, caps, operands):
def __init__(self, opname, caps, operands, version):
"""Initialization.
Arguments:
- opname: opcode name (with the 'Op' prefix)
- caps: a sequence of capability names required by this opcode
- operands: a sequence of (operand-kind, operand-quantifier) tuples
- version: version of the instruction set
"""
assert opname.startswith('Op')
self.opname = opname[2:] # Remove the "Op" prefix.
self.caps_mask = compose_capability_list(caps)
self.num_caps = len(caps)
self.caps_mask = get_capability_array_name(caps, version)
self.operands = [convert_operand_kind(o) for o in operands]
self.fix_syntax()
@ -198,11 +253,13 @@ class InstInitializer(object):
self.operands.pop()
def __str__(self):
template = ['{{"{opname}"', 'SpvOp{opname}', '{caps_mask}',
template = ['{{"{opname}"', 'SpvOp{opname}',
'{num_caps}', '{caps_mask}',
'{num_operands}', '{{{operands}}}',
'{def_result_id}', '{ref_type_id}}}']
return ', '.join(template).format(
opname=self.opname,
num_caps=self.num_caps,
caps_mask=self.caps_mask,
num_operands=len(self.operands),
operands=', '.join(self.operands),
@ -214,7 +271,7 @@ class ExtInstInitializer(object):
"""Instances holds a SPIR-V extended instruction suitable for printing as
the initializer for spv_ext_inst_desc_t."""
def __init__(self, opname, opcode, caps, operands):
def __init__(self, opname, opcode, caps, operands, version):
"""Initialization.
Arguments:
@ -222,24 +279,27 @@ class ExtInstInitializer(object):
- opcode: enumerant value for this opcode
- caps: a sequence of capability names required by this opcode
- operands: a sequence of (operand-kind, operand-quantifier) tuples
- version: version of the extended instruction set
"""
self.opname = opname
self.opcode = opcode
self.caps_mask = compose_capability_list(caps)
self.num_caps = len(caps)
self.caps_mask = get_capability_array_name(caps, version)
self.operands = [convert_operand_kind(o) for o in operands]
self.operands.append('SPV_OPERAND_TYPE_NONE')
def __str__(self):
template = ['{{"{opname}"', '{opcode}', '{caps_mask}',
template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}',
'{{{operands}}}}}']
return ', '.join(template).format(
opname=self.opname,
opcode=self.opcode,
num_caps=self.num_caps,
caps_mask=self.caps_mask,
operands=', '.join(self.operands))
def generate_instruction(inst, is_ext_inst):
def generate_instruction(inst, version, is_ext_inst):
"""Returns the C initializer for the given SPIR-V instruction.
Arguments:
@ -260,27 +320,49 @@ def generate_instruction(inst, is_ext_inst):
assert opname is not None
if is_ext_inst:
return str(ExtInstInitializer(opname, opcode, caps, operands))
return str(ExtInstInitializer(opname, opcode, caps, operands, version))
else:
return str(InstInitializer(opname, caps, operands))
return str(InstInitializer(opname, caps, operands, version))
def generate_instruction_table(inst_table, is_ext_inst):
"""Returns the info table containing all SPIR-V instructions.
def generate_instruction_table(inst_table, version):
"""Returns the info table containing all SPIR-V instructions,
prefixed by capability arrays.
Arguments:
- inst_table: a dict containing all SPIR-V instructions.
- is_ext_inst: a bool indicating whether |inst_table| is for
an extended instruction set.
- vesion: SPIR-V version.
"""
return ',\n'.join([generate_instruction(inst, is_ext_inst)
for inst in inst_table])
caps_arrays = generate_capability_arrays(
[inst.get('capabilities', []) for inst in inst_table], version)
insts = [generate_instruction(inst, version, False) for inst in inst_table]
insts = ['static const spv_opcode_desc_t kOpcodeTableEntries_{}[] = {{\n'
' {}\n}};'.format(version, ',\n '.join(insts))]
return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts))
def generate_extended_instruction_table(inst_table, set_name, version):
"""Returns the info table containing all SPIR-V extended instructions,
prefixed by capability arrays.
Arguments:
- inst_table: a dict containing all SPIR-V instructions.
- set_name: the name of the extended instruction set.
"""
caps = [inst.get('capabilities', []) for inst in inst_table]
caps_arrays = generate_capability_arrays(caps, version)
insts = [generate_instruction(inst, version, True) for inst in inst_table]
insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n'
' {}\n}};'.format(set_name, ',\n '.join(insts))]
return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts))
class EnumerantInitializer(object):
"""Prints an enumerant as the initializer for spv_operand_desc_t."""
def __init__(self, enumerant, value, caps, exts, parameters):
def __init__(self, enumerant, value, caps, exts, parameters, version):
"""Initialization.
Arguments:
@ -292,22 +374,26 @@ class EnumerantInitializer(object):
"""
self.enumerant = enumerant
self.value = value
self.caps = compose_capability_list(caps)
self.exts = compose_extension_list(exts)
self.num_caps = len(caps)
self.caps = get_capability_array_name(caps, version)
self.num_exts = len(exts)
self.exts = get_extension_array_name(exts, version)
self.parameters = [convert_operand_kind(p) for p in parameters]
def __str__(self):
template = ['{{"{enumerant}"', '{value}',
'{caps}', '{exts}', '{{{parameters}}}}}']
template = ['{{"{enumerant}"', '{value}', '{num_caps}',
'{caps}', '{num_exts}', '{exts}', '{{{parameters}}}}}']
return ', '.join(template).format(
enumerant=self.enumerant,
value=self.value,
num_caps=self.num_caps,
caps=self.caps,
num_exts=self.num_exts,
exts=self.exts,
parameters=', '.join(self.parameters))
def generate_enum_operand_kind_entry(entry):
def generate_enum_operand_kind_entry(entry, version):
"""Returns the C initializer for the given operand enum entry.
Arguments:
@ -327,7 +413,8 @@ def generate_enum_operand_kind_entry(entry):
assert enumerant is not None
assert value is not None
return str(EnumerantInitializer(enumerant, value, caps, exts, params))
return str(EnumerantInitializer(
enumerant, value, caps, exts, params, version))
def generate_enum_operand_kind(enum, version):
@ -336,7 +423,7 @@ def generate_enum_operand_kind(enum, version):
assert kind is not None
name = '{}_{}Entries_{}'.format(PYGEN_VARIABLE_PREFIX, kind, version)
entries = [' {}'.format(generate_enum_operand_kind_entry(e))
entries = [' {}'.format(generate_enum_operand_kind_entry(e, version))
for e in enum.get('enumerants', [])]
template = ['static const spv_operand_desc_t {name}[] = {{',
@ -351,9 +438,19 @@ def generate_enum_operand_kind(enum, version):
def generate_operand_kind_table(enums, version):
"""Returns the info table containing all SPIR-V operand kinds."""
# We only need to output info tables for those operand kinds that are enums.
enums = [generate_enum_operand_kind(e, version)
for e in enums
if e.get('category') in ['ValueEnum', 'BitEnum']]
enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']]
caps = [entry.get('capabilities', [])
for enum in enums
for entry in enum.get('enumerants', [])]
caps_arrays = generate_capability_arrays(caps, version)
exts = [entry.get('extensions', [])
for enum in enums
for entry in enum.get('enumerants', [])]
exts_arrays = generate_extension_arrays(exts, version)
enums = [generate_enum_operand_kind(e, version) for e in enums]
# We have three operand kinds that requires their optional counterpart to
# exist in the operand info table.
three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess']
@ -377,7 +474,7 @@ def generate_operand_kind_table(enums, version):
table = '\n'.join(template).format(
p=PYGEN_VARIABLE_PREFIX, v=version, enums=',\n'.join(table_entries))
return '\n\n'.join(enum_entries + (table,))
return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,))
def get_extension_list(operands):
@ -580,11 +677,11 @@ def main():
if args.core_insts_output is not None:
make_path_to_file(args.core_insts_output)
make_path_to_file(args.operand_kinds_output)
print(generate_instruction_table(
grammar['instructions'], False),
file=open(args.core_insts_output, 'w'))
version = '{}_{}'.format(grammar['major_version'],
grammar['minor_version'])
print(generate_instruction_table(
grammar['instructions'], version),
file=open(args.core_insts_output, 'w'))
print(generate_operand_kind_table(
grammar['operand_kinds'], version),
file=open(args.operand_kinds_output, 'w'))
@ -602,21 +699,27 @@ def main():
with open(args.extinst_glsl_grammar) as json_file:
grammar = json.loads(json_file.read())
make_path_to_file(args.glsl_insts_output)
print(generate_instruction_table(grammar['instructions'], True),
print(generate_extended_instruction_table(
grammar['instructions'], "glsl", "1_0"),
file=open(args.glsl_insts_output, 'w'))
if args.extinst_opencl_grammar is not None:
with open(args.extinst_opencl_grammar) as json_file:
grammar = json.loads(json_file.read())
make_path_to_file(args.opencl_insts_output)
print(generate_instruction_table(grammar['instructions'], True),
print(generate_extended_instruction_table(
grammar['instructions'], "opencl", "1_0"),
file=open(args.opencl_insts_output, 'w'))
if args.extinst_vendor_grammar is not None:
with open(args.extinst_vendor_grammar) as json_file:
grammar = json.loads(json_file.read())
make_path_to_file(args.vendor_insts_output)
print(generate_instruction_table(grammar['instructions'], True),
name = args.extinst_vendor_grammar
start = name.find("extinst.") + len("extinst.")
name = name[start:-len(".grammar.json")].replace("-", "_")
print(generate_extended_instruction_table(
grammar['instructions'], name, "1_0"),
file=open(args.vendor_insts_output, 'w'))