Add passes to propagate and eliminate redundant line instructions (#2027). (#2039)

These are bookend passes designed to help preserve line information
across passes which delete, move and clone instructions. The propagation
pass attaches a debug line instruction to every instruction based on
SPIR-V line propagation rules. It should be performed before optimization.
The redundant line elimination pass eliminates all line instructions
which match the previous line instruction. This pass should be performed
at the end of optimization to reduce physical SPIR-V file size.

Fixes #2027.
This commit is contained in:
greg-lunarg 2018-11-15 12:06:17 -07:00 committed by Steven Perron
parent ab76e332de
commit c37388f1ad
13 changed files with 993 additions and 0 deletions

View File

@ -131,6 +131,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/pass.cpp \
source/opt/pass_manager.cpp \
source/opt/private_to_local_pass.cpp \
source/opt/process_lines_pass.cpp \
source/opt/propagator.cpp \
source/opt/reduce_load_size.cpp \
source/opt/redundancy_elimination.cpp \

View File

@ -573,6 +573,8 @@ static_library("spvtools_opt") {
"source/opt/passes.h",
"source/opt/private_to_local_pass.cpp",
"source/opt/private_to_local_pass.h",
"source/opt/process_lines_pass.cpp",
"source/opt/process_lines_pass.h",
"source/opt/propagator.cpp",
"source/opt/propagator.h",
"source/opt/reduce_load_size.cpp",

View File

@ -499,6 +499,30 @@ Optimizer::PassToken CreateCommonUniformElimPass();
// eliminated with standard dead code elimination.
Optimizer::PassToken CreateAggressiveDCEPass();
// Create line propagation pass
// This pass propagates line information based on the rules for OpLine and
// OpNoline and clones an appropriate line instruction into every instruction
// which does not already have debug line instructions.
//
// This pass is intended to maximize preservation of source line information
// through passes which delete, move and clone instructions. Ideally it should
// be run before any such pass. It is a bookend pass with EliminateDeadLines
// which can be used to remove redundant line instructions at the end of a
// run of such passes and reduce final output file size.
Optimizer::PassToken CreatePropagateLineInfoPass();
// Create dead line elimination pass
// This pass eliminates redundant line instructions based on the rules for
// OpLine and OpNoline. Its main purpose is to reduce the size of the file
// need to store the SPIR-V without losing line information.
//
// This is a bookend pass with PropagateLines which attaches line instructions
// to every instruction to preserve line information during passes which
// delete, move and clone instructions. DeadLineElim should be run after
// PropagateLines and all such subsequent passes. Normally it would be one
// of the last passes to be run.
Optimizer::PassToken CreateRedundantLineInfoElimPass();
// Creates a compact ids pass.
// The pass remaps result ids to a compact and gapless range starting from %1.
Optimizer::PassToken CreateCompactIdsPass();

View File

@ -77,6 +77,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
pass.h
pass_manager.h
private_to_local_pass.h
process_lines_pass.h
propagator.h
reduce_load_size.h
redundancy_elimination.h
@ -165,6 +166,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
pass.cpp
pass_manager.cpp
private_to_local_pass.cpp
process_lines_pass.cpp
propagator.cpp
reduce_load_size.cpp
redundancy_elimination.cpp

View File

@ -68,6 +68,13 @@ void Function::ForEachInst(const std::function<void(const Instruction*)>& f,
->ForEachInst(f, run_on_debug_line_insts);
}
void Function::ForEachParam(const std::function<void(Instruction*)>& f,
bool run_on_debug_line_insts) {
for (auto& param : params_)
static_cast<Instruction*>(param.get())
->ForEachInst(f, run_on_debug_line_insts);
}
void Function::ForEachParam(const std::function<void(const Instruction*)>& f,
bool run_on_debug_line_insts) const {
for (const auto& param : params_)

View File

@ -115,6 +115,8 @@ class Function {
// and optionally on debug line instructions that might precede them.
void ForEachParam(const std::function<void(const Instruction*)>& f,
bool run_on_debug_line_insts = false) const;
void ForEachParam(const std::function<void(Instruction*)>& f,
bool run_on_debug_line_insts = false);
BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
BasicBlock* position);

View File

@ -296,6 +296,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateLocalAccessChainConvertPass());
} else if (pass_name == "eliminate-dead-code-aggressive") {
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "propagate-line-info") {
RegisterPass(CreatePropagateLineInfoPass());
} else if (pass_name == "eliminate-redundant-line-info") {
RegisterPass(CreateRedundantLineInfoElimPass());
} else if (pass_name == "eliminate-insert-extract") {
RegisterPass(CreateInsertExtractElimPass());
} else if (pass_name == "eliminate-local-single-block") {
@ -624,6 +628,16 @@ Optimizer::PassToken CreateAggressiveDCEPass() {
MakeUnique<opt::AggressiveDCEPass>());
}
Optimizer::PassToken CreatePropagateLineInfoPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines));
}
Optimizer::PassToken CreateRedundantLineInfoElimPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
}
Optimizer::PassToken CreateCommonUniformElimPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::CommonUniformElimPass>());

