Transform to combine consecutive access chains

* Combines OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain and
OpInBoundsPtrAccessChain
* New folding rule to fold add with 0 for integers
 * Converts to a bitcast if the result type does not match the operand
 type
V
This commit is contained in:
Alan Baker 2018-07-23 11:23:11 -04:00
parent 8a0ec22f13
commit 755e5c9420
12 changed files with 1299 additions and 4 deletions

View File

@ -68,6 +68,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/cfg.cpp \ source/opt/cfg.cpp \
source/opt/cfg_cleanup_pass.cpp \ source/opt/cfg_cleanup_pass.cpp \
source/opt/ccp_pass.cpp \ source/opt/ccp_pass.cpp \
source/opt/combine_access_chains.cpp \
source/opt/common_uniform_elim_pass.cpp \ source/opt/common_uniform_elim_pass.cpp \
source/opt/compact_ids_pass.cpp \ source/opt/compact_ids_pass.cpp \
source/opt/composite.cpp \ source/opt/composite.cpp \

View File

@ -636,6 +636,11 @@ Optimizer::PassToken CreateVectorDCEPass();
// a load of the specific elements. // a load of the specific elements.
Optimizer::PassToken CreateReduceLoadSizePass(); Optimizer::PassToken CreateReduceLoadSizePass();
// Create a pass to combine chained access chains.
// This pass looks for access chains fed by other access chains and combines
// them into a single instruction where possible.
Optimizer::PassToken CreateCombineAccessChainsPass();
} // namespace spvtools } // namespace spvtools
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_ #endif // SPIRV_TOOLS_OPTIMIZER_HPP_

View File

