Use id_map in Fold*ToConstant
authorSteven Perron <stevenperron@google.com>
Mon, 22 Jan 2018 19:48:43 +0000 (14:48 -0500)
committerSteven Perron <stevenperron@google.com>
Mon, 22 Jan 2018 21:27:31 +0000 (16:27 -0500)
The folding routines are suppose to use the id_map provided to map the
ids in the instruction.  The ones I just added are missing it.

source/opt/fold.cpp
test/opt/fold_test.cpp

index 7492f8e..fa2e73b 100644 (file)
@@ -211,10 +211,13 @@ uint32_t FoldScalars(SpvOp opcode,
 
 // Returns true if |inst| is a binary operation that takes two integers as
 // parameters and folds to a constant that can be represented as an unsigned
-// 32-bit value.  If |inst| can be folded, the resulting value is returned
-// in |*result|.  Valid result types for the instruction are any integer (signed
-// or unsigned) with 32-bits or less, or a boolean value.
-bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
+// 32-bit value when the ids have been replaced by |id_map|.  If |inst| can be
+// folded, the resulting value is returned in |*result|.  Valid result types for
+// the instruction are any integer (signed or unsigned) with 32-bits or less, or
+// a boolean value.
+bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst,
+                                   std::function<uint32_t(uint32_t)> id_map,
+                                   uint32_t* result) {
   SpvOp opcode = inst->opcode();
   ir::IRContext* context = inst->context();
   analysis::ConstantManager* const_manger = context->get_constant_mgr();
@@ -226,7 +229,7 @@ bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
     if (operand->type != SPV_OPERAND_TYPE_ID) {
       return false;
     }
-    ids[i] = operand->words[0];
+    ids[i] = id_map(operand->words[0]);
     const analysis::Constant* constant =
         const_manger->FindDeclaredConstant(ids[i]);
     constants[i] = (constant != nullptr ? constant->AsIntConstant() : nullptr);
@@ -397,9 +400,11 @@ bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
 }
 
 // Returns true if |inst| is a binary operation on two boolean values, and folds
-// to a constant boolean value.  If |inst| can be folded, the result value is
-// returned in |*result|.
-bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
+// to a constant boolean value when the ids have been replaced using |id_map|.
+// If |inst| can be folded, the result value is returned in |*result|.
+bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst,
+                                   std::function<uint32_t(uint32_t)> id_map,
+                                   uint32_t* result) {
   SpvOp opcode = inst->opcode();
   ir::IRContext* context = inst->context();
   analysis::ConstantManager* const_manger = context->get_constant_mgr();
@@ -411,7 +416,7 @@ bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
     if (operand->type != SPV_OPERAND_TYPE_ID) {
       return false;
     }
-    ids[i] = operand->words[0];
+    ids[i] = id_map(operand->words[0]);
     const analysis::Constant* constant =
         const_manger->FindDeclaredConstant(ids[i]);
     constants[i] = (constant != nullptr ? constant->AsBoolConstant() : nullptr);
@@ -446,16 +451,19 @@ bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
   return false;
 }
 
