diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b3aab2d8177..5cd600cc398e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -632,6 +632,8 @@ endif() if(GTEST_FOUND) file(GLOB_RECURSE TEST_SRCS tests/cpp/*.cc) add_executable(cpptest ${TEST_SRCS}) + # include runtime files for unit testing + target_include_directories(cpptest PUBLIC "src/runtime") target_link_libraries(cpptest PRIVATE ${TVM_TEST_LIBRARY_NAME} GTest::GTest GTest::Main pthread dl) set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_ALL 1) set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1) diff --git a/cmake/modules/Hexagon.cmake b/cmake/modules/Hexagon.cmake index a9c4f6073bc3..4dd85eddfe3a 100644 --- a/cmake/modules/Hexagon.cmake +++ b/cmake/modules/Hexagon.cmake @@ -75,6 +75,9 @@ if (NOT BUILD_FOR_HEXAGON AND NOT BUILD_FOR_ANDROID) USE_HEXAGON_PROXY_RPC STREQUAL "OFF" AND NOT USE_HEXAGON_RPC) if(USE_HEXAGON_DEVICE STREQUAL "OFF") list(APPEND COMPILER_SRCS src/target/opt/build_hexagon_off.cc) + # append select runtime sources for unit testing + list(APPEND RUNTIME_SRCS src/runtime/hexagon/hexagon/hexagon_buffer.cc) + list(APPEND RUNTIME_SRCS src/runtime/hexagon/hexagon/hexagon_common.cc) if (NOT USE_HEXAGON_RPC) return() endif() diff --git a/src/runtime/hexagon/hexagon/hexagon_common.cc b/src/runtime/hexagon/hexagon/hexagon_common.cc index 6927cd5f6fcc..246a956ee66b 100644 --- a/src/runtime/hexagon/hexagon/hexagon_common.cc +++ b/src/runtime/hexagon/hexagon/hexagon_common.cc @@ -83,7 +83,7 @@ PackedFunc WrapPackedFunc(TVMBackendPackedCFunc faddr, const ObjectPtr& TVMValue* arg_values = const_cast(args.values); std::vector> buffer_args; - for (size_t i = 0; i < args.num_args; i++) { + for (int i = 0; i < args.num_args; i++) { if (args.type_codes[i] == kTVMDLTensorHandle) { DLTensor* tensor = static_cast(arg_values[i].v_handle); buffer_args.emplace_back(i, static_cast(tensor->data)); diff --git a/tests/cpp/runtime/hexagon_buffer.cc b/tests/cpp/runtime/hexagon_buffer.cc new file mode 100644 index 000000000000..9ff01f5fc06b --- /dev/null +++ b/tests/cpp/runtime/hexagon_buffer.cc @@ -0,0 +1,250 @@ +/* + * 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 +#include +#include + +using namespace tvm::runtime; +using namespace tvm::runtime::hexagon; + +TEST(HexagonBuffer, default_scope) { + Optional scope; + HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope); + EXPECT_EQ(hb.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); +} + +TEST(HexagonBuffer, ddr_scope) { + Optional scope("global"); + HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope); + EXPECT_EQ(hb.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); +} + +TEST(HexagonBuffer, vtcm_scope) { + Optional scope("global.vtcm"); + HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope); + EXPECT_EQ(hb.GetStorageScope(), HexagonBuffer::StorageScope::kVTCM); +} + +TEST(HexagonBuffer, invalid_scope) { + Optional scope("invalid"); + EXPECT_THROW(HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope), InternalError); +} + +TEST(HexagonBuffer, copy_from) { + Optional scope("global"); + HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope); + + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + hb.CopyFrom(data.data(), data.size()); + + uint8_t* ptr = static_cast(hb.GetPointer()[0]); + for (size_t i = 0; i < data.size(); ++i) { + EXPECT_EQ(ptr[i], data[i]); + } +} + +TEST(HexagonBuffer, copy_from_invalid_size) { + Optional scope("global"); + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + + // HexagonBuffer too small + HexagonBuffer toosmall(4 /* nbytes */, 8 /* alignment */, scope); + EXPECT_THROW(toosmall.CopyFrom(data.data(), data.size()), InternalError); + + // HexagonBuffer too big + HexagonBuffer toobig(16 /* nbytes */, 16 /* alignment */, scope); + EXPECT_THROW(toobig.CopyFrom(data.data(), data.size()), InternalError); +} + +TEST(HexagonBuffer, nd) { + Optional def; + HexagonBuffer hb_default(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, def); + EXPECT_EQ(hb_default.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); + + Optional global("global"); + HexagonBuffer hb_global(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, global); + EXPECT_EQ(hb_global.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); + + Optional vtcm("global.vtcm"); + HexagonBuffer hb_vtcm(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, vtcm); + EXPECT_EQ(hb_vtcm.GetStorageScope(), HexagonBuffer::StorageScope::kVTCM); + + Optional invalid("invalid"); + EXPECT_THROW(HexagonBuffer hb_invalid(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, invalid), + InternalError); +} + +TEST(HexagonBuffer, nd_copy_from) { + Optional scope("global"); + HexagonBuffer hb(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, scope); + + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + hb.CopyFrom(data.data(), data.size()); + + uint8_t* ptr = static_cast(hb.GetPointer()[0]); + EXPECT_EQ(ptr[0], data[0]); + EXPECT_EQ(ptr[1], data[1]); + EXPECT_EQ(ptr[2], data[2]); + EXPECT_EQ(ptr[3], data[3]); + + ptr = static_cast(hb.GetPointer()[1]); + EXPECT_EQ(ptr[0], data[4]); + EXPECT_EQ(ptr[1], data[5]); + EXPECT_EQ(ptr[2], data[6]); + EXPECT_EQ(ptr[3], data[7]); +} + +TEST(HexagonBuffer, 1d_copy_from_1d) { + Optional global("global"); + HexagonBuffer from(8 /* nbytes */, 8 /* alignment */, global); + + Optional vtcm("global.vtcm"); + HexagonBuffer to(8 /* nbytes */, 8 /* alignment */, vtcm); + + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + from.CopyFrom(data.data(), data.size()); + to.CopyFrom(from); + + uint8_t* ptr = static_cast(to.GetPointer()[0]); + for (size_t i = 0; i < data.size(); ++i) { + EXPECT_EQ(ptr[i], data[i]); + } +} + +TEST(HexagonBuffer, 2d_copy_from_1d) { + Optional vtcm("global.vtcm"); + HexagonBuffer hb1d(8 /* nbytes */, 8 /* alignment */, vtcm); + + Optional global("global"); + HexagonBuffer hb2d(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, global); + + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + hb1d.CopyFrom(data.data(), data.size()); + hb2d.CopyFrom(hb1d); + + uint8_t* ptr = static_cast(hb2d.GetPointer()[0]); + EXPECT_EQ(ptr[0], data[0]); + EXPECT_EQ(ptr[1], data[1]); + EXPECT_EQ(ptr[2], data[2]); + EXPECT_EQ(ptr[3], data[3]); + + ptr = static_cast(hb2d.GetPointer()[1]); + EXPECT_EQ(ptr[0], data[4]); + EXPECT_EQ(ptr[1], data[5]); + EXPECT_EQ(ptr[2], data[6]); + EXPECT_EQ(ptr[3], data[7]); +} + +TEST(HexagonBuffer, 1d_copy_from_2d) { + Optional vtcm("global.vtcm"); + HexagonBuffer hb2d(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, vtcm); + + Optional global("global.vtcm"); + HexagonBuffer hb1d(8 /* nbytes */, 8 /* alignment */, global); + + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + hb2d.CopyFrom(data.data(), data.size()); + hb1d.CopyFrom(hb2d); + + uint8_t* ptr = static_cast(hb1d.GetPointer()[0]); + for (size_t i = 0; i < data.size(); ++i) { + EXPECT_EQ(ptr[i], data[i]); + } +} + +TEST(HexagonBuffer, nd_copy_from_nd_invalid_size) { + Optional scope("global"); + HexagonBuffer hb1d(8 /* nbytes */, 8 /* alignment */, scope); + HexagonBuffer hb2d(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, scope); + + HexagonBuffer toosmall1d(4 /* nbytes */, 8 /* alignment */, scope); + EXPECT_THROW(hb1d.CopyFrom(toosmall1d), InternalError); + EXPECT_THROW(hb2d.CopyFrom(toosmall1d), InternalError); + + HexagonBuffer toosbig1d(16 /* nbytes */, 16 /* alignment */, scope); + EXPECT_THROW(hb1d.CopyFrom(toosbig1d), InternalError); + EXPECT_THROW(hb2d.CopyFrom(toosbig1d), InternalError); + + HexagonBuffer toosmall2d(2 /* ndim */, 2 /* nbytes */, 8 /* alignment */, scope); + EXPECT_THROW(hb1d.CopyFrom(toosmall2d), InternalError); + EXPECT_THROW(hb2d.CopyFrom(toosmall2d), InternalError); + + HexagonBuffer toobig2d(2 /* ndim */, 16 /* nbytes */, 16 /* alignment */, scope); + EXPECT_THROW(hb1d.CopyFrom(toobig2d), InternalError); + EXPECT_THROW(hb2d.CopyFrom(toobig2d), InternalError); +} + +TEST(HexagonBuffer, md_copy_from_nd) { + Optional scope("global"); + HexagonBuffer hb3d(3 /* ndim */, 4 /* nbytes */, 8 /* alignment */, scope); + HexagonBuffer hb4d(4 /* ndim */, 3 /* nbytes */, 8 /* alignment */, scope); + EXPECT_THROW(hb3d.CopyFrom(hb4d), InternalError); + EXPECT_THROW(hb4d.CopyFrom(hb3d), InternalError); +} + +TEST(HexagonBuffer, copy_to) { + Optional scope("global"); + HexagonBuffer hb(8 /* nbytes */, 8 /* alignment */, scope); + + std::vector data_in{0, 1, 2, 3, 4, 5, 6, 7}; + hb.CopyFrom(data_in.data(), data_in.size()); + + std::vector data_out{7, 6, 5, 4, 3, 2, 1, 0}; + hb.CopyTo(data_out.data(), data_out.size()); + + for (size_t i = 0; i < data_in.size(); ++i) { + EXPECT_EQ(data_in[i], data_out[i]); + } +} + +TEST(HexagonBuffer, nd_copy_to) { + Optional scope("global"); + HexagonBuffer hb(2 /* ndim */, 4 /* nbytes */, 8 /* alignment */, scope); + + std::vector data_in{0, 1, 2, 3, 4, 5, 6, 7}; + hb.CopyFrom(data_in.data(), data_in.size()); + + std::vector data_out{7, 6, 5, 4, 3, 2, 1, 0}; + hb.CopyTo(data_out.data(), data_out.size()); + + for (size_t i = 0; i < data_in.size(); ++i) { + EXPECT_EQ(data_in[i], data_out[i]); + } +} + +TEST(HexagonBuffer, external) { + std::vector data{0, 1, 2, 3, 4, 5, 6, 7}; + + Optional def; + HexagonBuffer hb_default(data.data(), data.size(), def); + EXPECT_EQ(hb_default.GetPointer()[0], data.data()); + EXPECT_EQ(hb_default.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); + + Optional global("global"); + HexagonBuffer hb_global(data.data(), data.size(), global); + EXPECT_EQ(hb_global.GetPointer()[0], data.data()); + EXPECT_EQ(hb_global.GetStorageScope(), HexagonBuffer::StorageScope::kDDR); + + Optional vtcm("global.vtcm"); + EXPECT_THROW(HexagonBuffer hb_vtcm(data.data(), data.size(), vtcm), InternalError); + + Optional invalid("invalid"); + EXPECT_THROW(HexagonBuffer hb_vtcm(data.data(), data.size(), invalid), InternalError); +}