Add logging and test cases for Controlflow ops (#4129) accepted/tizen_6.0_unified_hotfix tizen_20201012 tizen_6.0_hotfix accepted/tizen/6.0/unified/20201030.121029 accepted/tizen/6.0/unified/hotfix/20201103.051019 accepted/tizen/unified/20200915.060157 submit/tizen/20200915.021158 submit/tizen_6.0/20201029.205103 submit/tizen_6.0_hotfix/20201102.192503 submit/tizen_6.0_hotfix/20201103.114803 tizen_6.0.m2_release
authorHanjoung Lee <hanjoung.lee@samsung.com>
Thu, 10 Sep 2020 04:46:16 +0000 (13:46 +0900)
committerChunseok Lee <chunseok.lee@samsung.com>
Tue, 15 Sep 2020 02:10:32 +0000 (11:10 +0900)
- Log subgraph calls during execution so we can trace the execution stack.
- Add a OneOp TC for If

This is a squashed commit of 465828c0 8aba0221 and 117a4712 .

ONE-DCO-1.0-Signed-off-by: Hanjoung Lee <hanjoung.lee@samsung.com>

Change-Id: Idfa7044145b855222ab8ab382291e2d05af63f78
Signed-off-by: Chunseok Lee <chunseok.lee@samsung.com>
runtime/onert/core/src/backend/controlflow/kernel/IfLayer.cc
runtime/onert/core/src/backend/controlflow/kernel/WhileLayer.cc
runtime/onert/core/src/exec/IPermuteFunction.h
tests/nnfw_api/src/CircleGen.cc
tests/nnfw_api/src/CircleGen.h
tests/nnfw_api/src/one_op_tests/If.cc [new file with mode: 0644]

index 8377c71..7ca8224 100644 (file)
@@ -63,13 +63,16 @@ void IfLayer::run()
   };
 
   exec::ExecutorBase *subg_exec = nullptr;
-  if (getResultCond(_cond_tensor.get()))
+  bool cond_result = getResultCond(_cond_tensor.get());
+  if (cond_result)
   {
+    VERBOSE(If) << "Call to $" << _then_subg_index << " (then)" << std::endl;
     subg_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>(
         _executor_map->at(_then_subg_index).get());
   }
   else
   {
+    VERBOSE(If) << "Call to $" << _else_subg_index << " (else)" << std::endl;
     subg_exec = nnfw::misc::polymorphic_downcast<exec::ExecutorBase *>(
         _executor_map->at(_else_subg_index).get());
   }
@@ -120,6 +123,8 @@ void IfLayer::run()
   // Copy & run
   subg_exec->execute(_input_tensors, permute_op_input_to_subg_input);
   permute_subg_output_to_op_output->run();
+  VERBOSE(If) << "Return from $" << (cond_result ? _then_subg_index : _else_subg_index)
+              << std::endl;
 }
 
 } // namespace kernel
index 50936e5..0d1ae65 100644 (file)
@@ -175,7 +175,9 @@ void WhileLayer::run()
   permute_body_output_to_body_input->prepare();
   permute_body_output_to_op_output->prepare();
 
+  VERBOSE(While) << "Call to $" << _cond_subg_index << " (cond)" << std::endl;
   cond_exec->execute(_input_tensors, permute_op_input_to_cond_input);
+  VERBOSE(While) << "Return from $" << _cond_subg_index << std::endl;
 
   assert(cond_exec->getOutputTensors().size() == 1);
   auto &cond_output_tensor = cond_exec->getOutputTensors().at(0);
@@ -186,16 +188,22 @@ void WhileLayer::run()
   };
 
   const auto body_execute_with_op_inputs = [&]() {
+    VERBOSE(While) << "Call to $" << _body_subg_index << " (body)" << std::endl;
     body_exec->execute(_input_tensors, permute_op_input_to_body_input);
+    VERBOSE(While) << "Return from $" << _body_subg_index << std::endl;
   };
 
   const auto body_execute_with_body_outputs = [&]() {
+    VERBOSE(While) << "Call to $" << _body_subg_index << " (body)" << std::endl;
     body_exec->execute(body_exec->getOutputTensors(), permute_body_output_to_body_input);
+    VERBOSE(While) << "Return from $" << _body_subg_index << std::endl;
   };
 
   std::function<void()> body_execute = body_execute_with_op_inputs;
   const auto cond_execute = [&]() {
+    VERBOSE(While) << "Call to $" << _cond_subg_index << " (cond)" << std::endl;
     cond_exec->execute(body_exec->getOutputTensors(), permute_body_output_to_cond_input);
+    VERBOSE(While) << "Return from $" << _cond_subg_index << std::endl;
   };
   auto permute_to_outputs_fn = permute_op_input_to_op_output;
 