@ -19,6 +19,7 @@ add_library(SPIRV-Tools-opt
ccp_pass.h ccp_pass.h
cfg_cleanup_pass.h cfg_cleanup_pass.h
cfg.h cfg.h
combine_access_chains.h
common_uniform_elim_pass.h common_uniform_elim_pass.h
compact_ids_pass.h compact_ids_pass.h
composite.h composite.h
@ -106,6 +107,7 @@ add_library(SPIRV-Tools-opt
ccp_pass.cpp ccp_pass.cpp
cfg_cleanup_pass.cpp cfg_cleanup_pass.cpp
cfg.cpp cfg.cpp
combine_access_chains.cpp
common_uniform_elim_pass.cpp common_uniform_elim_pass.cpp
compact_ids_pass.cpp compact_ids_pass.cpp
composite.cpp composite.cpp

View File

@ -0,0 +1,288 @@
// Copyright (c) 2018 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 "combine_access_chains.h"
#include "constants.h"
#include "ir_builder.h"
#include "ir_context.h"
namespace spvtools {
namespace opt {
Pass::Status CombineAccessChains::Process() {
bool modified = false;
for (auto& function : *get_module()) {
modified |= ProcessFunction(function);
}
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
bool CombineAccessChains::ProcessFunction(Function& function) {
bool modified = false;
cfg()->ForEachBlockInReversePostOrder(
function.entry().get(), [&modified, this](BasicBlock* block) {
block->ForEachInst([&modified, this](Instruction* inst) {
switch (inst->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
case SpvOpPtrAccessChain:
case SpvOpInBoundsPtrAccessChain:
modified |= CombineAccessChain(inst);
break;
default:
break;
}
});
});
return modified;
}
uint32_t CombineAccessChains::GetConstantValue(
const analysis::Constant* constant_inst) {
if (constant_inst->type()->AsInteger()->width() <= 32) {
if (constant_inst->type()->AsInteger()->IsSigned()) {
return static_cast<uint32_t>(constant_inst->GetS32());
} else {
return constant_inst->GetU32();
}
} else {
assert(false);
return 0u;
}
}
uint32_t CombineAccessChains::GetArrayStride(const Instruction* inst) {
uint32_t array_stride = 0;
context()->get_decoration_mgr()->WhileEachDecoration(
inst->type_id(), SpvDecorationArrayStride,
[&array_stride](const Instruction& decoration) {
assert(decoration.opcode() != SpvOpDecorateId);
if (decoration.opcode() == SpvOpDecorate) {
array_stride = decoration.GetSingleWordInOperand(1);
} else {
array_stride = decoration.GetSingleWordInOperand(2);
}
return false;
});
return array_stride;
}
const analysis::Type* CombineAccessChains::GetIndexedType(Instruction* inst) {
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
Instruction* base_ptr = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
const analysis::Type* type = type_mgr->GetType(base_ptr->type_id());
assert(type->AsPointer());
type = type->AsPointer()->pointee_type();
std::vector<uint32_t> element_indices;
uint32_t starting_index = 1;
if (IsPtrAccessChain(inst->opcode())) {
// Skip the first index of OpPtrAccessChain as it does not affect type
// resolution.
starting_index = 2;
}
for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) {
Instruction* index_inst =
def_use_mgr->GetDef(inst->GetSingleWordInOperand(i));
const analysis::Constant* index_constant =
context()->get_constant_mgr()->GetConstantFromInst(index_inst);
if (index_constant) {
uint32_t index_value = GetConstantValue(index_constant);
element_indices.push_back(index_value);
} else {
// This index must not matter to resolve the type in valid SPIR-V.
element_indices.push_back(0);
}
}
type = type_mgr->GetMemberType(type, element_indices);
return type;
}
bool CombineAccessChains::CombineIndices(Instruction* ptr_input,
Instruction* inst,
std::vector<Operand>* new_operands) {
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
analysis::ConstantManager* constant_mgr = context()->get_constant_mgr();
Instruction* last_index_inst = def_use_mgr->GetDef(
ptr_input->GetSingleWordInOperand(ptr_input->NumInOperands() - 1));
const analysis::Constant* last_index_constant =
constant_mgr->GetConstantFromInst(last_index_inst);
Instruction* element_inst =
def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
const analysis::Constant* element_constant =
constant_mgr->GetConstantFromInst(element_inst);
// Combine the last index of the AccessChain (|ptr_inst|) with the element
// operand of the PtrAccessChain (|inst|).
const bool combining_element_operands =
IsPtrAccessChain(inst->opcode()) &&
IsPtrAccessChain(ptr_input->opcode()) && ptr_input->NumInOperands() == 2;
uint32_t new_value_id = 0;
const analysis::Type* type = GetIndexedType(ptr_input);
if (last_index_constant && element_constant) {
// Combine the constants.
uint32_t new_value = GetConstantValue(last_index_constant) +
GetConstantValue(element_constant);
const analysis::Constant* new_value_constant =
constant_mgr->GetConstant(last_index_constant->type(), {new_value});
Instruction* new_value_inst =
constant_mgr->GetDefiningInstruction(new_value_constant);
new_value_id = new_value_inst->result_id();
} else if (!type->AsStruct() || combining_element_operands) {
// Generate an addition of the two indices.
InstructionBuilder builder(
context(), inst,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
Instruction* addition = builder.AddIAdd(last_index_inst->type_id(),
last_index_inst->result_id(),
element_inst->result_id());
new_value_id = addition->result_id();
} else {
// Indexing into structs must be constant, so bail out here.
return false;
}
new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}});
return true;
}
bool CombineAccessChains::CreateNewInputOperands(
Instruction* ptr_input, Instruction* inst,
std::vector<Operand>* new_operands) {
// Start by copying all the input operands of the feeder access chain.
for (uint32_t i = 0; i != ptr_input->NumInOperands() - 1; ++i) {
new_operands->push_back(ptr_input->GetInOperand(i));
}
// Deal with the last index of the feeder access chain.
if (IsPtrAccessChain(inst->opcode())) {
// The last index of the feeder should be combined with the element operand
// of |inst|.
if (!CombineIndices(ptr_input, inst, new_operands)) return false;
} else {
// The indices aren't being combined so now add the last index operand of
// |ptr_input|.
new_operands->push_back(
ptr_input->GetInOperand(ptr_input->NumInOperands() - 1));
}
// Copy the remaining index operands.
uint32_t starting_index = IsPtrAccessChain(inst->opcode()) ? 2 : 1;
for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) {
new_operands->push_back(inst->GetInOperand(i));
}
return true;
}
bool CombineAccessChains::CombineAccessChain(Instruction* inst) {
assert((inst->opcode() == SpvOpPtrAccessChain ||
inst->opcode() == SpvOpAccessChain ||
inst->opcode() == SpvOpInBoundsAccessChain ||
inst->opcode() == SpvOpInBoundsPtrAccessChain) &&
"Wrong opcode. Expected an access chain.");
Instruction* ptr_input =
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
if (ptr_input->opcode() != SpvOpAccessChain &&
ptr_input->opcode() != SpvOpInBoundsAccessChain &&
ptr_input->opcode() != SpvOpPtrAccessChain &&
ptr_input->opcode() != SpvOpInBoundsPtrAccessChain) {
return false;
}
if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false;
// Handles the following cases:
// 1. |ptr_input| is an index-less access chain. Replace the pointer
// in |inst| with |ptr_input|'s pointer.
// 2. |inst| is a index-less access chain. Change |inst| to an
// OpCopyObject.
// 3. |inst| is not a pointer access chain.
// |inst|'s indices are appended to |ptr_input|'s indices.
// 4. |ptr_input| is not pointer access chain.
// |inst| is a pointer access chain.
// |inst|'s element operand is combined with the last index in
// |ptr_input| to form a new operand.
// 5. |ptr_input| is a pointer access chain.
// Like the above scenario, |inst|'s element operand is combined
// with |ptr_input|'s last index. This results is either a
// combined element operand or combined regular index.
// TODO(alan-baker): Support this properly. Requires analyzing the
// size/alignment of the type and converting the stride into an element
// index.
uint32_t array_stride = GetArrayStride(ptr_input);
if (array_stride != 0) return false;
if (ptr_input->NumInOperands() == 1) {
// The input is effectively a no-op.
inst->SetInOperand(0, {ptr_input->GetSingleWordInOperand(0)});
context()->AnalyzeUses(inst);
} else if (inst->NumInOperands() == 1) {
// |inst| is a no-op, change it to a copy. Instruction simplification will
// clean it up.
inst->SetOpcode(SpvOpCopyObject);
} else {
std::vector<Operand> new_operands;
if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false;
// Update the instruction.
inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode()));
inst->SetInOperands(std::move(new_operands));
context()->AnalyzeUses(inst);
}
return true;
}
SpvOp CombineAccessChains::UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode) {
auto IsInBounds = [](SpvOp opcode) {
return opcode == SpvOpInBoundsPtrAccessChain ||
opcode == SpvOpInBoundsAccessChain;
};
if (input_opcode == SpvOpInBoundsPtrAccessChain) {
if (!IsInBounds(base_opcode)) return SpvOpPtrAccessChain;
} else if (input_opcode == SpvOpInBoundsAccessChain) {
if (!IsInBounds(base_opcode)) return SpvOpAccessChain;
}
return input_opcode;
}
bool CombineAccessChains::IsPtrAccessChain(SpvOp opcode) {
return opcode == SpvOpPtrAccessChain || opcode == SpvOpInBoundsPtrAccessChain;
}
bool CombineAccessChains::Has64BitIndices(Instruction* inst) {
for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
Instruction* index_inst =
context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(i));
const analysis::Type* index_type =
context()->get_type_mgr()->GetType(index_inst->type_id());
if (!index_type->AsInteger() || index_type->AsInteger()->width() != 32)
return true;
}
return false;
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,80 @@
// Copyright (c) 2018 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.
#ifndef LIBSPIRV_OPT_COMBINE_ACCESS_CHAINS_H_
#define LIBSPIRV_OPT_COMBINE_ACCESS_CHAINS_H_
#include "pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class CombineAccessChains : public Pass {
public:
const char* name() const override { return "combine-access-chains"; }
Status Process() override;
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDefUse |
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
IRContext::kAnalysisNameMap;
}
private:
// Combine access chains in |function|. Blocks are processed in reverse
// post-order. Returns true if the function is modified.
bool ProcessFunction(Function& function);
// Combines an access chain (normal, in bounds or pointer) |inst| if its base
// pointer is another access chain. Returns true if the access chain was
// modified.
bool CombineAccessChain(Instruction* inst);
// Returns the value of |constant_inst| as a uint32_t.
uint32_t GetConstantValue(const analysis::Constant* constant_inst);
// Returns the array stride of |inst|'s type.
uint32_t GetArrayStride(const Instruction* inst);
// Returns the type by resolving the index operands |inst|. |inst| must be an
// access chain instruction.
const analysis::Type* GetIndexedType(Instruction* inst);
// Populates |new_operands| with the operands for the combined access chain.
// Returns false if the access chains cannot be combined.
bool CreateNewInputOperands(Instruction* ptr_input, Instruction* inst,
std::vector<Operand>* new_operands);
// Combines the last index of |ptr_input| with the element operand of |inst|.
// Adds the combined operand to |new_operands|.
bool CombineIndices(Instruction* ptr_input, Instruction* inst,
std::vector<Operand>* new_operands);
// Returns the opcode to use for the combined access chain.
SpvOp UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode);
// Returns true if |opcode| is a pointer access chain.
bool IsPtrAccessChain(SpvOp opcode);
// Returns true if |inst| (an access chain) has 64-bit indices.
bool Has64BitIndices(Instruction* inst);
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_COMBINE_ACCESS_CHAINS_H_

