Add pass to reaplce invalid opcodes

Creates a pass that will remove instructions that are invalid for the
current shader stage.  For the instruction to be considered for replacement

1) The opcode must be valid for a shader modules.
2) The opcode must be invalid for the current shader stage.
3) All entry points to the module must be for the same shader stage.
4) The function containing the instruction must be reachable from an entry point.

Fixes #1247.
This commit is contained in:
Steven Perron 2018-01-30 11:24:03 -05:00
parent d37869c842
commit 61d8c0384b
11 changed files with 889 additions and 0 deletions

View File

@ -106,6 +106,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/propagator.cpp \
source/opt/redundancy_elimination.cpp \
source/opt/remove_duplicates_pass.cpp \
source/opt/replace_invalid_opc.cpp \
source/opt/scalar_replacement_pass.cpp \
source/opt/set_spec_constant_default_value_pass.cpp \
source/opt/strength_reduction_pass.cpp \

View File

@ -500,6 +500,10 @@ Optimizer::PassToken CreateWorkaround1209Pass();
// Creates a pass that converts if-then-else like assignments into OpSelect.
Optimizer::PassToken CreateIfConversionPass();
// Creates a pass that will replace instructions that are not valid for the
// current shader stage by constants. Has no effect on non-shader modules.
Optimizer::PassToken CreateReplaceInvalidOpcodePass();
} // namespace spvtools
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_

View File

@ -65,6 +65,7 @@ add_library(SPIRV-Tools-opt
redundancy_elimination.h
reflect.h
remove_duplicates_pass.h
replace_invalid_opc.h
scalar_replacement_pass.h
set_spec_constant_default_value_pass.h
strength_reduction_pass.h
@ -127,6 +128,7 @@ add_library(SPIRV-Tools-opt
propagator.cpp
redundancy_elimination.cpp
remove_duplicates_pass.cpp
replace_invalid_opc.cpp
scalar_replacement_pass.cpp
set_spec_constant_default_value_pass.cpp
strength_reduction_pass.cpp

View File

@ -375,4 +375,8 @@ Optimizer::PassToken CreateIfConversionPass() {
MakeUnique<opt::IfConversion>());
}
Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::ReplaceInvalidOpcodePass>());
}
} // namespace spvtools

View File

@ -45,6 +45,7 @@
#include "private_to_local_pass.h"
#include "redundancy_elimination.h"
#include "remove_duplicates_pass.h"
#include "replace_invalid_opc.h"
#include "scalar_replacement_pass.h"
#include "set_spec_constant_default_value_pass.h"
#include "strength_reduction_pass.h"

View File