index 6b4d153..9bead2f 100644 (file)
@@ -50,7 +50,7 @@ private:
 public:
   virtual void run() override
   {
-    assert(_src_tensors.size() > 0);
+    // TODO Optimization : Make control does not reach here? when (_src_tensors.size() == 0)
     assert(_src_tensors.size() == _dst_tensors.size());
     auto src_it = _src_tensors.begin();
     auto dst_it = _dst_tensors.begin();
index 19cb95f..6ebd5a9 100644 (file)
@@ -183,6 +183,14 @@ uint32_t CircleGen::addOperatorWhile(const OperatorParams &params, uint32_t cond
                                 circle::BuiltinOptions_WhileOptions, options);
 }
 
+uint32_t CircleGen::addOperatorIf(const OperatorParams &params, uint32_t then_subg,
+                                  uint32_t else_subg)
+{
+  auto options = circle::CreateIfOptions(_fbb, then_subg, else_subg).Union();
+  return addOperatorWithOptions(params, circle::BuiltinOperator_IF,
+                                circle::BuiltinOptions_IfOptions, options);
+}
+
 // NOTE Please add addOperator functions ABOVE this lie
 //
 // %  How to add a new addOperatorXXX fuction
index 09ca5a5..8cb83bc 100644 (file)
@@ -108,6 +108,7 @@ public:
   uint32_t addOperatorRank(const OperatorParams &params);
   uint32_t addOperatorResizeNearestNeighbor(const OperatorParams &params);
   uint32_t addOperatorWhile(const OperatorParams &params, uint32_t cond_subg, uint32_t body_subg);
+  uint32_t addOperatorIf(const OperatorParams &params, uint32_t cond_subg, uint32_t body_subg);
 
   // NOTE Please add addOperator functions ABOVE this lie
   // ===== Add Operator methods end =====
diff --git a/tests/nnfw_api/src/one_op_tests/If.cc b/tests/nnfw_api/src/one_op_tests/If.cc
new file mode 100644 (file)
index 0000000..2eb1d34
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * 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 "GenModelTest.h"
+
+#include <memory>
+
+TEST_F(GenModelTest, OneOp_If)
+{
+  // The model looks just like the below pseudocode
+  //
+  // function model(x)
+  // {
+  //   if (x < 0.0)
+  //     return -100.0;
+  //   else
+  //     return 100.0;
+  // }
+
+  CircleGen cgen;
+
+  // constant buffers
+  std::vector<float> comp_data{0.0};
+  uint32_t comp_buf = cgen.addBuffer(comp_data);
+  std::vector<float> then_data{-100};
+  uint32_t then_buf = cgen.addBuffer(then_data);
+  std::vector<float> else_data{100};
+  uint32_t else_buf = cgen.addBuffer(else_data);
+
+  // primary subgraph
+  {
+    int x = cgen.addTensor({{1}, circle::TensorType_FLOAT32});
+    int comp = cgen.addTensor({{1}, circle::TensorType_FLOAT32, comp_buf});
+    int cond = cgen.addTensor({{1}, circle::TensorType_BOOL});
+    cgen.addOperatorLess({{x, comp}, {cond}});
+
+    int ret = cgen.addTensor({{1}, circle::TensorType_FLOAT32});
+    cgen.addOperatorIf({{cond}, {ret}}, 1, 2);
+
+    cgen.setInputsAndOutputs({x}, {ret});
+  }
+
+  // then subgraph
+  {
+    cgen.nextSubgraph();
+    int ret = cgen.addTensor({{1}, circle::TensorType_FLOAT32, then_buf});
+    cgen.setInputsAndOutputs({}, {ret});
+  }
+
+  // else subgraph
+  {
+    cgen.nextSubgraph();
+    int ret = cgen.addTensor({{1}, circle::TensorType_FLOAT32, else_buf});
+    cgen.setInputsAndOutputs({}, {ret});
+  }
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase({{{-1.0}}, {{-100.0}}});
+  _context->addTestCase({{{1.0}}, {{100.0}}});
+  _context->setBackends({"cpu"});
+
+  SUCCEED();
+}