-// Returns true if |inst| can be folded to an constant.  If it can, the value
-// is returned in |result|.  If not, |result| is unchanged.  It is assumed that
-// not all operands are constant.  Those cases are handled by |FoldScalar|.
-bool FoldIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
+// Returns true if |inst| can be folded to an constant when the ids have been
+// substituted using id_map.  If it can, the value is returned in |result|.  If
+// not, |result| is unchanged.  It is assumed that not all operands are
+// constant.  Those cases are handled by |FoldScalar|.
+bool FoldIntegerOpToConstant(ir::Instruction* inst,
+                             std::function<uint32_t(uint32_t)> id_map,
+                             uint32_t* result) {
   assert(IsFoldableOpcode(inst->opcode()) &&
          "Unhandled instruction opcode in FoldScalars");
   switch (inst->NumInOperands()) {
     case 2:
-      return FoldBinaryIntegerOpToConstant(inst, result) ||
-             FoldBinaryBooleanOpToConstant(inst, result);
+      return FoldBinaryIntegerOpToConstant(inst, id_map, result) ||
+             FoldBinaryBooleanOpToConstant(inst, id_map, result);
     default:
       return false;
   }
@@ -589,7 +597,7 @@ ir::Instruction* FoldInstructionToConstant(
   }
 
   if (!successful) {
-    successful = FoldIntegerOpToConstant(inst, &result_val);
+    successful = FoldIntegerOpToConstant(inst, id_map, &result_val);
   }
 
   if (successful) {
index 67de442..0dbd540 100644 (file)
@@ -75,6 +75,8 @@ TEST_P(IntegerInstructionFoldingTest, Case) {
 }
 
 // Returns a common SPIR-V header for all of the test that follow.
+#define INT_0_ID 100
+#define TRUE_ID 101
 const std::string& Header() {
   static const std::string header = R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
@@ -87,6 +89,7 @@ OpName %main "main"
 %void_func = OpTypeFunction %void
 %bool = OpTypeBool
 %true = OpConstantTrue %bool
+%101 = OpConstantTrue %bool ; Need a def with an numerical id to define id maps.
 %false = OpConstantFalse %bool
 %short = OpTypeInt 16 1
 %int = OpTypeInt 32 1
@@ -98,6 +101,7 @@ OpName %main "main"
 %short_0 = OpConstant %short 0
 %short_3 = OpConstant %short 3
 %int_0 = OpConstant %int 0
+%100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps.
 %int_3 = OpConstant %int 3
 %int_min = OpConstant %int -2147483648
 %int_max = OpConstant %int 2147483647
@@ -988,4 +992,113 @@ INSTANTIATE_TEST_CASE_P(TestCase, InstructionNotFoldedTest,
       2, nullptr)
   ));
 // clang-format on
+
+template <class ResultType>
+struct InstructionFoldingCaseWithMap {
+  InstructionFoldingCaseWithMap(const std::string& tb, uint32_t id,
+                                ResultType result,
+                                std::function<uint32_t(uint32_t)> map)
+      : test_body(tb), id_to_fold(id), expected_result(result), id_map(map) {}
+
+  std::string test_body;
+  uint32_t id_to_fold;
+  ResultType expected_result;
+  std::function<uint32_t(uint32_t)> id_map;
+};
+
+using IntegerInstructionFoldingTestWithMap =
+    ::testing::TestWithParam<InstructionFoldingCaseWithMap<uint32_t>>;
+
+TEST_P(IntegerInstructionFoldingTestWithMap, Case) {
+  const auto& tc = GetParam();
+
+  // Build module.
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  // Fold the instruction to test.
+  opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+  inst = opt::FoldInstruction(inst, tc.id_map);
+
+  // Make sure the instruction folded as expected.
+  EXPECT_NE(inst, nullptr);
+  if (inst != nullptr) {
+    EXPECT_EQ(inst->opcode(), SpvOpConstant);
+    opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
+    const opt::analysis::IntConstant* result =
+        const_mrg->GetConstantFromInst(inst)->AsIntConstant();
+    EXPECT_NE(result, nullptr);
+    if (result != nullptr) {
+      EXPECT_EQ(result->GetU32BitValue(), tc.expected_result);
+    }
+  }
+}
+// clang-format off
+INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTestWithMap,
+  ::testing::Values(
+      // Test case 0: fold %3 = 0; %3 * n
+      InstructionFoldingCaseWithMap<uint32_t>(
+          Header() + "%main = OpFunction %void None %void_func\n" +
+              "%main_lab = OpLabel\n" +
+              "%n = OpVariable %_ptr_int Function\n" +
+              "%load = OpLoad %int %n\n" +
+              "%3 = OpCopyObject %int %int_0\n"
+              "%2 = OpIMul %int %3 %load\n" +
+              "OpReturn\n" +
+              "OpFunctionEnd",
+          2, 0, [](uint32_t id) {return (id == 3 ? INT_0_ID : id);})
+  ));
+// clang-format on
+
+using BooleanInstructionFoldingTestWithMap =
+    ::testing::TestWithParam<InstructionFoldingCaseWithMap<bool>>;
+
+TEST_P(BooleanInstructionFoldingTestWithMap, Case) {
+  const auto& tc = GetParam();
+
+  // Build module.
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  // Fold the instruction to test.
+  opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
+  inst = opt::FoldInstruction(inst, tc.id_map);
+
+  // Make sure the instruction folded as expected.
+  EXPECT_NE(inst, nullptr);
+  if (inst != nullptr) {
+    std::vector<SpvOp> bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse};
+    EXPECT_THAT(bool_opcodes, Contains(inst->opcode()));
+    opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
+    const opt::analysis::BoolConstant* result =
+        const_mrg->GetConstantFromInst(inst)->AsBoolConstant();
+    EXPECT_NE(result, nullptr);
+    if (result != nullptr) {
+      EXPECT_EQ(result->value(), tc.expected_result);
+    }
+  }
+}
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(TestCase, BooleanInstructionFoldingTestWithMap,
+  ::testing::Values(
+      // Test case 0: fold %3 = true; %3 || n
+      InstructionFoldingCaseWithMap<bool>(
+          Header() + "%main = OpFunction %void None %void_func\n" +
+              "%main_lab = OpLabel\n" +
+              "%n = OpVariable %_ptr_bool Function\n" +
+              "%load = OpLoad %bool %n\n" +
+              "%3 = OpCopyObject %bool %true\n" +
+              "%2 = OpLogicalOr %bool %3 %load\n" +
+              "OpReturn\n" +
+              "OpFunctionEnd",
+          2, true, [](uint32_t id) {return (id == 3 ? TRUE_ID : id);})
+  ));
+// clang-format on
 }  // anonymous namespace