View File

@ -51,6 +51,7 @@
#include "source/opt/merge_return_pass.h"
#include "source/opt/null_pass.h"
#include "source/opt/private_to_local_pass.h"
#include "source/opt/process_lines_pass.h"
#include "source/opt/reduce_load_size.h"
#include "source/opt/redundancy_elimination.h"
#include "source/opt/remove_duplicates_pass.h"

View File

@ -0,0 +1,157 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// 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 "source/opt/process_lines_pass.h"
#include <set>
#include <unordered_set>
#include <vector>
namespace {
// Input Operand Indices
static const int kSpvLineFileInIdx = 0;
static const int kSpvLineLineInIdx = 1;
static const int kSpvLineColInIdx = 2;
} // anonymous namespace
namespace spvtools {
namespace opt {
Pass::Status ProcessLinesPass::Process() {
bool modified = ProcessLines();
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
bool ProcessLinesPass::ProcessLines() {
bool modified = false;
uint32_t file_id = 0;
uint32_t line = 0;
uint32_t col = 0;
// Process types, globals, constants
for (Instruction& inst : get_module()->types_values())
modified |= line_process_func_(&inst, &file_id, &line, &col);
// Process functions
for (Function& function : *get_module()) {
modified |= line_process_func_(&function.DefInst(), &file_id, &line, &col);
function.ForEachParam(
[this, &modified, &file_id, &line, &col](Instruction* param) {
modified |= line_process_func_(param, &file_id, &line, &col);
});
for (BasicBlock& block : function) {
modified |=
line_process_func_(block.GetLabelInst(), &file_id, &line, &col);
for (Instruction& inst : block) {
modified |= line_process_func_(&inst, &file_id, &line, &col);
// Don't process terminal instruction if preceeded by merge
if (inst.opcode() == SpvOpSelectionMerge ||
inst.opcode() == SpvOpLoopMerge)
break;
}
// Nullify line info after each block.
file_id = 0;
}
modified |= line_process_func_(function.EndInst(), &file_id, &line, &col);
}
return modified;
}
bool ProcessLinesPass::PropagateLine(Instruction* inst, uint32_t* file_id,
uint32_t* line, uint32_t* col) {
bool modified = false;
// only the last debug instruction needs to be considered
auto line_itr = inst->dbg_line_insts().rbegin();
// if no line instructions, propagate previous info
if (line_itr == inst->dbg_line_insts().rend()) {
// if no current line info, add OpNoLine, else OpLine
if (*file_id == 0)
inst->dbg_line_insts().push_back(Instruction(context(), SpvOpNoLine));
else
inst->dbg_line_insts().push_back(Instruction(
context(), SpvOpLine, 0, 0,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*file_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {*line}},
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {*col}}}));
modified = true;
} else {
// else pre-existing line instruction, so update source line info
if (line_itr->opcode() == SpvOpNoLine) {
*file_id = 0;
} else {
assert(line_itr->opcode() == SpvOpLine && "unexpected debug inst");
*file_id = line_itr->GetSingleWordInOperand(kSpvLineFileInIdx);
*line = line_itr->GetSingleWordInOperand(kSpvLineLineInIdx);
*col = line_itr->GetSingleWordInOperand(kSpvLineColInIdx);
}
}
return modified;
}
bool ProcessLinesPass::EliminateDeadLines(Instruction* inst, uint32_t* file_id,
uint32_t* line, uint32_t* col) {
// If no debug line instructions, return without modifying lines
if (inst->dbg_line_insts().empty()) return false;
// Only the last debug instruction needs to be considered; delete all others
bool modified = inst->dbg_line_insts().size() > 1;
Instruction last_inst = inst->dbg_line_insts().back();
inst->dbg_line_insts().clear();
// If last line is OpNoLine
if (last_inst.opcode() == SpvOpNoLine) {
// If no propagated line info, throw away redundant OpNoLine
if (*file_id == 0) {
modified = true;
// Else replace OpNoLine and propagate no line info
} else {
inst->dbg_line_insts().push_back(last_inst);
*file_id = 0;
}
} else {
// Else last line is OpLine
assert(last_inst.opcode() == SpvOpLine && "unexpected debug inst");
// If propagated info matches last line, throw away last line
if (*file_id == last_inst.GetSingleWordInOperand(kSpvLineFileInIdx) &&
*line == last_inst.GetSingleWordInOperand(kSpvLineLineInIdx) &&
*col == last_inst.GetSingleWordInOperand(kSpvLineColInIdx)) {
modified = true;
} else {
// Else replace last line and propagate line info
*file_id = last_inst.GetSingleWordInOperand(kSpvLineFileInIdx);
*line = last_inst.GetSingleWordInOperand(kSpvLineLineInIdx);
*col = last_inst.GetSingleWordInOperand(kSpvLineColInIdx);
inst->dbg_line_insts().push_back(last_inst);
}
}
return modified;
}
ProcessLinesPass::ProcessLinesPass(uint32_t func_id) {
if (func_id == kLinesPropagateLines) {
line_process_func_ = [this](Instruction* inst, uint32_t* file_id,
uint32_t* line, uint32_t* col) {
return PropagateLine(inst, file_id, line, col);
};
} else {
assert(func_id == kLinesEliminateDeadLines && "unknown Lines param");
line_process_func_ = [this](Instruction* inst, uint32_t* file_id,
uint32_t* line, uint32_t* col) {
return EliminateDeadLines(inst, file_id, line, col);
};
}
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,86 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// 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 SOURCE_OPT_PROPAGATE_LINES_PASS_H_
#define SOURCE_OPT_PROPAGATE_LINES_PASS_H_
#include "source/opt/function.h"
#include "source/opt/ir_context.h"
#include "source/opt/pass.h"
namespace spvtools {
namespace opt {
namespace {
// Constructor Parameters
static const int kLinesPropagateLines = 0;
static const int kLinesEliminateDeadLines = 1;
} // anonymous namespace
// See optimizer.hpp for documentation.
class ProcessLinesPass : public Pass {
using LineProcessFunction =
std::function<bool(Instruction*, uint32_t*, uint32_t*, uint32_t*)>;
public:
ProcessLinesPass(uint32_t func_id);
~ProcessLinesPass() = default;
const char* name() const override { return "propagate-lines"; }
// See optimizer.hpp for this pass' user documentation.
Status Process() override;
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDefUse |
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
IRContext::kAnalysisNameMap;
}
private:
// If |inst| has no debug line instruction, create one with
// |file_id, line, col|. If |inst| has debug line instructions, set
// |file_id, line, col| from the last. |file_id| equals 0 indicates no line
// info is available. Return true if |inst| modified.
bool PropagateLine(Instruction* inst, uint32_t* file_id, uint32_t* line,
uint32_t* col);
// If last debug line instruction of |inst| matches |file_id, line, col|,
// delete all debug line instructions of |inst|. If they do not match,
// replace all debug line instructions of |inst| with new line instruction
// set from |file_id, line, col|. If |inst| has no debug line instructions,
// do not modify |inst|. |file_id| equals 0 indicates no line info is
// available. Return true if |inst| modified.
bool EliminateDeadLines(Instruction* inst, uint32_t* file_id, uint32_t* line,
uint32_t* col);
// Apply lpfn() to all type, constant, global variable and function
// instructions in their physical order.
bool ProcessLines();
// A function that calls either PropagateLine or EliminateDeadLines.
// Initialized by the class constructor.
LineProcessFunction line_process_func_;
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_PROPAGATE_LINES_PASS_H_

View File

@ -63,6 +63,7 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) {
}
break;
case SpvOpLine:
case SpvOpNoLine:
break;
case SpvOpLoopMerge:
adjacency_status = PHI_AND_VAR_INVALID;

View File

@ -64,6 +64,7 @@ add_spvtools_unittest(TARGET opt
pass_test.cpp pass_utils.cpp
pass_utils.cpp
private_to_local_test.cpp
process_lines_test.cpp
propagator_test.cpp
reduce_load_size_test.cpp
redundancy_elimination_test.cpp

View File

@ -0,0 +1,695 @@
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
//
// 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 <memory>
#include <string>
#include <vector>
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using ProcessLinesTest = PassTest<::testing::Test>;
TEST_F(ProcessLinesTest, SimplePropagation) {
// Texture2D g_tColor[128];
//
// layout(push_constant) cbuffer PerViewConstantBuffer_t
// {
// uint g_nDataIdx;
// uint g_nDataIdx2;
// bool g_B;
// };
//
// SamplerState g_sAniso;
//
// struct PS_INPUT
// {
// float2 vTextureCoords : TEXCOORD2;
// };
//
// struct PS_OUTPUT
// {
// float4 vColor : SV_Target0;
// };
//
// PS_OUTPUT MainPs(PS_INPUT i)
// {
// PS_OUTPUT ps_output;
//
// uint u;
// if (g_B)
// u = g_nDataIdx;
// else
// u = g_nDataIdx2;
// ps_output.vColor = g_tColor[u].Sample(g_sAniso, i.vTextureCoords.xy);
// return ps_output;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
OpExecutionMode %MainPs OriginUpperLeft
%5 = OpString "foo.frag"
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %PS_INPUT "PS_INPUT"
OpMemberName %PS_INPUT 0 "vTextureCoords"
OpName %PS_OUTPUT "PS_OUTPUT"
OpMemberName %PS_OUTPUT 0 "vColor"
OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;"
OpName %i "i"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpMemberName %PerViewConstantBuffer_t 1 "g_nDataIdx2"
OpMemberName %PerViewConstantBuffer_t 2 "g_B"
OpName %_ ""
OpName %u "u"
OpName %ps_output "ps_output"
OpName %g_tColor "g_tColor"
OpName %g_sAniso "g_sAniso"
OpName %i_0 "i"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpName %param "param"
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
OpMemberDecorate %PerViewConstantBuffer_t 2 Offset 8
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_tColor DescriptorSet 0
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
)";
const std::string before =
R"(%void = OpTypeVoid
%19 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%PS_INPUT = OpTypeStruct %v2float
%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
%v4float = OpTypeVector %float 4
%PS_OUTPUT = OpTypeStruct %v4float
%24 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
%uint = OpTypeInt 32 0
%PerViewConstantBuffer_t = OpTypeStruct %uint %uint %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%int = OpTypeInt 32 1
%int_2 = OpConstant %int 2
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%_ptr_Function_uint = OpTypePointer Function %uint
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
%36 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint_128 = OpConstant %uint 128
%_arr_36_uint_128 = OpTypeArray %36 %uint_128
%_ptr_UniformConstant__arr_36_uint_128 = OpTypePointer UniformConstant %_arr_36_uint_128
%g_tColor = OpVariable %_ptr_UniformConstant__arr_36_uint_128 UniformConstant
%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
%41 = OpTypeSampler
%_ptr_UniformConstant_41 = OpTypePointer UniformConstant %41
%g_sAniso = OpVariable %_ptr_UniformConstant_41 UniformConstant
%43 = OpTypeSampledImage %36
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
%MainPs = OpFunction %void None %19
%48 = OpLabel
%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
%param = OpVariable %_ptr_Function_PS_INPUT Function
OpLine %5 23 0
%49 = OpLoad %v2float %i_vTextureCoords
%50 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
OpStore %50 %49
%51 = OpLoad %PS_INPUT %i_0
OpStore %param %51
%52 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
%53 = OpCompositeExtract %v4float %52 0
OpStore %_entryPointOutput_vColor %53
OpReturn
OpFunctionEnd
%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %24
%i = OpFunctionParameter %_ptr_Function_PS_INPUT
%54 = OpLabel
%u = OpVariable %_ptr_Function_uint Function
%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
OpLine %5 27 0
%55 = OpAccessChain %_ptr_PushConstant_uint %_ %int_2
%56 = OpLoad %uint %55
%57 = OpINotEqual %bool %56 %uint_0
OpSelectionMerge %58 None
OpBranchConditional %57 %59 %60
%59 = OpLabel
OpLine %5 28 0
%61 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%62 = OpLoad %uint %61
OpStore %u %62
OpBranch %58
%60 = OpLabel
OpLine %5 30 0
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
%64 = OpLoad %uint %63
OpStore %u %64
OpBranch %58
%58 = OpLabel
OpLine %5 31 0
%65 = OpLoad %uint %u
%66 = OpAccessChain %_ptr_UniformConstant_36 %g_tColor %65
%67 = OpLoad %36 %66
%68 = OpLoad %41 %g_sAniso
%69 = OpSampledImage %43 %67 %68
%70 = OpAccessChain %_ptr_Function_v2float %i %int_0
%71 = OpLoad %v2float %70
%72 = OpImageSampleImplicitLod %v4float %69 %71
%73 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
OpStore %73 %72
OpLine %5 32 0
%74 = OpLoad %PS_OUTPUT %ps_output
OpReturnValue %74
OpFunctionEnd
)";
const std::string after =
R"(OpNoLine
%void = OpTypeVoid
OpNoLine
%19 = OpTypeFunction %void
OpNoLine
%float = OpTypeFloat 32
OpNoLine
%v2float = OpTypeVector %float 2
OpNoLine
%PS_INPUT = OpTypeStruct %v2float
OpNoLine
%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
OpNoLine
%v4float = OpTypeVector %float 4
OpNoLine
%PS_OUTPUT = OpTypeStruct %v4float
OpNoLine
%24 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
OpNoLine
%uint = OpTypeInt 32 0
OpNoLine
%PerViewConstantBuffer_t = OpTypeStruct %uint %uint %uint
OpNoLine
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
OpNoLine
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
OpNoLine
%int = OpTypeInt 32 1
OpNoLine
%int_2 = OpConstant %int 2
OpNoLine
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
OpNoLine
%bool = OpTypeBool
OpNoLine
%uint_0 = OpConstant %uint 0
OpNoLine
%_ptr_Function_uint = OpTypePointer Function %uint
OpNoLine
%int_0 = OpConstant %int 0
OpNoLine
%int_1 = OpConstant %int 1
OpNoLine
%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
OpNoLine
%36 = OpTypeImage %float 2D 0 0 0 1 Unknown
OpNoLine
%uint_128 = OpConstant %uint 128
OpNoLine
%_arr_36_uint_128 = OpTypeArray %36 %uint_128
OpNoLine
%_ptr_UniformConstant__arr_36_uint_128 = OpTypePointer UniformConstant %_arr_36_uint_128
OpNoLine
%g_tColor = OpVariable %_ptr_UniformConstant__arr_36_uint_128 UniformConstant
OpNoLine
%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
OpNoLine
%41 = OpTypeSampler
OpNoLine
%_ptr_UniformConstant_41 = OpTypePointer UniformConstant %41
OpNoLine
%g_sAniso = OpVariable %_ptr_UniformConstant_41 UniformConstant
OpNoLine
%43 = OpTypeSampledImage %36
OpNoLine
%_ptr_Function_v2float = OpTypePointer Function %v2float
OpNoLine
%_ptr_Function_v4float = OpTypePointer Function %v4float
OpNoLine
%_ptr_Input_v2float = OpTypePointer Input %v2float
OpNoLine
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
OpNoLine
%_ptr_Output_v4float = OpTypePointer Output %v4float
OpNoLine
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
OpNoLine
%MainPs = OpFunction %void None %19
OpNoLine
%48 = OpLabel
OpNoLine
%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
OpNoLine
%param = OpVariable %_ptr_Function_PS_INPUT Function
OpLine %5 23 0
%49 = OpLoad %v2float %i_vTextureCoords
OpLine %5 23 0
%50 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
OpLine %5 23 0
OpStore %50 %49
OpLine %5 23 0
%51 = OpLoad %PS_INPUT %i_0
OpLine %5 23 0
OpStore %param %51
OpLine %5 23 0
%52 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
OpLine %5 23 0
%53 = OpCompositeExtract %v4float %52 0
OpLine %5 23 0
OpStore %_entryPointOutput_vColor %53
OpLine %5 23 0
OpReturn
OpNoLine
OpFunctionEnd
OpNoLine
%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %24
OpNoLine
%i = OpFunctionParameter %_ptr_Function_PS_INPUT
OpNoLine
%54 = OpLabel
OpNoLine
%u = OpVariable %_ptr_Function_uint Function
OpNoLine
%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
OpLine %5 27 0
%55 = OpAccessChain %_ptr_PushConstant_uint %_ %int_2
OpLine %5 27 0
%56 = OpLoad %uint %55
OpLine %5 27 0
%57 = OpINotEqual %bool %56 %uint_0
OpLine %5 27 0
OpSelectionMerge %58 None
OpBranchConditional %57 %59 %60
OpNoLine
%59 = OpLabel
OpLine %5 28 0
%61 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
OpLine %5 28 0
%62 = OpLoad %uint %61
OpLine %5 28 0
OpStore %u %62
OpLine %5 28 0
OpBranch %58
OpNoLine
%60 = OpLabel
OpLine %5 30 0
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
OpLine %5 30 0
%64 = OpLoad %uint %63
OpLine %5 30 0
OpStore %u %64
OpLine %5 30 0
OpBranch %58
OpNoLine
%58 = OpLabel
OpLine %5 31 0
%65 = OpLoad %uint %u
OpLine %5 31 0
%66 = OpAccessChain %_ptr_UniformConstant_36 %g_tColor %65
OpLine %5 31 0
%67 = OpLoad %36 %66
OpLine %5 31 0
%68 = OpLoad %41 %g_sAniso
OpLine %5 31 0
%69 = OpSampledImage %43 %67 %68
OpLine %5 31 0
%70 = OpAccessChain %_ptr_Function_v2float %i %int_0
OpLine %5 31 0
%71 = OpLoad %v2float %70
OpLine %5 31 0
%72 = OpImageSampleImplicitLod %v4float %69 %71
OpLine %5 31 0
%73 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
OpLine %5 31 0
OpStore %73 %72
OpLine %5 32 0
%74 = OpLoad %PS_OUTPUT %ps_output
OpLine %5 32 0
OpReturnValue %74
OpNoLine
OpFunctionEnd
)";
SinglePassRunAndCheck<ProcessLinesPass>(predefs + before, predefs + after,
false, true, kLinesPropagateLines);
}
TEST_F(ProcessLinesTest, SimpleElimination) {
// Previous test with before and after reversed
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
OpExecutionMode %MainPs OriginUpperLeft
%5 = OpString "foo.frag"
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %PS_INPUT "PS_INPUT"
OpMemberName %PS_INPUT 0 "vTextureCoords"
OpName %PS_OUTPUT "PS_OUTPUT"
OpMemberName %PS_OUTPUT 0 "vColor"
OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;"
OpName %i "i"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpMemberName %PerViewConstantBuffer_t 1 "g_nDataIdx2"
OpMemberName %PerViewConstantBuffer_t 2 "g_B"
OpName %_ ""
OpName %u "u"
OpName %ps_output "ps_output"
OpName %g_tColor "g_tColor"
OpName %g_sAniso "g_sAniso"
OpName %i_0 "i"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpName %param "param"
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
OpMemberDecorate %PerViewConstantBuffer_t 2 Offset 8
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_tColor DescriptorSet 0
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
)";
const std::string before =
R"(OpNoLine
%void = OpTypeVoid
OpNoLine
%19 = OpTypeFunction %void
OpNoLine
%float = OpTypeFloat 32
OpNoLine
%v2float = OpTypeVector %float 2
OpNoLine
%PS_INPUT = OpTypeStruct %v2float
OpNoLine
%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
OpNoLine
%v4float = OpTypeVector %float 4
OpNoLine
%PS_OUTPUT = OpTypeStruct %v4float
OpNoLine
%24 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
OpNoLine
%uint = OpTypeInt 32 0
OpNoLine
%PerViewConstantBuffer_t = OpTypeStruct %uint %uint %uint
OpNoLine
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
OpNoLine
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
OpNoLine
%int = OpTypeInt 32 1
OpNoLine
%int_2 = OpConstant %int 2
OpNoLine
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
OpNoLine
%bool = OpTypeBool
OpNoLine
%uint_0 = OpConstant %uint 0
OpNoLine
%_ptr_Function_uint = OpTypePointer Function %uint
OpNoLine
%int_0 = OpConstant %int 0
OpNoLine
%int_1 = OpConstant %int 1
OpNoLine
%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
OpNoLine
%36 = OpTypeImage %float 2D 0 0 0 1 Unknown
OpNoLine
%uint_128 = OpConstant %uint 128
OpNoLine
%_arr_36_uint_128 = OpTypeArray %36 %uint_128
OpNoLine
%_ptr_UniformConstant__arr_36_uint_128 = OpTypePointer UniformConstant %_arr_36_uint_128
OpNoLine
%g_tColor = OpVariable %_ptr_UniformConstant__arr_36_uint_128 UniformConstant
OpNoLine
%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
OpNoLine
%41 = OpTypeSampler
OpNoLine
%_ptr_UniformConstant_41 = OpTypePointer UniformConstant %41
OpNoLine
%g_sAniso = OpVariable %_ptr_UniformConstant_41 UniformConstant
OpNoLine
%43 = OpTypeSampledImage %36
OpNoLine
%_ptr_Function_v2float = OpTypePointer Function %v2float
OpNoLine
%_ptr_Function_v4float = OpTypePointer Function %v4float
OpNoLine
%_ptr_Input_v2float = OpTypePointer Input %v2float
OpNoLine
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
OpNoLine
%_ptr_Output_v4float = OpTypePointer Output %v4float
OpNoLine
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
OpNoLine
%MainPs = OpFunction %void None %19
OpNoLine
%48 = OpLabel
OpNoLine
%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
OpNoLine
%param = OpVariable %_ptr_Function_PS_INPUT Function
OpLine %5 23 0
%49 = OpLoad %v2float %i_vTextureCoords
OpLine %5 23 0
%50 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
OpLine %5 23 0
OpStore %50 %49
OpLine %5 23 0
%51 = OpLoad %PS_INPUT %i_0
OpLine %5 23 0
OpStore %param %51
OpLine %5 23 0
%52 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
OpLine %5 23 0
%53 = OpCompositeExtract %v4float %52 0
OpLine %5 23 0
OpStore %_entryPointOutput_vColor %53
OpLine %5 23 0
OpReturn
OpNoLine
OpFunctionEnd
OpNoLine
%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %24
OpNoLine
%i = OpFunctionParameter %_ptr_Function_PS_INPUT
OpNoLine
%54 = OpLabel
OpNoLine
%u = OpVariable %_ptr_Function_uint Function
OpNoLine
%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
OpLine %5 27 0
%55 = OpAccessChain %_ptr_PushConstant_uint %_ %int_2
OpLine %5 27 0
%56 = OpLoad %uint %55
OpLine %5 27 0
%57 = OpINotEqual %bool %56 %uint_0
OpLine %5 27 0
OpSelectionMerge %58 None
OpBranchConditional %57 %59 %60
OpNoLine
%59 = OpLabel
OpLine %5 28 0
%61 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
OpLine %5 28 0
%62 = OpLoad %uint %61
OpLine %5 28 0
OpStore %u %62
OpLine %5 28 0
OpBranch %58
OpNoLine
%60 = OpLabel
OpLine %5 30 0
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
OpLine %5 30 0
%64 = OpLoad %uint %63
OpLine %5 30 0
OpStore %u %64
OpLine %5 30 0
OpBranch %58
OpNoLine
%58 = OpLabel
OpLine %5 31 0
%65 = OpLoad %uint %u
OpLine %5 31 0
%66 = OpAccessChain %_ptr_UniformConstant_36 %g_tColor %65
OpLine %5 31 0
%67 = OpLoad %36 %66
OpLine %5 31 0
%68 = OpLoad %41 %g_sAniso
OpLine %5 31 0
%69 = OpSampledImage %43 %67 %68
OpLine %5 31 0
%70 = OpAccessChain %_ptr_Function_v2float %i %int_0
OpLine %5 31 0
%71 = OpLoad %v2float %70
OpLine %5 31 0
%72 = OpImageSampleImplicitLod %v4float %69 %71
OpLine %5 31 0
%73 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
OpLine %5 31 0
OpStore %73 %72
OpLine %5 32 0
%74 = OpLoad %PS_OUTPUT %ps_output
OpLine %5 32 0
OpReturnValue %74
OpNoLine
OpFunctionEnd
)";
const std::string after =
R"(%void = OpTypeVoid
%19 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%PS_INPUT = OpTypeStruct %v2float
%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
%v4float = OpTypeVector %float 4
%PS_OUTPUT = OpTypeStruct %v4float
%24 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
%uint = OpTypeInt 32 0
%PerViewConstantBuffer_t = OpTypeStruct %uint %uint %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%int = OpTypeInt 32 1
%int_2 = OpConstant %int 2
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%_ptr_Function_uint = OpTypePointer Function %uint
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
%36 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint_128 = OpConstant %uint 128
%_arr_36_uint_128 = OpTypeArray %36 %uint_128
%_ptr_UniformConstant__arr_36_uint_128 = OpTypePointer UniformConstant %_arr_36_uint_128
%g_tColor = OpVariable %_ptr_UniformConstant__arr_36_uint_128 UniformConstant
%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
%41 = OpTypeSampler
%_ptr_UniformConstant_41 = OpTypePointer UniformConstant %41
%g_sAniso = OpVariable %_ptr_UniformConstant_41 UniformConstant
%43 = OpTypeSampledImage %36
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
%MainPs = OpFunction %void None %19
%48 = OpLabel
%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
%param = OpVariable %_ptr_Function_PS_INPUT Function
OpLine %5 23 0
%49 = OpLoad %v2float %i_vTextureCoords
%50 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
OpStore %50 %49
%51 = OpLoad %PS_INPUT %i_0
OpStore %param %51
%52 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
%53 = OpCompositeExtract %v4float %52 0
OpStore %_entryPointOutput_vColor %53
OpReturn
OpFunctionEnd
%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %24
%i = OpFunctionParameter %_ptr_Function_PS_INPUT
%54 = OpLabel
%u = OpVariable %_ptr_Function_uint Function
%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
OpLine %5 27 0
%55 = OpAccessChain %_ptr_PushConstant_uint %_ %int_2
%56 = OpLoad %uint %55
%57 = OpINotEqual %bool %56 %uint_0
OpSelectionMerge %58 None
OpBranchConditional %57 %59 %60
%59 = OpLabel
OpLine %5 28 0
%61 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%62 = OpLoad %uint %61
OpStore %u %62
OpBranch %58
%60 = OpLabel
OpLine %5 30 0
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
%64 = OpLoad %uint %63
OpStore %u %64
OpBranch %58
%58 = OpLabel
OpLine %5 31 0
%65 = OpLoad %uint %u
%66 = OpAccessChain %_ptr_UniformConstant_36 %g_tColor %65
%67 = OpLoad %36 %66
%68 = OpLoad %41 %g_sAniso
%69 = OpSampledImage %43 %67 %68
%70 = OpAccessChain %_ptr_Function_v2float %i %int_0
%71 = OpLoad %v2float %70
%72 = OpImageSampleImplicitLod %v4float %69 %71
%73 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
OpStore %73 %72
OpLine %5 32 0
%74 = OpLoad %PS_OUTPUT %ps_output
OpReturnValue %74
OpFunctionEnd
)";
SinglePassRunAndCheck<ProcessLinesPass>(
predefs + before, predefs + after, false, true, kLinesEliminateDeadLines);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// TODO(greg-lunarg): Think about other tests :)
} // namespace
} // namespace opt
} // namespace spvtools