@ -0,0 +1,207 @@
// 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 "replace_invalid_opc.h"
#include <bitset>
namespace spvtools {
namespace opt {
Pass::Status ReplaceInvalidOpcodePass::Process(ir::IRContext* c) {
InitializeProcessing(c);
bool modified = false;
if (context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage)) {
return Status::SuccessWithoutChange;
}
SpvExecutionModel execution_model = GetExecutionModel();
if (execution_model == SpvExecutionModelKernel) {
// We do not handle kernels.
return Status::SuccessWithoutChange;
}
if (execution_model == SpvExecutionModelMax) {
// Mixed execution models for the entry points. This case is not currently
// handled.
return Status::SuccessWithoutChange;
}
for (ir::Function& func : *get_module()) {
modified |= RewriteFunction(&func, execution_model);
}
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
SpvExecutionModel ReplaceInvalidOpcodePass::GetExecutionModel() {
SpvExecutionModel result = SpvExecutionModelMax;
bool first = true;
for (ir::Instruction& entry_point : get_module()->entry_points()) {
if (first) {
result =
static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
first = false;
} else {
SpvExecutionModel current_model =
static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
if (current_model != result) {
result = SpvExecutionModelMax;
break;
}
}
}
return result;
}
bool ReplaceInvalidOpcodePass::RewriteFunction(ir::Function* function,
SpvExecutionModel model) {
bool modified = false;
ir::Instruction* last_line_dbg_inst = nullptr;
function->ForEachInst(
[model, &modified, &last_line_dbg_inst, this](ir::Instruction* inst) {
// Track the debug information so we can have a meaningful message.
if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) {
last_line_dbg_inst = nullptr;
return;
} else if (inst->opcode() == SpvOpLine) {
last_line_dbg_inst = inst;
return;
}
bool replace = false;
if (model != SpvExecutionModelFragment &&
IsFragmentShaderOnlyInstruction(inst)) {
replace = true;
}
if (model != SpvExecutionModelTessellationControl &&
model != SpvExecutionModelGLCompute) {
if (inst->opcode() == SpvOpControlBarrier) {
assert(model != SpvExecutionModelKernel &&
"Expecting to be working on a shader module.");
replace = true;
}
}
if (replace) {
modified = true;
if (last_line_dbg_inst == nullptr) {
ReplaceInstruction(inst, nullptr, 0, 0);
} else {
// Get the name of the source file.
ir::Instruction* file_name = context()->get_def_use_mgr()->GetDef(
last_line_dbg_inst->GetSingleWordInOperand(0));
const char* source = reinterpret_cast<const char*>(
&file_name->GetInOperand(0).words[0]);
// Get the line number and column number.
uint32_t line_number =
last_line_dbg_inst->GetSingleWordInOperand(1);
uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2);
// Replace the instruction.
ReplaceInstruction(inst, source, line_number, col_number);
}
}
},
/* run_on_debug_line_insts = */ true);
return modified;
}
bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction(
ir::Instruction* inst) {
switch (inst->opcode()) {
case SpvOpDPdx:
case SpvOpDPdy:
case SpvOpFwidth:
case SpvOpDPdxFine:
case SpvOpDPdyFine:
case SpvOpFwidthFine:
case SpvOpDPdxCoarse:
case SpvOpDPdyCoarse:
case SpvOpFwidthCoarse:
case SpvOpImageSampleImplicitLod:
case SpvOpImageSampleDrefImplicitLod:
case SpvOpImageSampleProjImplicitLod:
case SpvOpImageSampleProjDrefImplicitLod:
case SpvOpImageSparseSampleImplicitLod:
case SpvOpImageSparseSampleDrefImplicitLod:
case SpvOpImageQueryLod:
// TODO: Teach |ReplaceInstruction| to handle block terminators. Then
// uncomment the OpKill case.
// case SpvOpKill:
return true;
default:
return false;
}
}
void ReplaceInvalidOpcodePass::ReplaceInstruction(ir::Instruction* inst,
const char* source,
uint32_t line_number,
uint32_t column_number) {
if (inst->result_id() != 0) {
uint32_t const_id = GetSpecialConstant(inst->type_id());
context()->KillNamesAndDecorates(inst);
context()->ReplaceAllUsesWith(inst->result_id(), const_id);
}
assert(!inst->IsBlockTerminator() &&
"We cannot simply delete a block terminator. It must be replaced "
"with something.");
if (consumer()) {
std::string message = BuildWarningMessage(inst->opcode());
consumer()(SPV_MSG_WARNING, source, {line_number, column_number, 0},
message.c_str());
}
context()->KillInst(inst);
}
uint32_t ReplaceInvalidOpcodePass::GetSpecialConstant(uint32_t type_id) {
const analysis::Constant* special_const = nullptr;
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
ir::Instruction* type = context()->get_def_use_mgr()->GetDef(type_id);
if (type->opcode() == SpvOpTypeVector) {
uint32_t component_const =
GetSpecialConstant(type->GetSingleWordInOperand(0));
std::vector<uint32_t> ids;
for (uint32_t i = 0; i < type->GetSingleWordInOperand(1); ++i) {
ids.push_back(component_const);
}
special_const = const_mgr->GetConstant(type_mgr->GetType(type_id), ids);
} else {
assert(type->opcode() == SpvOpTypeInt || type->opcode() == SpvOpTypeFloat);
std::vector<uint32_t> literal_words;
for (uint32_t i = 0; i < type->GetSingleWordInOperand(0); i += 32) {
literal_words.push_back(0xDEADBEEF);
}
special_const =
const_mgr->GetConstant(type_mgr->GetType(type_id), literal_words);
}
assert(special_const != nullptr);
return const_mgr->GetDefiningInstruction(special_const)->result_id();
}
std::string ReplaceInvalidOpcodePass::BuildWarningMessage(SpvOp opcode) {
spv_opcode_desc opcode_info;
context()->grammar().lookupOpcode(opcode, &opcode_info);
std::string message = "Removing ";
message += opcode_info->name;
message += " instruction because of incompatible execution model.";
return message;
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,65 @@
// 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_REPLACE_INVALID_OPC_H_
#define LIBSPIRV_OPT_REPLACE_INVALID_OPC_H_
#include "pass.h"
namespace spvtools {
namespace opt {
// This pass will runs on shader modules only. It will replace the result of
// instructions that are valid for shader modules, but not the current shader
// stage, with a constant value. If the instruction does not have a return
// value, the instruction will simply be deleted.
class ReplaceInvalidOpcodePass : public Pass {
public:
const char* name() const override { return "replace-invalid-opcodes"; }
Status Process(ir::IRContext*) override;
private:
// Returns the execution model that is used by every entry point in the
// module. If more than one execution model is used in the module, then the
// return value is SpvExecutionModelMax.
SpvExecutionModel GetExecutionModel();
// Replaces all instructions in |function| that are invalid with execution
// model |mode|, but valid for another shader model, with a special constant
// value. See |GetSpecialConstant|.
bool RewriteFunction(ir::Function* function, SpvExecutionModel mode);
// Returns true if |inst| is valid for fragment shaders only.
bool IsFragmentShaderOnlyInstruction(ir::Instruction* inst);
// Replaces all uses of the result of |inst|, if there is one, with the id of
// a special constant. Then |inst| is killed. |inst| cannot be a block
// terminator because the basic block will then become invalid. |inst| is no
// longer valid after calling this function.
void ReplaceInstruction(ir::Instruction* inst, const char* source,
uint32_t line_number, uint32_t column_number);
// Returns the id of a constant with type |type_id|. The type must be an
// integer, float, or vector. For scalar types, the hex representation of the
// constant will be the concatenation of 0xDEADBEEF with itself until the
// width of the type has been reached. For a vector, each element of the
// constant will be constructed the same way.
uint32_t GetSpecialConstant(uint32_t type_id);
std::string BuildWarningMessage(SpvOp opcode);
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_REPLACE_INVALID_OPC_H_

View File

@ -286,3 +286,8 @@ add_spvtools_unittest(TARGET instruction_folding
SRCS fold_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET replace_invalid_opc
SRCS replace_invalid_opc_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
)

View File

@ -226,6 +226,10 @@ class PassTest : public TestT {
MessageConsumer consumer() { return consumer_; }
ir::IRContext* context() { return context_.get(); }
void SetMessageConsumer(MessageConsumer msg_consumer) {
consumer_ = msg_consumer;
}
private:
MessageConsumer consumer_; // Message consumer.
std::unique_ptr<ir::IRContext> context_; // IR context

View File

@ -0,0 +1,590 @@
// Copyright (c) 2017 Google 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 "assembly_builder.h"
#include "gmock/gmock.h"
#include "pass_fixture.h"
#include <cstdarg>
namespace {
using namespace spvtools;
using ReplaceInvalidOpcodeTest = PassTest<::testing::Test>;
#ifdef SPIRV_EFFCEE
TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstruction) {
const std::string text = R"(
; CHECK: [[special_const:%\w+]] = OpConstant %float -6.25985e+18
; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]]
; CHECK-NOT: OpImageSampleImplicitLod
; CHECK: OpStore [[:%\w+]] [[constant]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpLoad %12 %25
%28 = OpLoad %10 %24
%29 = OpSampledImage %14 %28 %27
%30 = OpImageSampleImplicitLod %v4float %29 %23
%31 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %31 %30
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::ReplaceInvalidOpcodePass>(text, false);
}
TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstructionInNonEntryPoint) {
const std::string text = R"(
; CHECK: [[special_const:%\w+]] = OpConstant %float -6.25985e+18
; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]]
; CHECK-NOT: OpImageSampleImplicitLod
; CHECK: OpStore [[:%\w+]] [[constant]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpFunctionCall %void %28
OpReturn
OpFunctionEnd
%28 = OpFunction %void None %8
%29 = OpLabel
%30 = OpLoad %12 %25
%31 = OpLoad %10 %24
%32 = OpSampledImage %14 %31 %30
%33 = OpImageSampleImplicitLod %v4float %32 %23
%34 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %34 %33
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::ReplaceInvalidOpcodePass>(text, false);
}
TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstructionMultipleEntryPoints) {
const std::string text = R"(
; CHECK: [[special_const:%\w+]] = OpConstant %float -6.25985e+18
; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]]
; CHECK-NOT: OpImageSampleImplicitLod
; CHECK: OpStore [[:%\w+]] [[constant]]
; CHECK-NOT: OpImageSampleImplicitLod
; CHECK: OpStore [[:%\w+]] [[constant]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpEntryPoint Vertex %main2 "main2" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpName %main2 "main2"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpLoad %12 %25
%28 = OpLoad %10 %24
%29 = OpSampledImage %14 %28 %27
%30 = OpImageSampleImplicitLod %v4float %29 %23
%31 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %31 %30
OpReturn
OpFunctionEnd
%main2 = OpFunction %void None %8
%46 = OpLabel
%47 = OpLoad %12 %25
%48 = OpLoad %10 %24
%49 = OpSampledImage %14 %48 %47
%50 = OpImageSampleImplicitLod %v4float %49 %23
%51 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %51 %50
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::ReplaceInvalidOpcodePass>(text, false);
}
TEST_F(ReplaceInvalidOpcodeTest, DontReplaceInstruction) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpLoad %12 %25
%28 = OpLoad %10 %24
%29 = OpSampledImage %14 %28 %27
%30 = OpImageSampleImplicitLod %v4float %29 %23
%31 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %31 %30
OpReturn
OpFunctionEnd)";
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(ReplaceInvalidOpcodeTest, MultipleEntryPointsDifferentStage) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpEntryPoint Fragment %main2 "main2" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpName %main2 "main2"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpLoad %12 %25
%28 = OpLoad %10 %24
%29 = OpSampledImage %14 %28 %27
%30 = OpImageSampleImplicitLod %v4float %29 %23
%31 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %31 %30
OpReturn
OpFunctionEnd
%main2 = OpFunction %void None %8
%46 = OpLabel
%47 = OpLoad %12 %25
%48 = OpLoad %10 %24
%49 = OpSampledImage %14 %48 %47
%50 = OpImageSampleImplicitLod %v4float %49 %23
%51 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %51 %50
OpReturn
OpFunctionEnd)";
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(ReplaceInvalidOpcodeTest, DontReplaceLinkage) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_6 0 BuiltIn Position
OpDecorate %_struct_6 Block
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
%10 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%12 = OpTypeSampler
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%14 = OpTypeSampledImage %10
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_6 = OpTypeStruct %v4float
%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6
%5 = OpVariable %_ptr_Output__struct_6 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%23 = OpConstantComposite %v2float %float_0 %float_0
%24 = OpVariable %_ptr_UniformConstant_10 UniformConstant
%25 = OpVariable %_ptr_UniformConstant_12 UniformConstant
%main = OpFunction %void None %8
%26 = OpLabel
%27 = OpLoad %12 %25
%28 = OpLoad %10 %24
%29 = OpSampledImage %14 %28 %27
%30 = OpImageSampleImplicitLod %v4float %29 %23
%31 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %31 %30
OpReturn
OpFunctionEnd)";
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(ReplaceInvalidOpcodeTest, BarrierDontReplace) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_264 = OpConstant %uint 264
%main = OpFunction %void None %3
%5 = OpLabel
OpControlBarrier %uint_2 %uint_2 %uint_264
OpReturn
OpFunctionEnd)";
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(ReplaceInvalidOpcodeTest, BarrierReplace) {
const std::string text = R"(
; CHECK-NOT: OpControlBarrier
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_264 = OpConstant %uint 264
%main = OpFunction %void None %3
%5 = OpLabel
OpControlBarrier %uint_2 %uint_2 %uint_264
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::ReplaceInvalidOpcodePass>(text, false);
}
struct Message {
spv_message_level_t level;
const char* source_file;
uint32_t line_number;
uint32_t column_number;
const char* message;
};
MessageConsumer GetTestMessageConsumer(
std::vector<Message>& expected_messages) {
return [&expected_messages](spv_message_level_t level, const char* source,
const spv_position_t& position,
const char* message) {
EXPECT_TRUE(!expected_messages.empty());
if (expected_messages.empty()) {
return;
}
EXPECT_EQ(expected_messages[0].level, level);
EXPECT_EQ(expected_messages[0].line_number, position.line);
EXPECT_EQ(expected_messages[0].column_number, position.column);
EXPECT_STREQ(expected_messages[0].source_file, source);
EXPECT_STREQ(expected_messages[0].message, message);
expected_messages.erase(expected_messages.begin());
};
}
TEST_F(ReplaceInvalidOpcodeTest, MessageTest) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
%6 = OpString "test.hlsl"
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_7 0 BuiltIn Position
OpDecorate %_struct_7 Block
%void = OpTypeVoid
%9 = OpTypeFunction %void
%float = OpTypeFloat 32
%11 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%13 = OpTypeSampler
%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
%15 = OpTypeSampledImage %11
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_7 = OpTypeStruct %v4float
%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7
%5 = OpVariable %_ptr_Output__struct_7 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%24 = OpConstantComposite %v2float %float_0 %float_0
%25 = OpVariable %_ptr_UniformConstant_11 UniformConstant
%26 = OpVariable %_ptr_UniformConstant_13 UniformConstant
%main = OpFunction %void None %9
%27 = OpLabel
OpLine %6 2 4
%28 = OpLoad %13 %26
%29 = OpLoad %11 %25
%30 = OpSampledImage %15 %29 %28
%31 = OpImageSampleImplicitLod %v4float %30 %24
%32 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %32 %31
OpReturn
OpFunctionEnd)";
std::vector<Message> messages = {
{SPV_MSG_WARNING, "test.hlsl", 2, 4,
"Removing ImageSampleImplicitLod instruction because of incompatible "
"execution model."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithChange, std::get<1>(result));
}
TEST_F(ReplaceInvalidOpcodeTest, MultipleMessageTest) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5
OpSource GLSL 400
%6 = OpString "test.hlsl"
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpDecorate %3 Location 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %_struct_7 0 BuiltIn Position
OpDecorate %_struct_7 Block
%void = OpTypeVoid
%9 = OpTypeFunction %void
%float = OpTypeFloat 32
%11 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%13 = OpTypeSampler
%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
%15 = OpTypeSampledImage %11
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%3 = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_struct_7 = OpTypeStruct %v4float
%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7
%5 = OpVariable %_ptr_Output__struct_7 Output
%int_0 = OpConstant %int 0
%float_0 = OpConstant %float 0
%24 = OpConstantComposite %v2float %float_0 %float_0
%25 = OpVariable %_ptr_UniformConstant_11 UniformConstant
%26 = OpVariable %_ptr_UniformConstant_13 UniformConstant
%main = OpFunction %void None %9
%27 = OpLabel
OpLine %6 2 4
%28 = OpLoad %13 %26
%29 = OpLoad %11 %25
%30 = OpSampledImage %15 %29 %28
%31 = OpImageSampleImplicitLod %v4float %30 %24
OpLine %6 12 4
%41 = OpImageSampleProjImplicitLod %v4float %30 %24
%32 = OpAccessChain %_ptr_Output_v4float %5 %int_0
OpStore %32 %31
OpReturn
OpFunctionEnd)";
std::vector<Message> messages = {
{SPV_MSG_WARNING, "test.hlsl", 2, 4,
"Removing ImageSampleImplicitLod instruction because of incompatible "
"execution model."},
{SPV_MSG_WARNING, "test.hlsl", 12, 4,
"Removing ImageSampleProjImplicitLod instruction because of "
"incompatible "
"execution model."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunAndDisassemble<opt::ReplaceInvalidOpcodePass>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithChange, std::get<1>(result));
}
#endif
} // anonymous namespace

View File

@ -240,6 +240,10 @@ Options (in lexicographical order):
Allow store from one struct type to a different type with
compatible layout and members. This option is forwarded to the
validator.
--replace-invalid-opcode
Replaces instructions whose opcode is valid for shader modules,
but not for the current shader stage. To have an effect, all
entry points must have the same execution model.
--scalar-replacement
Replace aggregate function scope variables that are only accessed
via their elements with new function variables representing each
@ -459,6 +463,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
optimizer->RegisterPass(CreateWorkaround1209Pass());
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options->relax_struct_store = true;
} else if (0 == strcmp(cur_arg, "--replace-invalid-opcode")) {
optimizer->RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (0 == strcmp(cur_arg, "--skip-validation")) {
*skip_validator = true;
} else if (0 == strcmp(cur_arg, "-O")) {