| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 <dlpack/dlpack.h> |
| #include <gtest/gtest.h> |
| |
| #include <map> |
| #include <random> |
| #include <vector> |
| |
| #ifdef USE_MICRO_STANDALONE_RUNTIME |
| |
| // Use system(..), `gcc -shared -fPIC`, thus restrict the test to OS X for now. |
| #if defined(__APPLE__) && defined(__MACH__) |
| |
| #include <gtest/gtest.h> |
| #include <spawn.h> |
| #include <sys/wait.h> |
| #include <tvm/driver/driver_api.h> |
| #include <tvm/relay/analysis.h> |
| #include <tvm/relay/expr.h> |
| #include <tvm/relay/transform.h> |
| #include <tvm/relay/type.h> |
| #include <tvm/runtime/micro/standalone/utvm_runtime.h> |
| #include <tvm/runtime/module.h> |
| #include <tvm/runtime/packed_func.h> |
| #include <tvm/runtime/registry.h> |
| #include <tvm/te/operation.h> |
| #include <tvm/topi/generic/injective.h> |
| |
| TVM_REGISTER_GLOBAL("test.sch").set_body([](tvm::TVMArgs args, tvm::TVMRetValue* rv) { |
| *rv = ::tvm::topi::generic::schedule_injective(args[0], args[1]); |
| }); |
| |
| TEST(MicroStandaloneRuntime, BuildModule) { |
| using namespace tvm; |
| auto tensor_type = relay::TensorType({2, 3}, ::tvm::runtime::DataType::Float(32)); |
| auto a = relay::Var("a", tensor_type); |
| auto b = relay::Var("b", tensor_type); |
| auto add_op = relay::Op::Get("add"); |
| auto x = relay::Call(add_op, {a, b}, tvm::Attrs(), {}); |
| auto c = relay::Var("c", tensor_type); |
| auto y = relay::Call(add_op, {x, c}, tvm::Attrs(), {}); |
| auto func = relay::Function(relay::FreeVars(y), y, relay::Type(), {}); |
| auto A = tvm::runtime::NDArray::Empty({2, 3}, {kDLFloat, 32, 1}, {kDLCPU, 0}); |
| auto B = tvm::runtime::NDArray::Empty({2, 3}, {kDLFloat, 32, 1}, {kDLCPU, 0}); |
| auto C = tvm::runtime::NDArray::Empty({2, 3}, {kDLFloat, 32, 1}, {kDLCPU, 0}); |
| |
| auto pA = (float*)A->data; |
| auto pB = (float*)B->data; |
| auto pC = (float*)C->data; |
| |
| for (int i = 0; i < 6; ++i) { |
| pA[i] = i; |
| pB[i] = i + 1; |
| pC[i] = i + 2; |
| } |
| // get schedule |
| auto reg = tvm::runtime::Registry::Get("relay.op._Register"); |
| auto s_i = tvm::runtime::Registry::Get("test.sch"); |
| if (!reg) { |
| LOG(FATAL) << "no _Register"; |
| } |
| if (!s_i) { |
| LOG(FATAL) << "no test_sch"; |
| } |
| (*reg)("add", "FTVMSchedule", *s_i, 10); |
| // build |
| auto pfb = tvm::runtime::Registry::Get("relay.build_module._BuildModule"); |
| tvm::runtime::Module build_mod = (*pfb)(); |
| auto build_f = build_mod.GetFunction("build", false); |
| auto json_f = build_mod.GetFunction("get_graph_json", false); |
| auto mod_f = build_mod.GetFunction("get_module", false); |
| Map<tvm::Integer, tvm::Target> targets; |
| |
| Target llvm_tgt = Target("llvm"); |
| targets.Set(0, llvm_tgt); |
| build_f(func, targets, llvm_tgt); |
| std::string json = json_f(); |
| tvm::runtime::Module mod = mod_f(); |
| std::string o_fname = std::tmpnam(nullptr); |
| std::string so_fname = std::tmpnam(nullptr); |
| mod->SaveToFile(o_fname, "o"); |
| const std::vector<std::string> args = {"gcc", "-shared", "-fPIC", "-o", so_fname, o_fname}; |
| std::stringstream s; |
| for (auto& c : args) { |
| s << c << " "; |
| } |
| const auto ss = s.str(); |
| const auto ret = system(ss.c_str()); |
| ASSERT_EQ(ret, 0); |
| // Now, execute the minimal runtime. |
| auto* dsoModule = UTVMRuntimeDSOModuleCreate(so_fname.c_str(), so_fname.size()); |
| ASSERT_NE(dsoModule, nullptr); |
| auto* handle = UTVMRuntimeCreate(json.c_str(), json.size(), dsoModule); |
| ASSERT_NE(handle, nullptr); |
| |
| UTVMRuntimeSetInput(handle, 0, &A.ToDLPack()->dl_tensor); |
| UTVMRuntimeSetInput(handle, 1, &B.ToDLPack()->dl_tensor); |
| UTVMRuntimeSetInput(handle, 2, &C.ToDLPack()->dl_tensor); |
| UTVMRuntimeRun(handle); |
| auto Y = tvm::runtime::NDArray::Empty({2, 3}, {kDLFloat, 32, 1}, {kDLCPU, 0}); |
| UTVMRuntimeGetOutput(handle, 0, &Y.ToDLPack()->dl_tensor); |
| auto* pY = (float*)Y->data; |
| for (int i = 0; i < 6; ++i) { |
| CHECK_LT(fabs(pY[i] - (i + (i + 1) + (i + 2))), 1e-4); |
| } |
| UTVMRuntimeDestroy(handle); |
| UTVMRuntimeDSOModuleDestroy(dsoModule); |
| } |
| |
| #endif |
| #endif |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| return RUN_ALL_TESTS(); |
| } |