Add pass to reaplce invalid opcodes
authorSteven Perron <stevenperron@google.com>
Tue, 30 Jan 2018 16:24:03 +0000 (11:24 -0500)
committerSteven Perron <stevenperron@google.com>
Thu, 1 Feb 2018 20:25:09 +0000 (15:25 -0500)
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.

Android.mk
include/spirv-tools/optimizer.hpp
source/opt/CMakeLists.txt
source/opt/optimizer.cpp
source/opt/passes.h
source/opt/replace_invalid_opc.cpp [new file with mode: 0644]
source/opt/replace_invalid_opc.h [new file with mode: 0644]
test/opt/CMakeLists.txt
test/opt/pass_fixture.h
test/opt/replace_invalid_opc_test.cpp [new file with mode: 0644]
tools/opt/opt.cpp

index b4d56ce..6498569 100644 (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 \
index a8d8da0..b866bc6 100644 (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_
index 321d3f8..bb2311a 100644 (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
index f79657a..bac6891 100644 (file)
@@ -375,4 +375,8 @@ Optimizer::PassToken CreateIfConversionPass() {
       MakeUnique<opt::IfConversion>());
 }
 
+Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::ReplaceInvalidOpcodePass>());
+}
 }  // namespace spvtools
index a2d4f09..96d0568 100644 (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"
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
new file mode 100644 (file)
index 0000000..a025c3c
--- /dev/null
@@ -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
diff --git a/source/opt/replace_invalid_opc.h b/source/opt/replace_invalid_opc.h
new file mode 100644 (file)
index 0000000..e661fce
--- /dev/null
@@ -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_
index 0f53897..63266bf 100644 (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
+)
index 33010ec..d935231 100644 (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
diff --git a/test/opt/replace_invalid_opc_test.cpp b/test/opt/replace_invalid_opc_test.cpp
new file mode 100644 (file)
index 0000000..447af1a
--- /dev/null
@@ -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
index e072084..709e517 100644 (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")) {