Add folding of redundant OpSelect insns
authorArseny Kapoulkine <arseny.kapoulkine@gmail.com>
Thu, 15 Feb 2018 04:45:36 +0000 (20:45 -0800)
committerSteven Perron <31666470+s-perron@users.noreply.github.com>
Thu, 15 Feb 2018 15:03:22 +0000 (10:03 -0500)
We can fold OpSelect into one of the operands in two cases:

- condition is constant
- both results are the same

Even if the original shader doesn't have either of these, if-conversion
pass sometimes ends up generating instructions like

   %7127 = OpSelect %int %3220 %7058 %7058

And this optimization cleans them up.

source/opt/folding_rules.cpp
test/opt/fold_test.cpp

index 4762e75..10208bd 100644 (file)
@@ -294,6 +294,38 @@ FoldingRule RedundantPhi() {
         return true;
       };
 }
+
+FoldingRule RedundantSelect() {
+  // An OpSelect instruction where both values are the same or the condition is
+  // constant can be replaced by one of the values
+  return [](ir::Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == SpvOpSelect &&
+           "Wrong opcode.  Should be OpSelect.");
+    assert(inst->NumInOperands() == 3);
+    assert(constants.size() == 3);
+
+    const analysis::BoolConstant* bc =
+        constants[0] ? constants[0]->AsBoolConstant() : nullptr;
+    uint32_t true_id = inst->GetSingleWordInOperand(1);
+    uint32_t false_id = inst->GetSingleWordInOperand(2);
+
+    if (bc) {
+      // Select condition is constant, result is known
+      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {bc->value() ? true_id : false_id}}});
+      return true;
+    } else if (true_id == false_id) {
+      // Both results are the same, condition doesn't matter
+      inst->SetOpcode(SpvOpCopyObject);
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}});
+      return true;
+    } else {
+      return false;
+    }
+  };
+}
 }  // namespace
 
 spvtools::opt::FoldingRules::FoldingRules() {
@@ -310,6 +342,8 @@ spvtools::opt::FoldingRules::FoldingRules() {
   rules_[SpvOpIMul].push_back(IntMultipleBy1());
 
   rules_[SpvOpPhi].push_back(RedundantPhi());
+
+  rules_[SpvOpSelect].push_back(RedundantSelect());
 }
 }  // namespace opt
 }  // namespace spvtools
index 0cc1e3a..36729c8 100644 (file)
@@ -1549,5 +1549,39 @@ INSTANTIATE_TEST_CASE_P(PhiFoldingTest, GeneralInstructionFoldingTest,
           "OpFunctionEnd",
       2, 0)
 ));
+
+INSTANTIATE_TEST_CASE_P(SelectFoldingTest, GeneralInstructionFoldingTest,
+::testing::Values(
+  // Test case 0: Fold select with the same values for both sides
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%n = OpVariable %_ptr_bool Function\n" +
+          "%load = OpLoad %bool %n\n" +
+          "%2 = OpSelect %int %load %100 %100\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, INT_0_ID),
+  // Test case 1: Fold select true to left side
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%n = OpVariable %_ptr_int Function\n" +
+          "%load = OpLoad %bool %n\n" +
+          "%2 = OpSelect %int %true %100 %n\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, INT_0_ID),
+  // Test case 2: Fold select false to right side
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%n = OpVariable %_ptr_int Function\n" +
+          "%load = OpLoad %bool %n\n" +
+          "%2 = OpSelect %int %false %n %100\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, INT_0_ID)
+));
 // clang-format off
 }  // anonymous namespace