Extend reducer to remove relaxed precision decorations (#2797)

Adds a reduction pass that removes OpDecorate and OpMemberDecorate
instructions that annotate instructions and members with
RelaxedPrecision.  As well as being useful in its own right, removing
such references allows other passes to remove further instructions.
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index def4d21..7651e86 100644
--- a/source/reduce/CMakeLists.txt
+++ b/source/reduce/CMakeLists.txt
@@ -30,6 +30,7 @@
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity_finder.h
         remove_opname_instruction_reduction_opportunity_finder.h
+        remove_relaxed_precision_decoration_opportunity_finder.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity_finder.h
         remove_unreferenced_instruction_reduction_opportunity_finder.h
@@ -56,6 +57,7 @@
         remove_function_reduction_opportunity.cpp
         remove_function_reduction_opportunity_finder.cpp
         remove_instruction_reduction_opportunity.cpp
+        remove_relaxed_precision_decoration_opportunity_finder.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity_finder.cpp
         remove_unreferenced_instruction_reduction_opportunity_finder.cpp
diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp
index a677be3..ebb5d47 100644
--- a/source/reduce/reducer.cpp
+++ b/source/reduce/reducer.cpp
@@ -25,6 +25,7 @@
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
 #include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@@ -175,6 +176,8 @@
 void Reducer::AddDefaultReductionPasses() {
   AddReductionPass(spvtools::MakeUnique<
                    RemoveOpNameInstructionReductionOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   RemoveRelaxedPrecisionDecorationOpportunityFinder>());
   AddReductionPass(
       spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
   AddReductionPass(
diff --git a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
new file mode 100644
index 0000000..352cefb
--- /dev/null
+++ b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
@@ -0,0 +1,49 @@
+// Copyright (c) 2018 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 "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
+
+#include "source/reduce/remove_instruction_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+std::vector<std::unique_ptr<ReductionOpportunity>>
+RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
+    opt::IRContext* context) const {
+  std::vector<std::unique_ptr<ReductionOpportunity>> result;
+
+  // Consider all annotation instructions
+  for (auto& inst : context->module()->annotations()) {
+    // We are interested in removing instructions of the form:
+    //   SpvOpDecorate %id RelaxedPrecision
+    // and
+    //   SpvOpMemberDecorate %id member RelaxedPrecision
+    if ((inst.opcode() == SpvOpDecorate &&
+         inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
+        (inst.opcode() == SpvOpMemberDecorate &&
+         inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
+      result.push_back(
+          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+    }
+  }
+  return result;
+}
+
+std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
+  return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
+}
+
+}  // namespace reduce
+}  // namespace spvtools
diff --git a/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
new file mode 100644
index 0000000..673049c
--- /dev/null
+++ b/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2019 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 SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to remove relaxed precision decorations.
+class RemoveRelaxedPrecisionDecorationOpportunityFinder
+    : public ReductionOpportunityFinder {
+ public:
+  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+      opt::IRContext* context) const override;
+
+  std::string GetName() const override;
+};
+
+}  // namespace reduce
+}  // namespace spvtools
+
+#endif  // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
diff --git a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
index 8f32435..dabee50 100644
--- a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
@@ -21,11 +21,9 @@
 namespace spvtools {
 namespace reduce {
 
-using opt::IRContext;
-
 std::vector<std::unique_ptr<ReductionOpportunity>>
 RemoveUnreferencedInstructionReductionOpportunityFinder::
-    GetAvailableOpportunities(IRContext* context) const {
+    GetAvailableOpportunities(opt::IRContext* context) const {
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
 
   for (auto& function : *context->module()) {
diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt
index 964abdd..2d3b378 100644
--- a/test/reduce/CMakeLists.txt
+++ b/test/reduce/CMakeLists.txt
@@ -24,6 +24,7 @@
         remove_block_test.cpp
         remove_function_test.cpp
         remove_opname_instruction_test.cpp
+        remove_relaxed_precision_decoration_test.cpp
         remove_selection_test.cpp
         remove_unreferenced_instruction_test.cpp
         structured_loop_to_selection_test.cpp
diff --git a/test/reduce/remove_relaxed_precision_decoration_test.cpp b/test/reduce/remove_relaxed_precision_decoration_test.cpp
new file mode 100644
index 0000000..f9ff081
--- /dev/null
+++ b/test/reduce/remove_relaxed_precision_decoration_test.cpp
@@ -0,0 +1,177 @@
+// Copyright (c) 2019 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 "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
+
+#include "source/opt/build_module.h"
+#include "source/reduce/reduction_opportunity.h"
+#include "source/reduce/reduction_pass.h"
+#include "test/reduce/reduce_test_util.h"
+
+namespace spvtools {
+namespace reduce {
+namespace {
+
+TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
+  const std::string source = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, source, kReduceAssembleOption);
+  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(0, ops.size());
+}
+
+TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
+  const std::string source = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "i"
+               OpName %16 "v"
+               OpName %19 "S"
+               OpMemberName %19 0 "a"
+               OpMemberName %19 1 "b"
+               OpMemberName %19 2 "c"
+               OpName %21 "s"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+               OpDecorate %17 RelaxedPrecision
+               OpDecorate %18 RelaxedPrecision
+               OpMemberDecorate %19 0 RelaxedPrecision
+               OpMemberDecorate %19 1 RelaxedPrecision
+               OpMemberDecorate %19 2 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %23 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 22
+         %14 = OpTypeVector %6 2
+         %15 = OpTypePointer Function %14
+         %19 = OpTypeStruct %10 %6 %14
+         %20 = OpTypePointer Function %19
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %21 = OpVariable %20 Function
+               OpStore %8 %9
+               OpStore %12 %13
+         %17 = OpLoad %6 %8
+         %18 = OpCompositeConstruct %14 %17 %17
+               OpStore %16 %18
+         %22 = OpLoad %10 %12
+         %23 = OpLoad %6 %8
+         %24 = OpLoad %14 %16
+         %25 = OpCompositeConstruct %19 %22 %23 %24
+               OpStore %21 %25
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, source, kReduceAssembleOption);
+  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(11, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+  }
+
+  const std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "i"
+               OpName %16 "v"
+               OpName %19 "S"
+               OpMemberName %19 0 "a"
+               OpMemberName %19 1 "b"
+               OpMemberName %19 2 "c"
+               OpName %21 "s"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 22
+         %14 = OpTypeVector %6 2
+         %15 = OpTypePointer Function %14
+         %19 = OpTypeStruct %10 %6 %14
+         %20 = OpTypePointer Function %19
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %21 = OpVariable %20 Function
+               OpStore %8 %9
+               OpStore %12 %13
+         %17 = OpLoad %6 %8
+         %18 = OpCompositeConstruct %14 %17 %17
+               OpStore %16 %18
+         %22 = OpLoad %10 %12
+         %23 = OpLoad %6 %8
+         %24 = OpLoad %14 %16
+         %25 = OpCompositeConstruct %19 %22 %23 %24
+               OpStore %21 %25
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected, context.get());
+}
+
+}  // namespace
+}  // namespace reduce
+}  // namespace spvtools