View File

@ -1907,6 +1907,37 @@ FoldingRule RedundantFMix() {
}; };
} }
// This rule handles addition of zero for integers.
FoldingRule RedundantIAdd() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
assert(inst->opcode() == SpvOpIAdd && "Wrong opcode. Should be OpIAdd.");
uint32_t operand = std::numeric_limits<uint32_t>::max();
const analysis::Type* operand_type = nullptr;
if (constants[0] && constants[0]->IsZero()) {
operand = inst->GetSingleWordInOperand(1);
operand_type = constants[0]->type();
} else if (constants[1] && constants[1]->IsZero()) {
operand = inst->GetSingleWordInOperand(0);
operand_type = constants[1]->type();
}
if (operand != std::numeric_limits<uint32_t>::max()) {
const analysis::Type* inst_type =
context->get_type_mgr()->GetType(inst->type_id());
if (inst_type->IsSame(operand_type)) {
inst->SetOpcode(SpvOpCopyObject);
} else {
inst->SetOpcode(SpvOpBitcast);
}
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}});
return true;
}
return false;
};
}
// This rule look for a dot with a constant vector containing a single 1 and // This rule look for a dot with a constant vector containing a single 1 and
// the rest 0s. This is the same as doing an extract. // the rest 0s. This is the same as doing an extract.
FoldingRule DotProductDoingExtract() { FoldingRule DotProductDoingExtract() {
@ -2177,6 +2208,7 @@ FoldingRules::FoldingRules() {
rules_[SpvOpFSub].push_back(MergeSubAddArithmetic()); rules_[SpvOpFSub].push_back(MergeSubAddArithmetic());
rules_[SpvOpFSub].push_back(MergeSubSubArithmetic()); rules_[SpvOpFSub].push_back(MergeSubSubArithmetic());
rules_[SpvOpIAdd].push_back(RedundantIAdd());
rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic()); rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());
rules_[SpvOpIAdd].push_back(MergeAddAddArithmetic()); rules_[SpvOpIAdd].push_back(MergeAddAddArithmetic());
rules_[SpvOpIAdd].push_back(MergeAddSubArithmetic()); rules_[SpvOpIAdd].push_back(MergeAddSubArithmetic());

View File

@ -159,6 +159,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateCCPPass()) .RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateRedundancyEliminationPass()) .RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCombineAccessChainsPass())
.RegisterPass(CreateSimplificationPass()) .RegisterPass(CreateSimplificationPass())
.RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateVectorDCEPass())
.RegisterPass(CreateDeadInsertElimPass()) .RegisterPass(CreateDeadInsertElimPass())
@ -303,6 +304,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateInlineExhaustivePass()); RegisterPass(CreateInlineExhaustivePass());
} else if (pass_name == "inline-entry-points-opaque") { } else if (pass_name == "inline-entry-points-opaque") {
RegisterPass(CreateInlineOpaquePass()); RegisterPass(CreateInlineOpaquePass());
} else if (pass_name == "combine-access-chains") {
RegisterPass(CreateCombineAccessChainsPass());
} else if (pass_name == "convert-local-access-chains") { } else if (pass_name == "convert-local-access-chains") {
RegisterPass(CreateLocalAccessChainConvertPass()); RegisterPass(CreateLocalAccessChainConvertPass());
} else if (pass_name == "eliminate-dead-code-aggressive") { } else if (pass_name == "eliminate-dead-code-aggressive") {
@ -710,4 +713,9 @@ Optimizer::PassToken CreateReduceLoadSizePass() {
return MakeUnique<Optimizer::PassToken::Impl>( return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::ReduceLoadSize>()); MakeUnique<opt::ReduceLoadSize>());
} }
Optimizer::PassToken CreateCombineAccessChainsPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::CombineAccessChains>());
}
} // namespace spvtools } // namespace spvtools

