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(
|
||||
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();
|
||||
switch (br->opcode()) {
|
||||
case SpvOpBranch: {
|
||||
f(br->GetOperand(0).words[0]);
|
||||
} break;
|
||||
case SpvOpBranch:
|
||||
return f(br->GetOperand(0).words[0]);
|
||||
case SpvOpBranchConditional:
|
||||
case SpvOpSwitch: {
|
||||
bool is_first = true;
|
||||
br->ForEachInId([&is_first, &f](const uint32_t* idp) {
|
||||
if (!is_first) f(*idp);
|
||||
return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
|
||||
if (!is_first) return f(*idp);
|
||||
is_first = false;
|
||||
return true;
|
||||
});
|
||||
} break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,11 @@ class BasicBlock {
|
||||
void ForEachSuccessorLabel(
|
||||
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.
|
||||
// Modifying the pointed value will change the branch taken by the basic
|
||||
// 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,
|
||||
std::vector<BasicBlock*>* order,
|
||||
std::unordered_set<BasicBlock*>* seen) {
|
||||
seen->insert(bb);
|
||||
static_cast<const BasicBlock*>(bb)->ForEachSuccessorLabel(
|
||||
[&order, &seen, this](const uint32_t sbid) {
|
||||
BasicBlock* succ_bb = id2block_[sbid];
|
||||
if (!seen->count(succ_bb)) {
|
||||
ComputePostOrderTraversal(succ_bb, order, seen);
|
||||
}
|
||||
});
|
||||
order->push_back(bb);
|
||||
std::vector<BasicBlock*> stack;
|
||||
stack.push_back(bb);
|
||||
while (!stack.empty()) {
|
||||
bb = stack.back();
|
||||
seen->insert(bb);
|
||||
static_cast<const BasicBlock*>(bb)->WhileEachSuccessorLabel(
|
||||
[&seen, &stack, this](const uint32_t sbid) {
|
||||
BasicBlock* succ_bb = id2block_[sbid];
|
||||
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) {
|
||||
|
@ -563,11 +563,6 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
|
||||
}
|
||||
void CopyPropagateArrays::UpdateUses(Instruction* original_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::ConstantManager* const_mgr = context()->get_constant_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 id, const std::vector<uint32_t>& access_chain) const {
|
||||
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.
|
||||
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
|
||||
// the only store that does so. Note it does not look through OpAccessChain
|
||||
// instruction, so partial stores are not considered.
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "fix_storage_class.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
@ -25,12 +27,22 @@ Pass::Status FixStorageClass::Process() {
|
||||
|
||||
get_module()->ForEachInst([this, &modified](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpVariable) {
|
||||
std::vector<Instruction*> uses;
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
||||
for (Instruction* use : uses) {
|
||||
std::set<uint32_t> seen;
|
||||
std::vector<std::pair<Instruction*, uint32_t>> uses;
|
||||
get_def_use_mgr()->ForEachUse(inst,
|
||||
[&uses](Instruction* use, uint32_t op_idx) {
|
||||
uses.push_back({use, op_idx});
|
||||
});
|
||||
|
||||
for (auto& use : uses) {
|
||||
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,
|
||||
SpvStorageClass storage_class) {
|
||||
SpvStorageClass storage_class,
|
||||
std::set<uint32_t>* seen) {
|
||||
if (!IsPointerResultType(inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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()) {
|
||||
@ -54,7 +84,7 @@ bool FixStorageClass::PropagateStorageClass(Instruction* inst,
|
||||
case SpvOpCopyObject:
|
||||
case SpvOpPhi:
|
||||
case SpvOpSelect:
|
||||
FixInstruction(inst, storage_class);
|
||||
FixInstructionStorageClass(inst, storage_class, seen);
|
||||
return true;
|
||||
case SpvOpFunctionCall:
|
||||
// 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,
|
||||
SpvStorageClass storage_class) {
|
||||
void FixStorageClass::FixInstructionStorageClass(Instruction* inst,
|
||||
SpvStorageClass storage_class,
|
||||
std::set<uint32_t>* seen) {
|
||||
assert(IsPointerResultType(inst) &&
|
||||
"The result type of the instruction must be a pointer.");
|
||||
|
||||
@ -90,7 +121,7 @@ void FixStorageClass::FixInstruction(Instruction* inst,
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
inst, [&uses](Instruction* use) { uses.push_back(use); });
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -47,13 +47,19 @@ class FixStorageClass : public Pass {
|
||||
// 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.
|
||||
// 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|.
|
||||
// Is it assumed that the result type of |inst| is a pointer type.
|
||||
// Propagates the change to the users of |inst| as well.
|
||||
// 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
|
||||
// result type of |inst| must be a pointer.
|
||||
@ -67,6 +73,18 @@ class FixStorageClass : public Pass {
|
||||
// |storage_class|.
|
||||
bool IsPointerToStorageClass(Instruction* inst,
|
||||
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
|
||||
|
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/ir_builder.h"
|
||||
#include "source/opt/iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -52,5 +53,72 @@ uint32_t Pass::GetPointeeTypeId(const Instruction* ptrInst) const {
|
||||
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 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.
|
||||
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:
|
||||
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)
|
||||
<< "' is not a function.";
|
||||
}
|
||||
// don't check kernel function signatures
|
||||
|
||||
// Only check the shader execution models
|
||||
const SpvExecutionModel execution_model =
|
||||
inst->GetOperandAs<SpvExecutionModel>(0);
|
||||
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 = _.FindDef(entry_point_type_id);
|
||||
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);
|
||||
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);
|
||||
switch (mode) {
|
||||
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
|
||||
ccp_test.cpp
|
||||
cfg_cleanup_test.cpp
|
||||
cfg_test.cpp
|
||||
code_sink_test.cpp
|
||||
combine_access_chains_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) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyObject %_ptr_Workgroup_float
|
||||
; CHECK: OpCopyObject %_ptr_Workgroup__struct_17
|
||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||
OpCapability Shader
|
||||
@ -172,8 +172,9 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
||||
%_arr_float_uint_10 = OpTypeArray %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_5 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||
%_ptr_Workgroup__struct_5 = OpTypePointer Workgroup %_struct_5
|
||||
%_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
|
||||
%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17
|
||||
%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17
|
||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||
@ -183,7 +184,7 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
||||
%30 = OpTypeFunction %void
|
||||
%_ptr_Function_float = OpTypePointer Function %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
|
||||
%gl_GlobalInvocationID = 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
|
||||
%38 = OpLabel
|
||||
%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
|
||||
%51 = OpLoad %float %50
|
||||
%52 = OpFMul %float %float_2 %51
|
||||
@ -209,7 +210,7 @@ TEST_F(FixStorageClassTest, FixCopyObject) {
|
||||
|
||||
TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpPhi %_ptr_Workgroup_float
|
||||
; CHECK: OpPhi %_ptr_Workgroup__struct_19
|
||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||
OpCapability Shader
|
||||
@ -237,6 +238,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_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_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||
@ -259,7 +261,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
||||
%32 = OpLabel
|
||||
OpBranch %31
|
||||
%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
|
||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||
%36 = OpLoad %float %35
|
||||
@ -278,7 +280,7 @@ TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
|
||||
|
||||
TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpPhi %_ptr_Workgroup_float
|
||||
; CHECK: OpPhi %_ptr_Workgroup__struct_19
|
||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||
OpCapability Shader
|
||||
@ -306,6 +308,7 @@ TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_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_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||
@ -328,7 +331,7 @@ TEST_F(FixStorageClassTest, FixPhiInLoop) {
|
||||
%32 = OpLabel
|
||||
OpBranch %31
|
||||
%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
|
||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||
%36 = OpLoad %float %35
|
||||
@ -378,7 +381,7 @@ OpFunctionEnd
|
||||
|
||||
TEST_F(FixStorageClassTest, FixSelect) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpSelect %_ptr_Workgroup_float
|
||||
; CHECK: OpSelect %_ptr_Workgroup__struct_19
|
||||
; CHECK: OpAccessChain %_ptr_Workgroup_float
|
||||
; CHECK: OpAccessChain %_ptr_Uniform_float
|
||||
OpCapability Shader
|
||||
@ -406,6 +409,7 @@ TEST_F(FixStorageClassTest, FixSelect) {
|
||||
%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_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_Function__struct_19 = OpTypePointer Function %_struct_19
|
||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||
%_struct_7 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
||||
@ -423,7 +427,7 @@ TEST_F(FixStorageClassTest, FixSelect) {
|
||||
%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
|
||||
%1 = OpFunction %void None %25
|
||||
%30 = OpLabel
|
||||
%33 = OpSelect %_ptr_Function_float %true %28 %29
|
||||
%33 = OpSelect %_ptr_Function__struct_19 %true %28 %29
|
||||
%34 = OpLoad %v3uint %gl_LocalInvocationID
|
||||
%35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
|
||||
%36 = OpLoad %float %35
|
||||
@ -459,6 +463,378 @@ OpFunctionEnd
|
||||
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 opt
|
||||
} // 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));
|
||||
}
|
||||
|
||||
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 val
|
||||
} // namespace spvtools
|
||||
|
Loading…
Reference in New Issue
Block a user