Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2019-05-05 18:57:52 -07:00
parent f60f095346
commit f19690fc99
15 changed files with 1131 additions and 123 deletions

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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) {

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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

View 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

View File

@ -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

View File

@ -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