mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
2
3rdparty/spirv-tools/README.md
vendored
2
3rdparty/spirv-tools/README.md
vendored
@@ -47,7 +47,7 @@ version. An API call reports the software version as a C-style string.
|
||||
|
||||
* Support for SPIR-V 1.0, 1.1, 1.2, and 1.3
|
||||
* Based on SPIR-V syntax described by JSON grammar files in the
|
||||
[SPIRV-Headers](spirv-headers) repository.
|
||||
[SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository.
|
||||
* Support for extended instruction sets:
|
||||
* GLSL std450 version 1.0 Rev 3
|
||||
* OpenCL version 1.0 Rev 2
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
int main() {
|
||||
const std::string source =
|
||||
" OpCapability Linkage "
|
||||
" OpCapability Shader "
|
||||
" OpMemoryModel Logical GLSL450 "
|
||||
" OpSource GLSL 450 "
|
||||
@@ -36,8 +37,8 @@ int main() {
|
||||
" %spec = OpSpecConstant %int 0 "
|
||||
"%const = OpConstant %int 42";
|
||||
|
||||
spvtools::SpirvTools core(SPV_ENV_VULKAN_1_0);
|
||||
spvtools::Optimizer opt(SPV_ENV_VULKAN_1_0);
|
||||
spvtools::SpirvTools core(SPV_ENV_UNIVERSAL_1_3);
|
||||
spvtools::Optimizer opt(SPV_ENV_UNIVERSAL_1_3);
|
||||
|
||||
auto print_msg_to_stderr = [](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char* m) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-147-g7aad9653"
|
||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-154-g64faf6d9"
|
||||
|
||||
@@ -34,43 +34,78 @@ bool HasInitializer(Instruction* inst) {
|
||||
|
||||
Pass::Status DecomposeInitializedVariablesPass::Process() {
|
||||
auto* module = context()->module();
|
||||
bool changed = false;
|
||||
std::unordered_set<Instruction*> changed;
|
||||
|
||||
// TODO(zoddicus): Handle 'Output' variables
|
||||
// TODO(zoddicus): Handle 'Private' variables
|
||||
std::vector<std::tuple<uint32_t, uint32_t>> global_stores;
|
||||
for (auto iter = module->types_values_begin();
|
||||
iter != module->types_values_end(); ++iter) {
|
||||
Instruction* inst = &(*iter);
|
||||
if (!HasInitializer(inst)) continue;
|
||||
|
||||
auto var_id = inst->result_id();
|
||||
auto val_id = inst->GetOperand(3).words[0];
|
||||
global_stores.push_back(std::make_tuple(var_id, val_id));
|
||||
iter->RemoveOperand(3);
|
||||
changed.insert(&*iter);
|
||||
}
|
||||
|
||||
std::unordered_set<uint32_t> entry_ids;
|
||||
for (auto entry = module->entry_points().begin();
|
||||
entry != module->entry_points().end(); ++entry) {
|
||||
entry_ids.insert(entry->GetSingleWordInOperand(1));
|
||||
}
|
||||
|
||||
// Handle 'Function' variables
|
||||
for (auto func = module->begin(); func != module->end(); ++func) {
|
||||
auto block = func->entry().get();
|
||||
std::vector<Instruction*> new_stores;
|
||||
|
||||
auto last_var = block->begin();
|
||||
for (auto iter = block->begin();
|
||||
iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) {
|
||||
last_var = iter;
|
||||
std::vector<Instruction*> function_stores;
|
||||
auto first_block = func->entry().get();
|
||||
inst_iterator insert_point = first_block->begin();
|
||||
for (auto iter = first_block->begin();
|
||||
iter != first_block->end() && iter->opcode() == SpvOpVariable;
|
||||
++iter) {
|
||||
// For valid SPIRV-V, there is guaranteed to be at least one instruction
|
||||
// after the OpVariable instructions.
|
||||
insert_point = (*iter).NextNode();
|
||||
Instruction* inst = &(*iter);
|
||||
if (!HasInitializer(inst)) continue;
|
||||
|
||||
changed = true;
|
||||
auto var_id = inst->result_id();
|
||||
auto val_id = inst->GetOperand(3).words[0];
|
||||
Instruction* store_inst = new Instruction(
|
||||
context(), SpvOpStore, 0, 0,
|
||||
{{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}});
|
||||
new_stores.push_back(store_inst);
|
||||
function_stores.push_back(store_inst);
|
||||
iter->RemoveOperand(3);
|
||||
get_def_use_mgr()->UpdateDefUse(&*iter);
|
||||
changed.insert(&*iter);
|
||||
}
|
||||
|
||||
for (auto store = new_stores.begin(); store != new_stores.end(); ++store) {
|
||||
context()->AnalyzeDefUse(*store);
|
||||
context()->set_instr_block(*store, block);
|
||||
(*store)->InsertAfter(&*last_var);
|
||||
last_var = *store;
|
||||
if (entry_ids.find(func->result_id()) != entry_ids.end()) {
|
||||
for (auto store_ids : global_stores) {
|
||||
uint32_t var_id;
|
||||
uint32_t val_id;
|
||||
std::tie(var_id, val_id) = store_ids;
|
||||
auto* store_inst = new Instruction(
|
||||
context(), SpvOpStore, 0, 0,
|
||||
{{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}});
|
||||
context()->set_instr_block(store_inst, &*first_block);
|
||||
first_block->AddInstruction(std::unique_ptr<Instruction>(store_inst));
|
||||
store_inst->InsertBefore(&*insert_point);
|
||||
changed.insert(store_inst);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto store = function_stores.begin(); store != function_stores.end();
|
||||
++store) {
|
||||
context()->set_instr_block(*store, first_block);
|
||||
(*store)->InsertBefore(&*insert_point);
|
||||
changed.insert(*store);
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
auto* def_use_mgr = get_def_use_mgr();
|
||||
for (auto* inst : changed) def_use_mgr->UpdateDefUse(inst);
|
||||
|
||||
return !changed.empty() ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
|
||||
@@ -225,12 +225,12 @@ bool ScalarReplacementPass::ReplaceAccessChain(
|
||||
// indexes) or a direct use of the replacement variable.
|
||||
uint32_t indexId = chain->GetSingleWordInOperand(1u);
|
||||
const Instruction* index = get_def_use_mgr()->GetDef(indexId);
|
||||
size_t indexValue = GetConstantInteger(index);
|
||||
uint64_t indexValue = GetConstantInteger(index);
|
||||
if (indexValue > replacements.size()) {
|
||||
// Out of bounds access, this is illegal IR.
|
||||
return false;
|
||||
} else {
|
||||
const Instruction* var = replacements[indexValue];
|
||||
const Instruction* var = replacements[static_cast<size_t>(indexValue)];
|
||||
if (chain->NumInOperands() > 2) {
|
||||
// Replace input access chain with another access chain.
|
||||
BasicBlock::iterator chainIter(chain);
|
||||
@@ -457,16 +457,16 @@ void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
|
||||
}
|
||||
}
|
||||
|
||||
size_t ScalarReplacementPass::GetIntegerLiteral(const Operand& op) const {
|
||||
uint64_t ScalarReplacementPass::GetIntegerLiteral(const Operand& op) const {
|
||||
assert(op.words.size() <= 2);
|
||||
size_t len = 0;
|
||||
uint64_t len = 0;
|
||||
for (uint32_t i = 0; i != op.words.size(); ++i) {
|
||||
len |= (op.words[i] << (32 * i));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t ScalarReplacementPass::GetConstantInteger(
|
||||
uint64_t ScalarReplacementPass::GetConstantInteger(
|
||||
const Instruction* constant) const {
|
||||
assert(get_def_use_mgr()->GetDef(constant->type_id())->opcode() ==
|
||||
SpvOpTypeInt);
|
||||
@@ -480,7 +480,7 @@ size_t ScalarReplacementPass::GetConstantInteger(
|
||||
return GetIntegerLiteral(op);
|
||||
}
|
||||
|
||||
size_t ScalarReplacementPass::GetArrayLength(
|
||||
uint64_t ScalarReplacementPass::GetArrayLength(
|
||||
const Instruction* arrayType) const {
|
||||
assert(arrayType->opcode() == SpvOpTypeArray);
|
||||
const Instruction* length =
|
||||
@@ -488,14 +488,14 @@ size_t ScalarReplacementPass::GetArrayLength(
|
||||
return GetConstantInteger(length);
|
||||
}
|
||||
|
||||
size_t ScalarReplacementPass::GetNumElements(const Instruction* type) const {
|
||||
uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const {
|
||||
assert(type->opcode() == SpvOpTypeVector ||
|
||||
type->opcode() == SpvOpTypeMatrix);
|
||||
const Operand& op = type->GetInOperand(1u);
|
||||
assert(op.words.size() <= 2);
|
||||
size_t len = 0;
|
||||
for (uint32_t i = 0; i != op.words.size(); ++i) {
|
||||
len |= (op.words[i] << (32 * i));
|
||||
uint64_t len = 0;
|
||||
for (size_t i = 0; i != op.words.size(); ++i) {
|
||||
len |= (static_cast<uint64_t>(op.words[i]) << (32ull * i));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -717,7 +717,7 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst,
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool ScalarReplacementPass::IsLargerThanSizeLimit(size_t length) const {
|
||||
bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const {
|
||||
if (max_num_elements_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -157,18 +157,18 @@ class ScalarReplacementPass : public Pass {
|
||||
// Returns the value of an OpConstant of integer type.
|
||||
//
|
||||
// |constant| must use two or fewer words to generate the value.
|
||||
size_t GetConstantInteger(const Instruction* constant) const;
|
||||
uint64_t GetConstantInteger(const Instruction* constant) const;
|
||||
|
||||
// Returns the integer literal for |op|.
|
||||
size_t GetIntegerLiteral(const Operand& op) const;
|
||||
uint64_t GetIntegerLiteral(const Operand& op) const;
|
||||
|
||||
// Returns the array length for |arrayInst|.
|
||||
size_t GetArrayLength(const Instruction* arrayInst) const;
|
||||
uint64_t GetArrayLength(const Instruction* arrayInst) const;
|
||||
|
||||
// Returns the number of elements in |type|.
|
||||
//
|
||||
// |type| must be a vector or matrix type.
|
||||
size_t GetNumElements(const Instruction* type) const;
|
||||
uint64_t GetNumElements(const Instruction* type) const;
|
||||
|
||||
// Returns true if |id| is a specialization constant.
|
||||
//
|
||||
@@ -228,7 +228,7 @@ class ScalarReplacementPass : public Pass {
|
||||
// Limit on the number of members in an object that will be replaced.
|
||||
// 0 means there is no limit.
|
||||
uint32_t max_num_elements_;
|
||||
bool IsLargerThanSizeLimit(size_t length) const;
|
||||
bool IsLargerThanSizeLimit(uint64_t length) const;
|
||||
char name_[55];
|
||||
};
|
||||
|
||||
|
||||
@@ -548,6 +548,9 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
typeId, storage_class_str, decoration_str,
|
||||
blockRules, next_offset, constraints, vstate)))
|
||||
return recursive_status;
|
||||
// If offsets accumulate up to a 16-byte multiple stop checking since
|
||||
// it will just repeat.
|
||||
if (i > 0 && (next_offset % 16 == 0)) break;
|
||||
}
|
||||
|
||||
// Proceed to the element in case it is an array.
|
||||
|
||||
@@ -55,9 +55,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
||||
SpvMemorySemanticsMakeAvailableKHRMask |
|
||||
SpvMemorySemanticsMakeVisibleKHRMask;
|
||||
if (!spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
valid_bits |= SpvMemorySemanticsAcquireMask |
|
||||
SpvMemorySemanticsReleaseMask |
|
||||
SpvMemorySemanticsAcquireReleaseMask;
|
||||
valid_bits |= SpvMemorySemanticsAcquireReleaseMask;
|
||||
}
|
||||
|
||||
if (value & ~valid_bits) {
|
||||
@@ -69,11 +67,18 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "WebGPU spec disallows any bit masks in Memory Semantics "
|
||||
"that are not Acquire, Release, AcquireRelease, "
|
||||
"UniformMemory, WorkgroupMemory, ImageMemory, "
|
||||
"OutputMemoryKHR, MakeAvailableKHR, or MakeVisibleKHR";
|
||||
"that are not AcquireRelease, UniformMemory, "
|
||||
"WorkgroupMemory, ImageMemory, OutputMemoryKHR, "
|
||||
"MakeAvailableKHR, or MakeVisibleKHR";
|
||||
}
|
||||
}
|
||||
|
||||
if (!spvOpcodeIsAtomicOp(inst->opcode()) &&
|
||||
!(value & SpvMemorySemanticsAcquireReleaseMask)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "WebGPU spec requires AcquireRelease to set in Memory "
|
||||
"Semantics.";
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
|
||||
|
||||
@@ -23,63 +23,230 @@ namespace {
|
||||
|
||||
using DecomposeInitializedVariablesTest = PassTest<::testing::Test>;
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs,
|
||||
const std::vector<const char*> rhs) {
|
||||
for (auto elem : rhs) lhs.push_back(elem);
|
||||
}
|
||||
std::string single_entry_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %1 "shader"
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%4 = OpConstantNull %uint
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::vector<const char*> header = {
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModelKHR",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical VulkanKHR",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%uint_1 = OpConstant %uint 1",
|
||||
"%4 = OpConstantNull %uint",
|
||||
"%void = OpTypeVoid",
|
||||
"%6 = OpTypeFunction %void"};
|
||||
|
||||
std::string GetFunctionTest(std::vector<const char*> body) {
|
||||
auto result = header;
|
||||
result += {"%_ptr_Function_uint = OpTypePointer Function %uint",
|
||||
"%1 = OpFunction %void None %6", "%8 = OpLabel"};
|
||||
result += body;
|
||||
result += {"OpReturn", "OpFunctionEnd"};
|
||||
return JoinAllInsts(result);
|
||||
std::string GetFunctionTest(std::string body) {
|
||||
auto result = single_entry_header;
|
||||
result += "%_ptr_Function_uint = OpTypePointer Function %uint\n";
|
||||
result += "%1 = OpFunction %void None %6\n";
|
||||
result += "%8 = OpLabel\n";
|
||||
result += body + "\n";
|
||||
result += "OpReturn\n";
|
||||
result += "OpFunctionEnd\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1"});
|
||||
std::string expected = GetFunctionTest(
|
||||
{"%9 = OpVariable %_ptr_Function_uint Function", "OpStore %9 %uint_1"});
|
||||
std::string input = "%9 = OpVariable %_ptr_Function_uint Function %uint_1";
|
||||
std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %9 %uint_1)";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(expected),
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) {
|
||||
std::string input = "%9 = OpVariable %_ptr_Function_uint Function";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(input), /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) {
|
||||
std::string input = R"(%9 = OpVariable %_ptr_Function_uint Function %uint_1
|
||||
%10 = OpVariable %_ptr_Function_uint Function %4)";
|
||||
std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function
|
||||
%10 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %9 %uint_1
|
||||
OpStore %10 %4)";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(expected),
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetGlobalTest(std::string storage_class, bool initialized,
|
||||
bool decomposed) {
|
||||
auto result = single_entry_header;
|
||||
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateChanged) {
|
||||
std::string input = GetGlobalTest("Private", true, false);
|
||||
std::string expected = GetGlobalTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function"});
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateUnchanged) {
|
||||
std::string input = GetGlobalTest("Private", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1",
|
||||
"%10 = OpVariable %_ptr_Function_uint Function %4"});
|
||||
std::string expected =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function",
|
||||
"%10 = OpVariable %_ptr_Function_uint Function",
|
||||
"OpStore %9 %uint_1", "OpStore %10 %4"});
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputChanged) {
|
||||
std::string input = GetGlobalTest("Output", true, false);
|
||||
std::string expected = GetGlobalTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputUnchanged) {
|
||||
std::string input = GetGlobalTest("Output", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string multiple_entry_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %1 "vertex"
|
||||
OpEntryPoint Fragment %2 "fragment"
|
||||
%uint = OpTypeInt 32 0
|
||||
%4 = OpConstantNull %uint
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::string GetGlobalMultipleEntryTest(std::string storage_class,
|
||||
bool initialized, bool decomposed) {
|
||||
auto result = multiple_entry_header;
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
%2 = OpFunction %void None %10
|
||||
%10 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryChanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Private", true, false);
|
||||
std::string expected = GetGlobalMultipleEntryTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryUnchanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Private", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryChanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Output", true, false);
|
||||
std::string expected = GetGlobalMultipleEntryTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryUnchanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Output", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetGlobalWithNonEntryPointTest(std::string storage_class,
|
||||
bool initialized, bool decomposed) {
|
||||
auto result = single_entry_header;
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
%10 = OpFunction %void None %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointChanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Private", true, false);
|
||||
std::string expected = GetGlobalWithNonEntryPointTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointUnchanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Private", false, false);
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointChanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Output", true, false);
|
||||
std::string expected = GetGlobalWithNonEntryPointTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointUnchanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Output", false, false);
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -78,9 +78,13 @@ OpCapability Shader
|
||||
%acquire_and_release = OpConstant %u32 6
|
||||
%sequentially_consistent = OpConstant %u32 16
|
||||
%acquire_release_uniform_workgroup = OpConstant %u32 328
|
||||
%acquire_uniform_workgroup = OpConstant %u32 322
|
||||
%release_uniform_workgroup = OpConstant %u32 324
|
||||
%acquire_and_release_uniform = OpConstant %u32 70
|
||||
%acquire_release_subgroup = OpConstant %u32 136
|
||||
%uniform = OpConstant %u32 64
|
||||
%uniform_workgroup = OpConstant %u32 320
|
||||
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
%main_entry = OpLabel
|
||||
@@ -245,9 +249,8 @@ OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUSuccess) {
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %queuefamily %none
|
||||
OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
|
||||
)";
|
||||
|
||||
@@ -255,6 +258,41 @@ OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec requires AcquireRelease to set"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %acquire_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %device %device %none
|
||||
@@ -630,6 +668,50 @@ OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseSuccess) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec requires AcquireRelease to set"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %acquire_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup
|
||||
|
||||
@@ -5719,6 +5719,41 @@ OpFunctionEnd
|
||||
"improperly straddling vector at offset 28"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, LargeArray) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
OpDecorate %array ArrayStride 24
|
||||
OpMemberDecorate %inner 0 Offset 0
|
||||
OpMemberDecorate %inner 1 Offset 8
|
||||
OpMemberDecorate %inner 2 Offset 16
|
||||
OpMemberDecorate %inner 3 Offset 20
|
||||
OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_2000000 = OpConstant %int 2000000
|
||||
%int2 = OpTypeVector %int 2
|
||||
%inner = OpTypeStruct %int %int2 %int %int
|
||||
%array = OpTypeArray %inner %int_2000000
|
||||
%struct = OpTypeStruct %array
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
// NonWritable
|
||||
|
||||
// Returns a SPIR-V shader module with variables in various storage classes,
|
||||
|
||||
4
3rdparty/spirv-tools/tools/CMakeLists.txt
vendored
4
3rdparty/spirv-tools/tools/CMakeLists.txt
vendored
@@ -12,7 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
add_subdirectory(lesspipe)
|
||||
if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||
add_subdirectory(lesspipe)
|
||||
endif()
|
||||
add_subdirectory(emacs)
|
||||
|
||||
# Add a SPIR-V Tools command line tool. Signature:
|
||||
|
||||
Reference in New Issue
Block a user