Updated spirv-tools.
This commit is contained in:
parent
f60f095346
commit
f19690fc99
@ -1 +1 @@
|
|||||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-154-g64faf6d9"
|
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-159-g5fc5303e"
|
||||||
|
22
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
22
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
@ -108,21 +108,29 @@ void BasicBlock::KillAllInsts(bool killLabel) {
|
|||||||
|
|
||||||
void BasicBlock::ForEachSuccessorLabel(
|
void BasicBlock::ForEachSuccessorLabel(
|
||||||
const std::function<void(const uint32_t)>& f) const {
|
const std::function<void(const uint32_t)>& f) const {
|
||||||
|
WhileEachSuccessorLabel([f](const uint32_t l) {
|
||||||
|
f(l);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BasicBlock::WhileEachSuccessorLabel(
|
||||||
|
const std::function<bool(const uint32_t)>& f) const {
|
||||||
const auto br = &insts_.back();
|
const auto br = &insts_.back();
|
||||||
switch (br->opcode()) {
|
switch (br->opcode()) {
|
||||||
case SpvOpBranch: {
|
case SpvOpBranch:
|
||||||
f(br->GetOperand(0).words[0]);
|
return f(br->GetOperand(0).words[0]);
|
||||||
} break;
|
|
||||||
case SpvOpBranchConditional:
|
case SpvOpBranchConditional:
|
||||||
case SpvOpSwitch: {
|
case SpvOpSwitch: {
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
br->ForEachInId([&is_first, &f](const uint32_t* idp) {
|
return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
|
||||||
if (!is_first) f(*idp);
|
if (!is_first) return f(*idp);
|
||||||
is_first = false;
|
is_first = false;
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
} break;
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,11 @@ class BasicBlock {
|
|||||||
void ForEachSuccessorLabel(
|
void ForEachSuccessorLabel(
|
||||||
const std::function<void(const uint32_t)>& f) const;
|
const std::function<void(const uint32_t)>& f) const;
|
||||||
|
|
||||||
|
// Runs the given function |f| on each label id of each successor block. If
|
||||||
|
// |f| returns false, iteration is terminated and this function returns false.
|
||||||
|
bool WhileEachSuccessorLabel(
|
||||||
|
const std::function<bool(const uint32_t)>& f) const;
|
||||||
|
|
||||||
// Runs the given function |f| on each label id of each successor block.
|
// Runs the given function |f| on each label id of each successor block.
|
||||||
// Modifying the pointed value will change the branch taken by the basic
|
// Modifying the pointed value will change the branch taken by the basic
|
||||||
// block. It is the caller responsibility to update or invalidate the CFG.
|
// block. It is the caller responsibility to update or invalidate the CFG.
|
||||||
|
28
3rdparty/spirv-tools/source/opt/cfg.cpp
vendored
28
3rdparty/spirv-tools/source/opt/cfg.cpp
vendored
@ -150,15 +150,25 @@ void CFG::ComputeStructuredSuccessors(Function* func) {
|
|||||||
void CFG::ComputePostOrderTraversal(BasicBlock* bb,
|
void CFG::ComputePostOrderTraversal(BasicBlock* bb,
|
||||||
std::vector<BasicBlock*>* order,
|
std::vector<BasicBlock*>* order,
|
||||||
std::unordered_set<BasicBlock*>* seen) {
|
std::unordered_set<BasicBlock*>* seen) {
|
||||||
seen->insert(bb);
|
std::vector<BasicBlock*> stack;
|
||||||
static_cast<const BasicBlock*>(bb)->ForEachSuccessorLabel(
|
stack.push_back(bb);
|
||||||
[&order, &seen, this](const uint32_t sbid) {
|
while (!stack.empty()) {
|
||||||
BasicBlock* succ_bb = id2block_[sbid];
|
bb = stack.back();
|
||||||
if (!seen->count(succ_bb)) {
|
seen->insert(bb);
|
||||||
ComputePostOrderTraversal(succ_bb, order, seen);
|
static_cast<const BasicBlock*>(bb)->WhileEachSuccessorLabel(
|
||||||
}
|
[&seen, &stack, this](const uint32_t sbid) {
|
||||||
});
|
BasicBlock* succ_bb = id2block_[sbid];
|
||||||
order->push_back(bb);
|
if (!seen->count(succ_bb)) {
|
||||||
|
stack.push_back(succ_bb);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (stack.back() == bb) {
|
||||||
|
order->push_back(bb);
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
|
BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
|
||||||
|
@ -563,11 +563,6 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
|
|||||||
}
|
}
|
||||||
void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
|
void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
|
||||||
Instruction* new_ptr_inst) {
|
Instruction* new_ptr_inst) {
|
||||||
// TODO (s-perron): Keep the def-use manager up to date. Not done now because
|
|
||||||
// it can cause problems for the |ForEachUse| traversals. Can be use by
|
|
||||||
// keeping a list of instructions that need updating, and then updating them
|
|
||||||
// in |PropagateObject|.
|
|
||||||
|
|
||||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||||
@ -703,74 +698,6 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CopyPropagateArrays::GenerateCopy(Instruction* object_inst,
|
|
||||||
uint32_t new_type_id,
|
|
||||||
Instruction* insertion_position) {
|
|
||||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
|
||||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
|
||||||
|
|
||||||
uint32_t original_type_id = object_inst->type_id();
|
|
||||||
if (original_type_id == new_type_id) {
|
|
||||||
return object_inst->result_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionBuilder ir_builder(
|
|
||||||
context(), insertion_position,
|
|
||||||
IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
|
|
||||||
|
|
||||||
analysis::Type* original_type = type_mgr->GetType(original_type_id);
|
|
||||||
analysis::Type* new_type = type_mgr->GetType(new_type_id);
|
|
||||||
|
|
||||||
if (const analysis::Array* original_array_type = original_type->AsArray()) {
|
|
||||||
uint32_t original_element_type_id =
|
|
||||||
type_mgr->GetId(original_array_type->element_type());
|
|
||||||
|
|
||||||
analysis::Array* new_array_type = new_type->AsArray();
|
|
||||||
assert(new_array_type != nullptr && "Can't copy an array to a non-array.");
|
|
||||||
uint32_t new_element_type_id =
|
|
||||||
type_mgr->GetId(new_array_type->element_type());
|
|
||||||
|
|
||||||
std::vector<uint32_t> element_ids;
|
|
||||||
const analysis::Constant* length_const =
|
|
||||||
const_mgr->FindDeclaredConstant(original_array_type->LengthId());
|
|
||||||
assert(length_const->AsIntConstant());
|
|
||||||
uint32_t array_length = length_const->AsIntConstant()->GetU32();
|
|
||||||
for (uint32_t i = 0; i < array_length; i++) {
|
|
||||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
|
||||||
original_element_type_id, object_inst->result_id(), {i});
|
|
||||||
element_ids.push_back(
|
|
||||||
GenerateCopy(extract, new_element_type_id, insertion_position));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
|
|
||||||
->result_id();
|
|
||||||
} else if (const analysis::Struct* original_struct_type =
|
|
||||||
original_type->AsStruct()) {
|
|
||||||
analysis::Struct* new_struct_type = new_type->AsStruct();
|
|
||||||
|
|
||||||
const std::vector<const analysis::Type*>& original_types =
|
|
||||||
original_struct_type->element_types();
|
|
||||||
const std::vector<const analysis::Type*>& new_types =
|
|
||||||
new_struct_type->element_types();
|
|
||||||
std::vector<uint32_t> element_ids;
|
|
||||||
for (uint32_t i = 0; i < original_types.size(); i++) {
|
|
||||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
|
||||||
type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
|
|
||||||
element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
|
|
||||||
insertion_position));
|
|
||||||
}
|
|
||||||
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
|
|
||||||
->result_id();
|
|
||||||
} else {
|
|
||||||
// If we do not have an aggregate type, then we have a problem. Either we
|
|
||||||
// found multiple instances of the same type, or we are copying to an
|
|
||||||
// incompatible type. Either way the code is illegal.
|
|
||||||
assert(false &&
|
|
||||||
"Don't know how to copy this type. Code is likely illegal.");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t CopyPropagateArrays::GetMemberTypeId(
|
uint32_t CopyPropagateArrays::GetMemberTypeId(
|
||||||
uint32_t id, const std::vector<uint32_t>& access_chain) const {
|
uint32_t id, const std::vector<uint32_t>& access_chain) const {
|
||||||
for (uint32_t element_index : access_chain) {
|
for (uint32_t element_index : access_chain) {
|
||||||
|
@ -217,12 +217,6 @@ class CopyPropagateArrays : public MemPass {
|
|||||||
// |original_ptr_inst| to |type_id| and still have valid code.
|
// |original_ptr_inst| to |type_id| and still have valid code.
|
||||||
bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id);
|
bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id);
|
||||||
|
|
||||||
// Returns the id whose value is the same as |object_to_copy| except its type
|
|
||||||
// is |new_type_id|. Any instructions need to generate this value will be
|
|
||||||
// inserted before |insertion_position|.
|
|
||||||
uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
|
||||||
Instruction* insertion_position);
|
|
||||||
|
|
||||||
// Returns a store to |var_inst| that writes to the entire variable, and is
|
// Returns a store to |var_inst| that writes to the entire variable, and is
|
||||||
// the only store that does so. Note it does not look through OpAccessChain
|
// the only store that does so. Note it does not look through OpAccessChain
|
||||||
// instruction, so partial stores are not considered.
|
// instruction, so partial stores are not considered.
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "fix_storage_class.h"
|
#include "fix_storage_class.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "source/opt/instruction.h"
|
#include "source/opt/instruction.h"
|
||||||
#include "source/opt/ir_context.h"
|
#include "source/opt/ir_context.h"
|
||||||
|
|
||||||
@ -25,12 +27,22 @@ Pass::Status FixStorageClass::Process() {
|
|||||||
|
|
||||||
get_module()->ForEachInst([this, &modified](Instruction* inst) {
|
get_module()->ForEachInst([this, &modified](Instruction* inst) {
|
||||||
if (inst->opcode() == SpvOpVariable) {
|
if (inst->opcode() == SpvOpVariable) {
|
||||||
std::vector<Instruction*> uses;
|
std::set<uint32_t> seen;
|
||||||
get_def_use_mgr()->ForEachUser(
|
std::vector<std::pair<Instruction*, uint32_t>> uses;
|
||||||
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
get_def_use_mgr()->ForEachUse(inst,
|
||||||
for (Instruction* use : uses) {
|
[&uses](Instruction* use, uint32_t op_idx) {
|
||||||
|
uses.push_back({use, op_idx});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& use : uses) {
|
||||||
modified |= PropagateStorageClass(
|
modified |= PropagateStorageClass(
|
||||||
use, static_cast<SpvStorageClass>(inst->GetSingleWordInOperand(0)));
|
use.first,
|
||||||
|
static_cast<SpvStorageClass>(inst->GetSingleWordInOperand(0)),
|
||||||
|
&seen);
|
||||||
|
assert(seen.empty() && "Seen was not properly reset.");
|
||||||
|
modified |=
|
||||||
|
PropagateType(use.first, inst->type_id(), use.second, &seen);
|
||||||
|
assert(seen.empty() && "Seen was not properly reset.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -38,13 +50,31 @@ Pass::Status FixStorageClass::Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool FixStorageClass::PropagateStorageClass(Instruction* inst,
|
bool FixStorageClass::PropagateStorageClass(Instruction* inst,
|
||||||
SpvStorageClass storage_class) {
|
SpvStorageClass storage_class,
|
||||||
|
std::set<uint32_t>* seen) {
|
||||||
if (!IsPointerResultType(inst)) {
|
if (!IsPointerResultType(inst)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPointerToStorageClass(inst, storage_class)) {
|
if (IsPointerToStorageClass(inst, storage_class)) {
|
||||||
return false;
|
if (inst->opcode() == SpvOpPhi) {
|
||||||
|
if (!seen->insert(inst->result_id()).second) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
std::vector<Instruction*> uses;
|
||||||
|
get_def_use_mgr()->ForEachUser(
|
||||||
|
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
||||||
|
for (Instruction* use : uses) {
|
||||||
|
modified |= PropagateStorageClass(use, storage_class, seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst->opcode() == SpvOpPhi) {
|
||||||
|
seen->erase(inst->result_id());
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (inst->opcode()) {
|
switch (inst->opcode()) {
|
||||||
@ -54,7 +84,7 @@ bool FixStorageClass::PropagateStorageClass(Instruction* inst,
|
|||||||
case SpvOpCopyObject:
|
case SpvOpCopyObject:
|
||||||
case SpvOpPhi:
|
case SpvOpPhi:
|
||||||
case SpvOpSelect:
|
case SpvOpSelect:
|
||||||
FixInstruction(inst, storage_class);
|
FixInstructionStorageClass(inst, storage_class, seen);
|
||||||
return true;
|
return true;
|
||||||
case SpvOpFunctionCall:
|
case SpvOpFunctionCall:
|
||||||
// We cannot be sure of the actual connection between the storage class
|
// We cannot be sure of the actual connection between the storage class
|
||||||
@ -79,8 +109,9 @@ bool FixStorageClass::PropagateStorageClass(Instruction* inst,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FixStorageClass::FixInstruction(Instruction* inst,
|
void FixStorageClass::FixInstructionStorageClass(Instruction* inst,
|
||||||
SpvStorageClass storage_class) {
|
SpvStorageClass storage_class,
|
||||||
|
std::set<uint32_t>* seen) {
|
||||||
assert(IsPointerResultType(inst) &&
|
assert(IsPointerResultType(inst) &&
|
||||||
"The result type of the instruction must be a pointer.");
|
"The result type of the instruction must be a pointer.");
|
||||||
|
|
||||||
@ -90,7 +121,7 @@ void FixStorageClass::FixInstruction(Instruction* inst,
|
|||||||
get_def_use_mgr()->ForEachUser(
|
get_def_use_mgr()->ForEachUser(
|
||||||
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
||||||
for (Instruction* use : uses) {
|
for (Instruction* use : uses) {
|
||||||
PropagateStorageClass(use, storage_class);
|
PropagateStorageClass(use, storage_class, seen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +159,171 @@ bool FixStorageClass::IsPointerToStorageClass(Instruction* inst,
|
|||||||
return (result_type->storage_class() == storage_class);
|
return (result_type->storage_class() == storage_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FixStorageClass::ChangeResultType(Instruction* inst,
|
||||||
|
uint32_t new_type_id) {
|
||||||
|
if (inst->type_id() == new_type_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context()->ForgetUses(inst);
|
||||||
|
inst->SetResultType(new_type_id);
|
||||||
|
context()->AnalyzeUses(inst);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_id,
|
||||||
|
uint32_t op_idx, std::set<uint32_t>* seen) {
|
||||||
|
assert(type_id != 0 && "Not given a valid type in PropagateType");
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
// If the type of operand |op_idx| forces the result type of |inst| to a
|
||||||
|
// particular type, then we want find that type.
|
||||||
|
uint32_t new_type_id = 0;
|
||||||
|
switch (inst->opcode()) {
|
||||||
|
case SpvOpAccessChain:
|
||||||
|
case SpvOpPtrAccessChain:
|
||||||
|
case SpvOpInBoundsAccessChain:
|
||||||
|
case SpvOpInBoundsPtrAccessChain:
|
||||||
|
if (op_idx == 2) {
|
||||||
|
new_type_id = WalkAccessChainType(inst, type_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SpvOpCopyObject:
|
||||||
|
new_type_id = type_id;
|
||||||
|
break;
|
||||||
|
case SpvOpPhi:
|
||||||
|
if (seen->insert(inst->result_id()).second) {
|
||||||
|
new_type_id = type_id;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SpvOpSelect:
|
||||||
|
if (op_idx > 2) {
|
||||||
|
new_type_id = type_id;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SpvOpFunctionCall:
|
||||||
|
// We cannot be sure of the actual connection between the type
|
||||||
|
// of the parameter and the type of the result, so we should not
|
||||||
|
// do anything. If the result type needs to be fixed, the function call
|
||||||
|
// should be inlined.
|
||||||
|
return false;
|
||||||
|
case SpvOpLoad: {
|
||||||
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
||||||
|
new_type_id = type_inst->GetSingleWordInOperand(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SpvOpStore: {
|
||||||
|
uint32_t obj_id = inst->GetSingleWordInOperand(1);
|
||||||
|
Instruction* obj_inst = get_def_use_mgr()->GetDef(obj_id);
|
||||||
|
uint32_t obj_type_id = obj_inst->type_id();
|
||||||
|
|
||||||
|
uint32_t ptr_id = inst->GetSingleWordInOperand(0);
|
||||||
|
Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
|
||||||
|
uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
|
||||||
|
|
||||||
|
if (obj_type_id != pointee_type_id) {
|
||||||
|
uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst);
|
||||||
|
inst->SetInOperand(1, {copy_id});
|
||||||
|
context()->UpdateDefUse(inst);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SpvOpCopyMemory:
|
||||||
|
case SpvOpCopyMemorySized:
|
||||||
|
// TODO: May need to expand the copy as we do with the stores.
|
||||||
|
break;
|
||||||
|
case SpvOpCompositeConstruct:
|
||||||
|
case SpvOpCompositeExtract:
|
||||||
|
case SpvOpCompositeInsert:
|
||||||
|
// TODO: DXC does not seem to generate code that will require changes to
|
||||||
|
// these opcode. The can be implemented when they come up.
|
||||||
|
break;
|
||||||
|
case SpvOpImageTexelPointer:
|
||||||
|
case SpvOpBitcast:
|
||||||
|
// Nothing to change for these opcode. The result type is the same
|
||||||
|
// regardless of the type of the operand.
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
// I expect the remaining instructions to act on types that are guaranteed
|
||||||
|
// to be unique, so no change will be necessary.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the operand forces the result type, then make sure the result type
|
||||||
|
// matches, and update the uses of |inst|. We do not have to check the uses
|
||||||
|
// of |inst| in the result type is not forced because we are only looking for
|
||||||
|
// issue that come from mismatches between function formal and actual
|
||||||
|
// parameters after the function has been inlined. These parameters are
|
||||||
|
// pointers. Once the type no longer depends on the type of the parameter,
|
||||||
|
// then the types should have be correct.
|
||||||
|
if (new_type_id != 0) {
|
||||||
|
modified = ChangeResultType(inst, new_type_id);
|
||||||
|
|
||||||
|
std::vector<std::pair<Instruction*, uint32_t>> uses;
|
||||||
|
get_def_use_mgr()->ForEachUse(inst,
|
||||||
|
[&uses](Instruction* use, uint32_t idx) {
|
||||||
|
uses.push_back({use, idx});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& use : uses) {
|
||||||
|
PropagateType(use.first, new_type_id, use.second, seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst->opcode() == SpvOpPhi) {
|
||||||
|
seen->erase(inst->result_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FixStorageClass::WalkAccessChainType(Instruction* inst, uint32_t id) {
|
||||||
|
uint32_t start_idx = 0;
|
||||||
|
switch (inst->opcode()) {
|
||||||
|
case SpvOpAccessChain:
|
||||||
|
case SpvOpInBoundsAccessChain:
|
||||||
|
start_idx = 1;
|
||||||
|
break;
|
||||||
|
case SpvOpPtrAccessChain:
|
||||||
|
case SpvOpInBoundsPtrAccessChain:
|
||||||
|
start_idx = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction* orig_type_inst = get_def_use_mgr()->GetDef(id);
|
||||||
|
assert(orig_type_inst->opcode() == SpvOpTypePointer);
|
||||||
|
id = orig_type_inst->GetSingleWordInOperand(1);
|
||||||
|
|
||||||
|
for (uint32_t i = start_idx; i < inst->NumInOperands(); ++i) {
|
||||||
|
Instruction* type_inst = get_def_use_mgr()->GetDef(id);
|
||||||
|
switch (type_inst->opcode()) {
|
||||||
|
case SpvOpTypeArray:
|
||||||
|
case SpvOpTypeRuntimeArray:
|
||||||
|
case SpvOpTypeMatrix:
|
||||||
|
case SpvOpTypeVector:
|
||||||
|
id = type_inst->GetSingleWordInOperand(0);
|
||||||
|
break;
|
||||||
|
case SpvOpTypeStruct: {
|
||||||
|
const analysis::Constant* index_const =
|
||||||
|
context()->get_constant_mgr()->FindDeclaredConstant(
|
||||||
|
inst->GetSingleWordInOperand(i));
|
||||||
|
uint32_t index = index_const->GetU32();
|
||||||
|
id = type_inst->GetSingleWordInOperand(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(id != 0 &&
|
||||||
|
"Tried to extract from an object where it cannot be done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return context()->get_type_mgr()->FindPointerToType(
|
||||||
|
id,
|
||||||
|
static_cast<SpvStorageClass>(orig_type_inst->GetSingleWordInOperand(0)));
|
||||||
|
}
|
||||||
|
|
||||||
// namespace opt
|
// namespace opt
|
||||||
|
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
|
@ -47,13 +47,19 @@ class FixStorageClass : public Pass {
|
|||||||
// Changes the storage class of the result of |inst| to |storage_class| in
|
// Changes the storage class of the result of |inst| to |storage_class| in
|
||||||
// appropriate, and propagates the change to the users of |inst| as well.
|
// appropriate, and propagates the change to the users of |inst| as well.
|
||||||
// Returns true of any changes were made.
|
// Returns true of any changes were made.
|
||||||
bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class);
|
// |seen| is used to track OpPhi instructions that should not be processed.
|
||||||
|
bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class,
|
||||||
|
std::set<uint32_t>* seen);
|
||||||
|
|
||||||
// Changes the storage class of the result of |inst| to |storage_class|.
|
// Changes the storage class of the result of |inst| to |storage_class|.
|
||||||
// Is it assumed that the result type of |inst| is a pointer type.
|
// Is it assumed that the result type of |inst| is a pointer type.
|
||||||
// Propagates the change to the users of |inst| as well.
|
// Propagates the change to the users of |inst| as well.
|
||||||
// Returns true of any changes were made.
|
// Returns true of any changes were made.
|
||||||
void FixInstruction(Instruction* inst, SpvStorageClass storage_class);
|
// |seen| is used to track OpPhi instructions that should not be processed by
|
||||||
|
// |PropagateStorageClass|
|
||||||
|
void FixInstructionStorageClass(Instruction* inst,
|
||||||
|
SpvStorageClass storage_class,
|
||||||
|
std::set<uint32_t>* seen);
|
||||||
|
|
||||||
// Changes the storage class of the result of |inst| to |storage_class|. The
|
// Changes the storage class of the result of |inst| to |storage_class|. The
|
||||||
// result type of |inst| must be a pointer.
|
// result type of |inst| must be a pointer.
|
||||||
@ -67,6 +73,18 @@ class FixStorageClass : public Pass {
|
|||||||
// |storage_class|.
|
// |storage_class|.
|
||||||
bool IsPointerToStorageClass(Instruction* inst,
|
bool IsPointerToStorageClass(Instruction* inst,
|
||||||
SpvStorageClass storage_class);
|
SpvStorageClass storage_class);
|
||||||
|
|
||||||
|
// Change |inst| to match that operand |op_idx| now has type |type_id|, and
|
||||||
|
// adjust any uses of |inst| accordingly. Returns true if the code changed.
|
||||||
|
bool PropagateType(Instruction* inst, uint32_t type_id, uint32_t op_idx,
|
||||||
|
std::set<uint32_t>* seen);
|
||||||
|
|
||||||
|
// Changes the result type of |inst| to |new_type_id|.
|
||||||
|
bool ChangeResultType(Instruction* inst, uint32_t new_type_id);
|
||||||
|
|
||||||
|
// Returns the type id of the member of the type |id| that would be returned
|
||||||
|
// by following the indices of the access chain instruction |inst|.
|
||||||
|
uint32_t WalkAccessChainType(Instruction* inst, uint32_t id);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
|
68
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
68
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "source/opt/pass.h"
|
#include "source/opt/pass.h"
|
||||||
|
|
||||||
|
#include "source/opt/ir_builder.h"
|
||||||
#include "source/opt/iterator.h"
|
#include "source/opt/iterator.h"
|
||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
@ -52,5 +53,72 @@ uint32_t Pass::GetPointeeTypeId(const Instruction* ptrInst) const {
|
|||||||
return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Pass::GenerateCopy(Instruction* object_inst, uint32_t new_type_id,
|
||||||
|
Instruction* insertion_position) {
|
||||||
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||||
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||||
|
|
||||||
|
uint32_t original_type_id = object_inst->type_id();
|
||||||
|
if (original_type_id == new_type_id) {
|
||||||
|
return object_inst->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionBuilder ir_builder(
|
||||||
|
context(), insertion_position,
|
||||||
|
IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
|
||||||
|
|
||||||
|
analysis::Type* original_type = type_mgr->GetType(original_type_id);
|
||||||
|
analysis::Type* new_type = type_mgr->GetType(new_type_id);
|
||||||
|
|
||||||
|
if (const analysis::Array* original_array_type = original_type->AsArray()) {
|
||||||
|
uint32_t original_element_type_id =
|
||||||
|
type_mgr->GetId(original_array_type->element_type());
|
||||||
|
|
||||||
|
analysis::Array* new_array_type = new_type->AsArray();
|
||||||
|
assert(new_array_type != nullptr && "Can't copy an array to a non-array.");
|
||||||
|
uint32_t new_element_type_id =
|
||||||
|
type_mgr->GetId(new_array_type->element_type());
|
||||||
|
|
||||||
|
std::vector<uint32_t> element_ids;
|
||||||
|
const analysis::Constant* length_const =
|
||||||
|
const_mgr->FindDeclaredConstant(original_array_type->LengthId());
|
||||||
|
assert(length_const->AsIntConstant());
|
||||||
|
uint32_t array_length = length_const->AsIntConstant()->GetU32();
|
||||||
|
for (uint32_t i = 0; i < array_length; i++) {
|
||||||
|
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||||
|
original_element_type_id, object_inst->result_id(), {i});
|
||||||
|
element_ids.push_back(
|
||||||
|
GenerateCopy(extract, new_element_type_id, insertion_position));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
|
||||||
|
->result_id();
|
||||||
|
} else if (const analysis::Struct* original_struct_type =
|
||||||
|
original_type->AsStruct()) {
|
||||||
|
analysis::Struct* new_struct_type = new_type->AsStruct();
|
||||||
|
|
||||||
|
const std::vector<const analysis::Type*>& original_types =
|
||||||
|
original_struct_type->element_types();
|
||||||
|
const std::vector<const analysis::Type*>& new_types =
|
||||||
|
new_struct_type->element_types();
|
||||||
|
std::vector<uint32_t> element_ids;
|
||||||
|
for (uint32_t i = 0; i < original_types.size(); i++) {
|
||||||
|
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||||
|
type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
|
||||||
|
element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
|
||||||
|
insertion_position));
|
||||||
|
}
|
||||||
|
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
|
||||||
|
->result_id();
|
||||||
|
} else {
|
||||||
|
// If we do not have an aggregate type, then we have a problem. Either we
|
||||||
|
// found multiple instances of the same type, or we are copying to an
|
||||||
|
// incompatible type. Either way the code is illegal.
|
||||||
|
assert(false &&
|
||||||
|
"Don't know how to copy this type. Code is likely illegal.");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
6
3rdparty/spirv-tools/source/opt/pass.h
vendored
6
3rdparty/spirv-tools/source/opt/pass.h
vendored
@ -125,6 +125,12 @@ class Pass {
|
|||||||
// TODO(1841): Handle id overflow.
|
// TODO(1841): Handle id overflow.
|
||||||
uint32_t TakeNextId() { return context_->TakeNextId(); }
|
uint32_t TakeNextId() { return context_->TakeNextId(); }
|
||||||
|
|
||||||
|
// Returns the id whose value is the same as |object_to_copy| except its type
|
||||||
|
// is |new_type_id|. Any instructions needed to generate this value will be
|
||||||
|
// inserted before |insertion_position|.
|
||||||
|
uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
||||||
|
Instruction* insertion_position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageConsumer consumer_; // Message consumer.
|
MessageConsumer consumer_; // Message consumer.
|
||||||
|
|
||||||
|
@ -33,12 +33,11 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
|
|||||||
<< "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
|
<< "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
|
||||||
<< "' is not a function.";
|
<< "' is not a function.";
|
||||||
}
|
}
|
||||||
// don't check kernel function signatures
|
|
||||||
|
// Only check the shader execution models
|
||||||
const SpvExecutionModel execution_model =
|
const SpvExecutionModel execution_model =
|
||||||
inst->GetOperandAs<SpvExecutionModel>(0);
|
inst->GetOperandAs<SpvExecutionModel>(0);
|
||||||
if (execution_model != SpvExecutionModelKernel) {
|
if (execution_model != SpvExecutionModelKernel) {
|
||||||
// TODO: Check the entry point signature is void main(void), may be subject
|
|
||||||
// to change
|
|
||||||
const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
|
const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
|
||||||
const auto entry_point_type = _.FindDef(entry_point_type_id);
|
const auto entry_point_type = _.FindDef(entry_point_type_id);
|
||||||
if (!entry_point_type || 3 != entry_point_type->words().size()) {
|
if (!entry_point_type || 3 != entry_point_type->words().size()) {
|
||||||
@ -236,6 +235,36 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
|
const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
|
||||||
|
if (inst->opcode() == SpvOpExecutionModeId) {
|
||||||
|
size_t operand_count = inst->operands().size();
|
||||||
|
for (size_t i = 2; i < operand_count; ++i) {
|
||||||
|
const auto operand_id = inst->GetOperandAs<uint32_t>(2);
|
||||||
|
const auto* operand_inst = _.FindDef(operand_id);
|
||||||
|
if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
|
||||||
|
mode == SpvExecutionModeLocalSizeHintId ||
|
||||||
|
mode == SpvExecutionModeLocalSizeId) {
|
||||||
|
if (!spvOpcodeIsConstant(operand_inst->opcode())) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||||
|
<< "For OpExecutionModeId all Extra Operand ids must be "
|
||||||
|
"constant "
|
||||||
|
"instructions.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||||
|
<< "OpExecutionModeId is only valid when the Mode operand is an "
|
||||||
|
"execution mode that takes Extra Operands that are id "
|
||||||
|
"operands.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
|
||||||
|
mode == SpvExecutionModeLocalSizeHintId ||
|
||||||
|
mode == SpvExecutionModeLocalSizeId) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||||
|
<< "OpExecutionMode is only valid when the Mode operand is an "
|
||||||
|
"execution mode that takes no Extra Operands, or takes Extra "
|
||||||
|
"Operands that are not id operands.";
|
||||||
|
}
|
||||||
|
|
||||||
const auto* models = _.GetExecutionModels(entry_point_id);
|
const auto* models = _.GetExecutionModels(entry_point_id);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SpvExecutionModeInvocations:
|
case SpvExecutionModeInvocations:
|
||||||
|
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
@ -21,6 +21,7 @@ add_spvtools_unittest(TARGET opt
|
|||||||
block_merge_test.cpp
|
block_merge_test.cpp
|
||||||
ccp_test.cpp
|
ccp_test.cpp
|
||||||
cfg_cleanup_test.cpp
|
cfg_cleanup_test.cpp
|
||||||
|
cfg_test.cpp
|
||||||
code_sink_test.cpp
|
code_sink_test.cpp
|
||||||
combine_access_chains_test.cpp
|
combine_access_chains_test.cpp
|
||||||
common_uniform_elim_test.cpp
|
common_uniform_elim_test.cpp
|
||||||
|
205
3rdparty/spirv-tools/test/opt/cfg_test.cpp
vendored
Normal file
205
3rdparty/spirv-tools/test/opt/cfg_test.cpp
vendored
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// Copyright (c) 2019 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.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
#include "test/opt/pass_fixture.h"
|
||||||
|
#include "test/opt/pass_utils.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace opt {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::ContainerEq;
|
||||||
|
|
||||||
|
using CFGTest = PassTest<::testing::Test>;
|
||||||
|
|
||||||
|
TEST_F(CFGTest, ForEachBlockInPostOrderIf) {
|
||||||
|
const std::string test = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpName %main "main"
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%5 = OpConstant %uint 5
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%8 = OpLabel
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpBranchConditional %true %9 %10
|
||||||
|
%9 = OpLabel
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::unique_ptr<IRContext> context =
|
||||||
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
|
||||||
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
|
ASSERT_NE(nullptr, context);
|
||||||
|
|
||||||
|
CFG* cfg = context->cfg();
|
||||||
|
Module* module = context->module();
|
||||||
|
Function* function = &*module->begin();
|
||||||
|
std::vector<uint32_t> order;
|
||||||
|
cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) {
|
||||||
|
order.push_back(bb->id());
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<uint32_t> expected_result = {10, 9, 8};
|
||||||
|
EXPECT_THAT(order, ContainerEq(expected_result));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CFGTest, ForEachBlockInPostOrderLoop) {
|
||||||
|
const std::string test = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpName %main "main"
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%5 = OpConstant %uint 5
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%8 = OpLabel
|
||||||
|
OpBranch %9
|
||||||
|
%9 = OpLabel
|
||||||
|
OpLoopMerge %11 %10 None
|
||||||
|
OpBranchConditional %true %11 %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %9
|
||||||
|
%11 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::unique_ptr<IRContext> context =
|
||||||
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
|
||||||
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
|
ASSERT_NE(nullptr, context);
|
||||||
|
|
||||||
|
CFG* cfg = context->cfg();
|
||||||
|
Module* module = context->module();
|
||||||
|
Function* function = &*module->begin();
|
||||||
|
std::vector<uint32_t> order;
|
||||||
|
cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) {
|
||||||
|
order.push_back(bb->id());
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<uint32_t> expected_result1 = {10, 11, 9, 8};
|
||||||
|
std::vector<uint32_t> expected_result2 = {11, 10, 9, 8};
|
||||||
|
EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1),
|
||||||
|
ContainerEq(expected_result2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CFGTest, ForEachBlockInReversePostOrderIf) {
|
||||||
|
const std::string test = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpName %main "main"
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%5 = OpConstant %uint 5
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%8 = OpLabel
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpBranchConditional %true %9 %10
|
||||||
|
%9 = OpLabel
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::unique_ptr<IRContext> context =
|
||||||
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
|
||||||
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
|
ASSERT_NE(nullptr, context);
|
||||||
|
|
||||||
|
CFG* cfg = context->cfg();
|
||||||
|
Module* module = context->module();
|
||||||
|
Function* function = &*module->begin();
|
||||||
|
std::vector<uint32_t> order;
|
||||||
|
cfg->ForEachBlockInReversePostOrder(
|
||||||
|
&*function->begin(),
|
||||||
|
[&order](BasicBlock* bb) { order.push_back(bb->id()); });
|
||||||
|
|
||||||
|
std::vector<uint32_t> expected_result = {8, 9, 10};
|
||||||
|
EXPECT_THAT(order, ContainerEq(expected_result));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CFGTest, ForEachBlockInReversePostOrderLoop) {
|
||||||
|
const std::string test = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpName %main "main"
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%true = OpConstantTrue %bool
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%5 = OpConstant %uint 5
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%8 = OpLabel
|
||||||
|
OpBranch %9
|
||||||
|
%9 = OpLabel
|
||||||
|
OpLoopMerge %11 %10 None
|
||||||
|
OpBranchConditional %true %11 %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %9
|
||||||
|
%11 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::unique_ptr<IRContext> context =
|
||||||
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
|
||||||
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
|
ASSERT_NE(nullptr, context);
|
||||||
|
|
||||||
|
CFG* cfg = context->cfg();
|
||||||
|
Module* module = context->module();
|
||||||
|
Function* function = &*module->begin();
|
||||||
|
std::vector<uint32_t> order;
|
||||||
|
cfg->ForEachBlockInReversePostOrder(
|
||||||
|
&*function->begin(),
|
||||||
|
[&order](BasicBlock* bb) { order.push_back(bb->id()); });
|
||||||
|
|
||||||
|
std::vector<uint32_t> expected_result1 = {8, 9, 10, 11};
|
||||||
|
std::vector<uint32_t> expected_result2 = {8, 9, 11, 10};
|
||||||
|
EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1),
|
||||||
|
ContainerEq(expected_result2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace opt
|
||||||
|
} // namespace spvtools
|
@ -148,7 +148,7 @@ TEST_F(FixStorageClassTest, FixLinkedAccessChain) {
|
|||||||
|
|
||||||
TEST_F(FixStorageClassTest, FixCopyObject) {
|
TEST_F(FixStorageClassTest, FixCopyObject) {
|
||||||
const std::string text = R"(
|
const std::string text = R"(
|
||||||
; CHECK: OpCopyObject %_ptr_Workgroup_float
|
; CHECK: OpCopyObject %_ptr_Workgroup__struct_17
|
||||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -172,8 +172,9 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
|||||||
%_arr_float_uint_10 = OpTypeArray %float %uint_10
|
%_arr_float_uint_10 = OpTypeArray %float %uint_10
|
||||||
%ptr = OpTypePointer Function %_arr_float_uint_10
|
%ptr = OpTypePointer Function %_arr_float_uint_10
|
||||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
||||||
%_struct_5 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
%_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||||
%_ptr_Workgroup__struct_5 = OpTypePointer Workgroup %_struct_5
|
%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17
|
||||||
|
%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17
|
||||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||||
@ -183,7 +184,7 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
|||||||
%30 = OpTypeFunction %void
|
%30 = OpTypeFunction %void
|
||||||
%_ptr_Function_float = OpTypePointer Function %float
|
%_ptr_Function_float = OpTypePointer Function %float
|
||||||
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
||||||
%6 = OpVariable %_ptr_Workgroup__struct_5 Workgroup
|
%6 = OpVariable %_ptr_Workgroup__struct_17 Workgroup
|
||||||
%8 = OpVariable %_ptr_Uniform__struct_7 Uniform
|
%8 = OpVariable %_ptr_Uniform__struct_7 Uniform
|
||||||
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||||
%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||||
@ -191,7 +192,7 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
|||||||
%1 = OpFunction %void None %30
|
%1 = OpFunction %void None %30
|
||||||
%38 = OpLabel
|
%38 = OpLabel
|
||||||
%44 = OpLoad %v3uint %gl_LocalInvocationID
|
%44 = OpLoad %v3uint %gl_LocalInvocationID
|
||||||
%cp = OpCopyObject %_ptr_Function_float %6
|
%cp = OpCopyObject %_ptr_Function__struct_17 %6
|
||||||
%50 = OpAccessChain %_ptr_Function_float %cp %int_0 %int_0 %int_0
|
%50 = OpAccessChain %_ptr_Function_float %cp %int_0 %int_0 %int_0
|
||||||
%51 = OpLoad %float %50
|
%51 = OpLoad %float %50
|
||||||
%52 = OpFMul %float %float_2 %51
|
%52 = OpFMul %float %float_2 %51
|
||||||
@ -209,7 +210,7 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
|||||||
|
|
||||||
TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
||||||
const std::string text = R"(
|
const std::string text = R"(
|
||||||
; CHECK: OpPhi %_ptr_Workgroup_float
|
; CHECK: OpPhi %_ptr_Workgroup__struct_19
|
||||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -237,6 +238,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
|||||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
||||||
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||||
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
||||||
|
%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||||
@ -259,7 +261,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
|||||||
%32 = OpLabel
|
%32 = OpLabel
|
||||||
OpBranch %31
|
OpBranch %31
|
||||||
%31 = OpLabel
|
%31 = OpLabel
|
||||||
%33 = OpPhi %_ptr_Function_float %28 %30 %29 %32
|
%33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32
|
||||||
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
||||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||||
%36 = OpLoad %float %35
|
%36 = OpLoad %float %35
|
||||||
@ -278,7 +280,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
|||||||
|
|
||||||
TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
||||||
const std::string text = R"(
|
const std::string text = R"(
|
||||||
; CHECK: OpPhi %_ptr_Workgroup_float
|
; CHECK: OpPhi %_ptr_Workgroup__struct_19
|
||||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -306,6 +308,7 @@ TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
|||||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
||||||
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||||
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
||||||
|
%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||||
@ -328,7 +331,7 @@ TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
|||||||
%32 = OpLabel
|
%32 = OpLabel
|
||||||
OpBranch %31
|
OpBranch %31
|
||||||
%31 = OpLabel
|
%31 = OpLabel
|
||||||
%33 = OpPhi %_ptr_Function_float %28 %30 %29 %32
|
%33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32
|
||||||
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
||||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||||
%36 = OpLoad %float %35
|
%36 = OpLoad %float %35
|
||||||
@ -378,7 +381,7 @@ OpFunctionEnd
|
|||||||
|
|
||||||
TEST_F(FixStorageClassTest, FixSelect) {
|
TEST_F(FixStorageClassTest, FixSelect) {
|
||||||
const std::string text = R"(
|
const std::string text = R"(
|
||||||
; CHECK: OpSelect %_ptr_Workgroup_float
|
; CHECK: OpSelect %_ptr_Workgroup__struct_19
|
||||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -406,6 +409,7 @@ TEST_F(FixStorageClassTest, FixSelect) {
|
|||||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
||||||
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
%_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||||
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
|
||||||
|
%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||||
@ -423,7 +427,7 @@ TEST_F(FixStorageClassTest, FixSelect) {
|
|||||||
%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
|
%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
|
||||||
%1 = OpFunction %void None %25
|
%1 = OpFunction %void None %25
|
||||||
%30 = OpLabel
|
%30 = OpLabel
|
||||||
%33 = OpSelect %_ptr_Function_float %true %28 %29
|
%33 = OpSelect %_ptr_Function__struct_19 %true %28 %29
|
||||||
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
||||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||||
%36 = OpLoad %float %35
|
%36 = OpLoad %float %35
|
||||||
@ -459,6 +463,378 @@ OpFunctionEnd
|
|||||||
SinglePassRunAndCheck<FixStorageClass>(text, text, false);
|
SinglePassRunAndCheck<FixStorageClass>(text, text, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FixStorageClassTest, FixLinkedAccessChain2) {
|
||||||
|
// This case is similar to FixLinkedAccessChain. The difference is that the
|
||||||
|
// first OpAccessChain instruction starts as workgroup storage class. Only
|
||||||
|
// the second one needs to change.
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: OpAccessChain %_ptr_Workgroup__arr_float_uint_10
|
||||||
|
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||||
|
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID
|
||||||
|
OpExecutionMode %1 LocalSize 8 8 1
|
||||||
|
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||||
|
OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
|
||||||
|
OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId
|
||||||
|
OpDecorate %5 DescriptorSet 0
|
||||||
|
OpDecorate %5 Binding 0
|
||||||
|
OpDecorate %_runtimearr_float ArrayStride 4
|
||||||
|
OpMemberDecorate %_struct_7 0 Offset 0
|
||||||
|
OpDecorate %_struct_7 BufferBlock
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%int_0 = OpConstant %int 0
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%float_2 = OpConstant %float 2
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_10 = OpConstant %uint 10
|
||||||
|
%_arr_float_uint_10 = OpTypeArray %float %uint_10
|
||||||
|
%_ptr_Workgroup__arr_float_uint_10 = OpTypePointer Workgroup %_arr_float_uint_10
|
||||||
|
%_ptr = OpTypePointer Function %_arr_float_uint_10
|
||||||
|
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
|
||||||
|
%_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||||
|
%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17
|
||||||
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||||
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||||
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||||
|
%v3uint = OpTypeVector %uint 3
|
||||||
|
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%23 = OpTypeFunction %void
|
||||||
|
%_ptr_Function_float = OpTypePointer Function %float
|
||||||
|
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
||||||
|
%27 = OpVariable %_ptr_Workgroup__struct_17 Workgroup
|
||||||
|
%5 = OpVariable %_ptr_Uniform__struct_7 Uniform
|
||||||
|
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||||
|
%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||||
|
%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
|
||||||
|
%1 = OpFunction %void None %23
|
||||||
|
%28 = OpLabel
|
||||||
|
%29 = OpLoad %v3uint %gl_LocalInvocationID
|
||||||
|
%30 = OpAccessChain %_ptr_Workgroup__arr_float_uint_10 %27 %int_0 %int_0
|
||||||
|
%31 = OpAccessChain %_ptr_Function_float %30 %int_0
|
||||||
|
%32 = OpLoad %float %31
|
||||||
|
%33 = OpFMul %float %float_2 %32
|
||||||
|
OpStore %31 %33
|
||||||
|
%34 = OpLoad %float %31
|
||||||
|
%35 = OpCompositeExtract %uint %29 0
|
||||||
|
%36 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %35
|
||||||
|
OpStore %36 %34
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
using FixTypeTest = PassTest<::testing::Test>;
|
||||||
|
|
||||||
|
TEST_F(FixTypeTest, FixAccessChain) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
|
||||||
|
; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %main "main"
|
||||||
|
OpExecutionMode %main LocalSize 1 1 1
|
||||||
|
OpSource HLSL 600
|
||||||
|
OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
|
||||||
|
OpName %S "S"
|
||||||
|
OpMemberName %S 0 "t"
|
||||||
|
OpName %T "T"
|
||||||
|
OpMemberName %T 0 "a"
|
||||||
|
OpName %A "A"
|
||||||
|
OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
|
||||||
|
OpMemberName %type_ACSBuffer_counter 0 "counter"
|
||||||
|
OpName %counter_var_A "counter.var.A"
|
||||||
|
OpName %main "main"
|
||||||
|
OpName %S_0 "S"
|
||||||
|
OpMemberName %S_0 0 "t"
|
||||||
|
OpName %T_0 "T"
|
||||||
|
OpMemberName %T_0 0 "a"
|
||||||
|
OpDecorate %A DescriptorSet 0
|
||||||
|
OpDecorate %A Binding 0
|
||||||
|
OpDecorate %counter_var_A DescriptorSet 0
|
||||||
|
OpDecorate %counter_var_A Binding 1
|
||||||
|
OpMemberDecorate %T 0 Offset 0
|
||||||
|
OpMemberDecorate %S 0 Offset 0
|
||||||
|
OpDecorate %_runtimearr_S ArrayStride 4
|
||||||
|
OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
|
||||||
|
OpDecorate %type_RWStructuredBuffer_S BufferBlock
|
||||||
|
OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
|
||||||
|
OpDecorate %type_ACSBuffer_counter BufferBlock
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%int_0 = OpConstant %int 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%T = OpTypeStruct %int
|
||||||
|
%S = OpTypeStruct %T
|
||||||
|
%_runtimearr_S = OpTypeRuntimeArray %S
|
||||||
|
%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
|
||||||
|
%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
|
||||||
|
%type_ACSBuffer_counter = OpTypeStruct %int
|
||||||
|
%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%18 = OpTypeFunction %void
|
||||||
|
%T_0 = OpTypeStruct %int
|
||||||
|
%S_0 = OpTypeStruct %T_0
|
||||||
|
%_ptr_Function_S_0 = OpTypePointer Function %S_0
|
||||||
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||||
|
%_ptr_Uniform_T = OpTypePointer Uniform %T
|
||||||
|
%22 = OpTypeFunction %T_0 %_ptr_Function_S_0
|
||||||
|
%_ptr_Function_T_0 = OpTypePointer Function %T_0
|
||||||
|
%A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
|
||||||
|
%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
|
||||||
|
%main = OpFunction %void None %18
|
||||||
|
%24 = OpLabel
|
||||||
|
%25 = OpVariable %_ptr_Function_T_0 Function
|
||||||
|
%26 = OpVariable %_ptr_Function_S_0 Function
|
||||||
|
%27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
|
||||||
|
%28 = OpAccessChain %_ptr_Function_T_0 %27 %int_0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FixTypeTest, FixLoad) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
|
||||||
|
; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0
|
||||||
|
; CHECK: [[ld:%\w+]] = OpLoad %T [[ac2]]
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %main "main"
|
||||||
|
OpExecutionMode %main LocalSize 1 1 1
|
||||||
|
OpSource HLSL 600
|
||||||
|
OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
|
||||||
|
OpName %S "S"
|
||||||
|
OpMemberName %S 0 "t"
|
||||||
|
OpName %T "T"
|
||||||
|
OpMemberName %T 0 "a"
|
||||||
|
OpName %A "A"
|
||||||
|
OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
|
||||||
|
OpMemberName %type_ACSBuffer_counter 0 "counter"
|
||||||
|
OpName %counter_var_A "counter.var.A"
|
||||||
|
OpName %main "main"
|
||||||
|
OpName %S_0 "S"
|
||||||
|
OpMemberName %S_0 0 "t"
|
||||||
|
OpName %T_0 "T"
|
||||||
|
OpMemberName %T_0 0 "a"
|
||||||
|
OpDecorate %A DescriptorSet 0
|
||||||
|
OpDecorate %A Binding 0
|
||||||
|
OpDecorate %counter_var_A DescriptorSet 0
|
||||||
|
OpDecorate %counter_var_A Binding 1
|
||||||
|
OpMemberDecorate %T 0 Offset 0
|
||||||
|
OpMemberDecorate %S 0 Offset 0
|
||||||
|
OpDecorate %_runtimearr_S ArrayStride 4
|
||||||
|
OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
|
||||||
|
OpDecorate %type_RWStructuredBuffer_S BufferBlock
|
||||||
|
OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
|
||||||
|
OpDecorate %type_ACSBuffer_counter BufferBlock
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%int_0 = OpConstant %int 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%T = OpTypeStruct %int
|
||||||
|
%S = OpTypeStruct %T
|
||||||
|
%_runtimearr_S = OpTypeRuntimeArray %S
|
||||||
|
%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
|
||||||
|
%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
|
||||||
|
%type_ACSBuffer_counter = OpTypeStruct %int
|
||||||
|
%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%18 = OpTypeFunction %void
|
||||||
|
%T_0 = OpTypeStruct %int
|
||||||
|
%S_0 = OpTypeStruct %T_0
|
||||||
|
%_ptr_Function_S_0 = OpTypePointer Function %S_0
|
||||||
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||||
|
%_ptr_Uniform_T = OpTypePointer Uniform %T
|
||||||
|
%22 = OpTypeFunction %T_0 %_ptr_Function_S_0
|
||||||
|
%_ptr_Function_T_0 = OpTypePointer Function %T_0
|
||||||
|
%A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
|
||||||
|
%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
|
||||||
|
%main = OpFunction %void None %18
|
||||||
|
%24 = OpLabel
|
||||||
|
%25 = OpVariable %_ptr_Function_T_0 Function
|
||||||
|
%26 = OpVariable %_ptr_Function_S_0 Function
|
||||||
|
%27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
|
||||||
|
%28 = OpAccessChain %_ptr_Uniform_T %27 %int_0
|
||||||
|
%29 = OpLoad %T_0 %28
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FixTypeTest, FixStore) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: [[ld:%\w+]] = OpLoad %T
|
||||||
|
; CHECK: OpStore
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %main "main"
|
||||||
|
OpExecutionMode %main LocalSize 1 1 1
|
||||||
|
OpSource HLSL 600
|
||||||
|
OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
|
||||||
|
OpName %S "S"
|
||||||
|
OpMemberName %S 0 "t"
|
||||||
|
OpName %T "T"
|
||||||
|
OpMemberName %T 0 "a"
|
||||||
|
OpName %A "A"
|
||||||
|
OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
|
||||||
|
OpMemberName %type_ACSBuffer_counter 0 "counter"
|
||||||
|
OpName %counter_var_A "counter.var.A"
|
||||||
|
OpName %main "main"
|
||||||
|
OpName %S_0 "S"
|
||||||
|
OpMemberName %S_0 0 "t"
|
||||||
|
OpName %T_0 "T"
|
||||||
|
OpMemberName %T_0 0 "a"
|
||||||
|
OpDecorate %A DescriptorSet 0
|
||||||
|
OpDecorate %A Binding 0
|
||||||
|
OpDecorate %counter_var_A DescriptorSet 0
|
||||||
|
OpDecorate %counter_var_A Binding 1
|
||||||
|
OpMemberDecorate %T 0 Offset 0
|
||||||
|
OpMemberDecorate %S 0 Offset 0
|
||||||
|
OpDecorate %_runtimearr_S ArrayStride 4
|
||||||
|
OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
|
||||||
|
OpDecorate %type_RWStructuredBuffer_S BufferBlock
|
||||||
|
OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
|
||||||
|
OpDecorate %type_ACSBuffer_counter BufferBlock
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%int_0 = OpConstant %int 0
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%T = OpTypeStruct %int
|
||||||
|
%S = OpTypeStruct %T
|
||||||
|
%_runtimearr_S = OpTypeRuntimeArray %S
|
||||||
|
%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
|
||||||
|
%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
|
||||||
|
%type_ACSBuffer_counter = OpTypeStruct %int
|
||||||
|
%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%18 = OpTypeFunction %void
|
||||||
|
%T_0 = OpTypeStruct %int
|
||||||
|
%S_0 = OpTypeStruct %T_0
|
||||||
|
%_ptr_Function_S_0 = OpTypePointer Function %S_0
|
||||||
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||||
|
%_ptr_Uniform_T = OpTypePointer Uniform %T
|
||||||
|
%22 = OpTypeFunction %T_0 %_ptr_Function_S_0
|
||||||
|
%_ptr_Function_T_0 = OpTypePointer Function %T_0
|
||||||
|
%A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
|
||||||
|
%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
|
||||||
|
%main = OpFunction %void None %18
|
||||||
|
%24 = OpLabel
|
||||||
|
%25 = OpVariable %_ptr_Function_T_0 Function
|
||||||
|
%26 = OpVariable %_ptr_Function_S_0 Function
|
||||||
|
%27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
|
||||||
|
%28 = OpAccessChain %_ptr_Uniform_T %27 %int_0
|
||||||
|
%29 = OpLoad %T %28
|
||||||
|
OpStore %25 %29
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FixTypeTest, FixSelect) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: OpSelect %_ptr_Uniform__struct_3
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %1 "main"
|
||||||
|
OpExecutionMode %1 LocalSize 1 1 1
|
||||||
|
OpSource HLSL 600
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpMemberDecorate %_struct_3 0 Offset 0
|
||||||
|
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||||
|
OpMemberDecorate %_struct_5 0 Offset 0
|
||||||
|
OpDecorate %_struct_5 BufferBlock
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_1 = OpConstant %uint 1
|
||||||
|
%_struct_3 = OpTypeStruct %uint
|
||||||
|
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||||
|
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||||
|
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%11 = OpTypeFunction %void
|
||||||
|
%_struct_12 = OpTypeStruct %uint
|
||||||
|
%_ptr_Function__struct_12 = OpTypePointer Function %_struct_12
|
||||||
|
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||||
|
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||||
|
%1 = OpFunction %void None %11
|
||||||
|
%17 = OpLabel
|
||||||
|
%18 = OpAccessChain %_ptr_Uniform_uint %2 %uint_0 %uint_0 %uint_0
|
||||||
|
%19 = OpLoad %uint %18
|
||||||
|
%20 = OpSGreaterThan %bool %19 %uint_0
|
||||||
|
%21 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_0
|
||||||
|
%22 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_1
|
||||||
|
%23 = OpSelect %_ptr_Function__struct_12 %20 %21 %22
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FixTypeTest, FixPhiInLoop) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: [[ac_init:%\w+]] = OpAccessChain %_ptr_Uniform__struct_3
|
||||||
|
; CHECK: [[ac_phi:%\w+]] = OpPhi %_ptr_Uniform__struct_3 [[ac_init]] {{%\w+}} [[ac_update:%\w+]] {{%\w+}}
|
||||||
|
; CHECK: [[ac_update]] = OpPtrAccessChain %_ptr_Uniform__struct_3 [[ac_phi]] %int_1
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %1 "main"
|
||||||
|
OpExecutionMode %1 LocalSize 1 1 1
|
||||||
|
OpSource HLSL 600
|
||||||
|
OpDecorate %2 DescriptorSet 0
|
||||||
|
OpDecorate %2 Binding 0
|
||||||
|
OpMemberDecorate %_struct_3 0 Offset 0
|
||||||
|
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||||
|
OpMemberDecorate %_struct_5 0 Offset 0
|
||||||
|
OpDecorate %_struct_5 BufferBlock
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%int_0 = OpConstant %int 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
%_struct_3 = OpTypeStruct %int
|
||||||
|
%_struct_9 = OpTypeStruct %int
|
||||||
|
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||||
|
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||||
|
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%12 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||||
|
%_ptr_Function__struct_9 = OpTypePointer Function %_struct_9
|
||||||
|
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||||
|
%1 = OpFunction %void None %12
|
||||||
|
%16 = OpLabel
|
||||||
|
%17 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %int_0
|
||||||
|
OpBranch %18
|
||||||
|
%18 = OpLabel
|
||||||
|
%20 = OpPhi %_ptr_Function__struct_9 %17 %16 %21 %22
|
||||||
|
%23 = OpUndef %bool
|
||||||
|
OpLoopMerge %24 %22 None
|
||||||
|
OpBranchConditional %23 %22 %24
|
||||||
|
%22 = OpLabel
|
||||||
|
%21 = OpPtrAccessChain %_ptr_Function__struct_9 %20 %int_1
|
||||||
|
OpBranch %18
|
||||||
|
%24 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<FixStorageClass>(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
165
3rdparty/spirv-tools/test/val/val_modes_test.cpp
vendored
165
3rdparty/spirv-tools/test/val/val_modes_test.cpp
vendored
@ -837,6 +837,171 @@ OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
|
|||||||
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
|
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeSubgroupsPerWorkgroupIdBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability SubgroupDispatch
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpExecutionMode %main SubgroupsPerWorkgroupId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("OpExecutionMode is only valid when the Mode operand "
|
||||||
|
"is an execution mode that takes no Extra Operands"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdGood) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability SubgroupDispatch
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdNonConstantBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpCapability SubgroupDispatch
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_ptr = OpTypePointer Private %int
|
||||||
|
%int_1 = OpVariable %int_ptr Private
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("For OpExecutionModeId all Extra Operand ids must be "
|
||||||
|
"constant instructions."));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeLocalSizeHintIdBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Kernel %main "main"
|
||||||
|
OpExecutionMode %main LocalSizeHintId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("OpExecutionMode is only valid when the Mode operand "
|
||||||
|
"is an execution mode that takes no Extra Operands"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdGood) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Kernel %main "main"
|
||||||
|
OpExecutionModeId %main LocalSizeHintId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdNonConstantBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpExecutionModeId %main LocalSizeHintId %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_ptr = OpTypePointer Private %int
|
||||||
|
%int_1 = OpVariable %int_ptr Private
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("For OpExecutionModeId all Extra Operand ids must be "
|
||||||
|
"constant instructions."));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeLocalSizeIdBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Kernel %main "main"
|
||||||
|
OpExecutionMode %main LocalSizeId %int_1 %int_1 %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("OpExecutionMode is only valid when the Mode operand "
|
||||||
|
"is an execution mode that takes no Extra Operands"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdGood) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Kernel %main "main"
|
||||||
|
OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_1 = OpConstant %int 1
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdNonConstantBad) {
|
||||||
|
const std::string spirv = R"(
|
||||||
|
OpCapability Kernel
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
|
||||||
|
%int = OpTypeInt 32 0
|
||||||
|
%int_ptr = OpTypePointer Private %int
|
||||||
|
%int_1 = OpVariable %int_ptr Private
|
||||||
|
)" + kVoidFunction;
|
||||||
|
|
||||||
|
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
|
CompileSuccessfully(spirv, env);
|
||||||
|
EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
|
||||||
|
EXPECT_THAT(getDiagnosticString(),
|
||||||
|
HasSubstr("For OpExecutionModeId all Extra Operand ids must be "
|
||||||
|
"constant instructions."));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace val
|
} // namespace val
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
Loading…
Reference in New Issue
Block a user