View File

@ -21,6 +21,7 @@
#include "block_merge_pass.h" #include "block_merge_pass.h"
#include "ccp_pass.h" #include "ccp_pass.h"
#include "cfg_cleanup_pass.h" #include "cfg_cleanup_pass.h"
#include "combine_access_chains.h"
#include "common_uniform_elim_pass.h" #include "common_uniform_elim_pass.h"
#include "compact_ids_pass.h" #include "compact_ids_pass.h"
#include "copy_prop_arrays.h" #include "copy_prop_arrays.h"

View File

@ -332,3 +332,8 @@ add_spvtools_unittest(TARGET constant_manager
LIBS SPIRV-Tools-opt LIBS SPIRV-Tools-opt
) )
add_spvtools_unittest(TARGET combine_access_chains
SRCS combine_access_chains_test.cpp
LIBS SPIRV-Tools-opt
)

View File

@ -0,0 +1,752 @@
// Copyright (c) 2018 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 "assembly_builder.h"
#include "gmock/gmock.h"
#include "pass_fixture.h"
#include "pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using CombineAccessChainsTest = PassTest<::testing::Test>;
#ifdef SPIRV_EFFCEE
TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainConstant) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpAccessChain %ptr_Workgroup_uint %var %uint_0
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, PtrAccessChainFromInBoundsAccessChainConstant) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpInBoundsAccessChain %ptr_Workgroup_uint %var %uint_0
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainCombineConstant) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int2]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpAccessChain %ptr_Workgroup_uint %var %uint_1
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainNonConstant) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[ld1:%\w+]] = OpLoad
; CHECK: [[ld2:%\w+]] = OpLoad
; CHECK: [[add:%\w+]] = OpIAdd [[int]] [[ld1]] [[ld2]]
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[add]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%local_var = OpVariable %ptr_Function_uint Function
%ld1 = OpLoad %uint %local_var
%gep = OpAccessChain %ptr_Workgroup_uint %var %ld1
%ld2 = OpLoad %uint %local_var
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainExtraIndices) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1
; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4
%uint_array_4_array_4_array_4 = OpTypeArray %uint_array_4_array_4 %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4
%ptr_Workgroup_uint_array_4_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4_array_4_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1 %uint_0
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_2 %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest,
PtrAccessChainFromPtrAccessChainCombineElementOperand) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6
; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int6]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest,
PtrAccessChainFromPtrAccessChainOnlyElementOperand) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4
; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]]
; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6
; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest,
PtrAccessChainFromPtrAccessCombineNonElementIndex) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int3]] [[int3]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 %uint_0
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest,
AccessChainFromPtrAccessChainOnlyElementOperand) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int3]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, AccessChainFromPtrAccessChainAppend) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1
; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2
; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]] [[int3]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1 %uint_2
%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, AccessChainFromAccessChainAppend) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1
; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2
; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%ptr_gep = OpAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1
%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, NonConstantStructSlide) {
const std::string text = R"(
; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[ld:%\w+]] = OpLoad
; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[ld]] [[int0]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%struct = OpTypeStruct %uint %uint
%ptr_Workgroup_struct = OpTypePointer Workgroup %struct
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%wg_var = OpVariable %ptr_Workgroup_struct Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%1 = OpLabel
%func_var = OpVariable %ptr_Function_uint Function
%ld = OpLoad %uint %func_var
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_struct %wg_var %ld
%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, DontCombineNonConstantStructSlide) {
const std::string text = R"(
; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[ld:%\w+]] = OpLoad
; CHECK: [[gep:%\w+]] = OpAccessChain
; CHECK: OpPtrAccessChain {{%\w+}} [[gep]] [[ld]] [[int0]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
%struct = OpTypeStruct %uint %uint
%struct_array_4 = OpTypeArray %struct %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%ptr_Workgroup_struct = OpTypePointer Workgroup %struct
%ptr_Workgroup_struct_array_4 = OpTypePointer Workgroup %struct_array_4
%wg_var = OpVariable %ptr_Workgroup_struct_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%1 = OpLabel
%func_var = OpVariable %ptr_Function_uint Function
%ld = OpLoad %uint %func_var
%gep = OpAccessChain %ptr_Workgroup_struct %wg_var %uint_0
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld %uint_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, CombineNonConstantStructSlideElement) {
const std::string text = R"(
; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[ld:%\w+]] = OpLoad
; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] [[ld]]
; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[add]] [[int0]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
%struct = OpTypeStruct %uint %uint
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Function_uint = OpTypePointer Function %uint
%ptr_Workgroup_struct = OpTypePointer Workgroup %struct
%wg_var = OpVariable %ptr_Workgroup_struct Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%1 = OpLabel
%func_var = OpVariable %ptr_Function_uint Function
%ld = OpLoad %uint %func_var
%gep = OpPtrAccessChain %ptr_Workgroup_struct %wg_var %ld
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld %uint_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, PtrAccessChainFromInBoundsPtrAccessChain) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4
; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]]
; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6
; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]]
OpCapability Shader
OpCapability VariablePointers
OpCapability Addresses
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, InBoundsPtrAccessChainFromPtrAccessChain) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4
; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]]
; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6
; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]]
OpCapability Shader
OpCapability VariablePointers
OpCapability Addresses
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%ptr_gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest,
InBoundsPtrAccessChainFromInBoundsPtrAccessChain) {
const std::string text = R"(
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4
; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]]
; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]]
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup
; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6
; CHECK: OpInBoundsPtrAccessChain [[ptr_array]] [[var]] [[int6]]
OpCapability Shader
OpCapability VariablePointers
OpCapability Addresses
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_array_4 = OpTypeArray %uint %uint_4
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4
%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%main_lab = OpLabel
%gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3
%ptr_gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, NoIndexAccessChains) {
const std::string text = R"(
; CHECK: [[var:%\w+]] = OpVariable
; CHECK-NOT: OpConstant
; CHECK: [[gep:%\w+]] = OpAccessChain {{%\w+}} [[var]]
; CHECK: OpAccessChain {{%\w+}} [[var]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%var = OpVariable %ptr_Workgroup_uint Workgroup
%void_func = OpTypeFunction %void
%func = OpFunction %void None %void_func
%1 = OpLabel
%gep1 = OpAccessChain %ptr_Workgroup_uint %var
%gep2 = OpAccessChain %ptr_Workgroup_uint %gep1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, NoIndexPtrAccessChains) {
const std::string text = R"(
; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[var:%\w+]] = OpVariable
; CHECK: [[gep:%\w+]] = OpPtrAccessChain {{%\w+}} [[var]] [[int0]]
; CHECK: OpCopyObject {{%\w+}} [[gep]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%var = OpVariable %ptr_Workgroup_uint Workgroup
%void_func = OpTypeFunction %void
%func = OpFunction %void None %void_func
%1 = OpLabel
%gep1 = OpPtrAccessChain %ptr_Workgroup_uint %var %uint_0
%gep2 = OpAccessChain %ptr_Workgroup_uint %gep1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, NoIndexPtrAccessChains2) {
const std::string text = R"(
; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[var:%\w+]] = OpVariable
; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[int0]]
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%var = OpVariable %ptr_Workgroup_uint Workgroup
%void_func = OpTypeFunction %void
%func = OpFunction %void None %void_func
%1 = OpLabel
%gep1 = OpAccessChain %ptr_Workgroup_uint %var
%gep2 = OpPtrAccessChain %ptr_Workgroup_uint %gep1 %uint_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
TEST_F(CombineAccessChainsTest, CombineMixedSign) {
const std::string text = R"(
; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
; CHECK: [[var:%\w+]] = OpVariable
; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2
; CHECK: OpInBoundsPtrAccessChain {{%\w+}} [[var]] [[uint2]]
OpCapability Shader
OpCapability VariablePointers
OpCapability Addresses
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%uint_1 = OpConstant %uint 1
%int_1 = OpConstant %int 1
%ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%var = OpVariable %ptr_Workgroup_uint Workgroup
%void_func = OpTypeFunction %void
%func = OpFunction %void None %void_func
%1 = OpLabel
%gep1 = OpInBoundsPtrAccessChain %ptr_Workgroup_uint %var %uint_1
%gep2 = OpInBoundsPtrAccessChain %ptr_Workgroup_uint %gep1 %int_1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
#endif // SPIRV_EFFCEE
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -162,6 +162,7 @@ OpName %main "main"
%_ptr_v2float = OpTypePointer Function %v2float %_ptr_v2float = OpTypePointer Function %v2float
%_ptr_v2double = OpTypePointer Function %v2double %_ptr_v2double = OpTypePointer Function %v2double
%short_0 = OpConstant %short 0 %short_0 = OpConstant %short 0
%short_2 = OpConstant %short 2
%short_3 = OpConstant %short 3 %short_3 = OpConstant %short 3
%100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps. %100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps.
%103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps. %103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps.
@ -176,12 +177,15 @@ OpName %main "main"
%long_2 = OpConstant %long 2 %long_2 = OpConstant %long 2
%long_3 = OpConstant %long 3 %long_3 = OpConstant %long 3
%uint_0 = OpConstant %uint 0 %uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2 %uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3 %uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4 %uint_4 = OpConstant %uint 4
%uint_32 = OpConstant %uint 32 %uint_32 = OpConstant %uint 32
%uint_max = OpConstant %uint 4294967295 %uint_max = OpConstant %uint 4294967295
%v2int_undef = OpUndef %v2int %v2int_undef = OpUndef %v2int
%v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0
%v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0
%v2int_2_2 = OpConstantComposite %v2int %int_2 %int_2 %v2int_2_2 = OpConstantComposite %v2int %int_2 %int_2
%v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3 %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3
%v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2
@ -2589,19 +2593,19 @@ INSTANTIATE_TEST_CASE_P(IntegerArithmeticTestCases, GeneralInstructionFoldingTes
"OpReturn\n" + "OpReturn\n" +
"OpFunctionEnd", "OpFunctionEnd",
2, 0), 2, 0),
// Test case 38: Don't fold 0 + 3 (long), bad length // Test case 38: Don't fold 2 + 3 (long), bad length
InstructionFoldingCase<uint32_t>( InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" + Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" + "%main_lab = OpLabel\n" +
"%2 = OpIAdd %long %long_0 %long_3\n" + "%2 = OpIAdd %long %long_2 %long_3\n" +
"OpReturn\n" + "OpReturn\n" +
"OpFunctionEnd", "OpFunctionEnd",
2, 0), 2, 0),
// Test case 39: Don't fold 0 + 3 (short), bad length // Test case 39: Don't fold 2 + 3 (short), bad length
InstructionFoldingCase<uint32_t>( InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" + Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" + "%main_lab = OpLabel\n" +
"%2 = OpIAdd %short %short_0 %short_3\n" + "%2 = OpIAdd %short %short_2 %short_3\n" +
"OpReturn\n" + "OpReturn\n" +
"OpFunctionEnd", "OpFunctionEnd",
2, 0), 2, 0),
@ -3326,6 +3330,90 @@ INSTANTIATE_TEST_CASE_P(DoubleVectorRedundantFoldingTest, GeneralInstructionFold
2, 3) 2, 3)
)); ));
INSTANTIATE_TEST_CASE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingTest,
::testing::Values(
// Test case 0: Don't fold n + 1
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_uint Function\n" +
"%3 = OpLoad %uint %n\n" +
"%2 = OpIAdd %uint %3 %uint_1\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 1: Don't fold 1 + n
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_uint Function\n" +
"%3 = OpLoad %uint %n\n" +
"%2 = OpIAdd %uint %uint_1 %3\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 2: Fold n + 0
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_uint Function\n" +
"%3 = OpLoad %uint %n\n" +
"%2 = OpIAdd %uint %3 %uint_0\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 3),
// Test case 3: Fold 0 + n
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_uint Function\n" +
"%3 = OpLoad %uint %n\n" +
"%2 = OpIAdd %uint %uint_0 %3\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 3),
// Test case 4: Don't fold n + (1,0)
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_v2int Function\n" +
"%3 = OpLoad %v2int %n\n" +
"%2 = OpIAdd %v2int %3 %v2int_1_0\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 5: Don't fold (1,0) + n
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_v2int Function\n" +
"%3 = OpLoad %v2int %n\n" +
"%2 = OpIAdd %v2int %v2int_1_0 %3\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 6: Fold n + (0,0)
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_v2int Function\n" +
"%3 = OpLoad %v2int %n\n" +
"%2 = OpIAdd %v2int %3 %v2int_0_0\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 3),
// Test case 7: Fold (0,0) + n
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_v2int Function\n" +
"%3 = OpLoad %v2int %n\n" +
"%2 = OpIAdd %v2int %v2int_0_0 %3\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 3)
));
INSTANTIATE_TEST_CASE_P(ClampAndCmpLHS, GeneralInstructionFoldingTest, INSTANTIATE_TEST_CASE_P(ClampAndCmpLHS, GeneralInstructionFoldingTest,
::testing::Values( ::testing::Values(
// Test case 0: Don't Fold 0.0 < clamp(-1, 1) // Test case 0: Don't Fold 0.0 < clamp(-1, 1)
@ -3785,6 +3873,36 @@ TEST_P(MatchingInstructionFoldingTest, Case) {
} }
} }
INSTANTIATE_TEST_CASE_P(RedundantIntegerMatching, MatchingInstructionFoldingTest,
::testing::Values(
// Test case 0: Fold 0 + n (change sign)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
"; CHECK: %2 = OpBitcast [[uint]] %3\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_int Function\n" +
"%3 = OpLoad %uint %n\n" +
"%2 = OpIAdd %uint %int_0 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
2, true),
// Test case 0: Fold 0 + n (change sign)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
"; CHECK: %2 = OpBitcast [[int]] %3\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_int Function\n" +
"%3 = OpLoad %int %n\n" +
"%2 = OpIAdd %int %uint_0 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
2, true)
));
INSTANTIATE_TEST_CASE_P(MergeNegateTest, MatchingInstructionFoldingTest, INSTANTIATE_TEST_CASE_P(MergeNegateTest, MatchingInstructionFoldingTest,
::testing::Values( ::testing::Values(
// Test case 0: fold consecutive fnegate // Test case 0: fold consecutive fnegate

View File

@ -106,6 +106,9 @@ Options (in lexicographical order):
Cleanup the control flow graph. This will remove any unnecessary Cleanup the control flow graph. This will remove any unnecessary
code from the CFG like unreachable code. Performed on entry code from the CFG like unreachable code. Performed on entry
point call tree functions and exported functions. point call tree functions and exported functions.
--combine-access-chains
Combines chained access chains to produce a single instruction
where possible.
--compact-ids --compact-ids
Remap result ids to a compact range starting from %%1 and without Remap result ids to a compact range starting from %%1 and without
any gaps. any gaps.