--- /dev/null
+// 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
--- /dev/null
+// 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