From 22f5ebd0654298777ae2fa4a0b56dd3668649efc Mon Sep 17 00:00:00 2001 From: Pratyush Patel Date: Thu, 4 Apr 2019 09:42:20 -0700 Subject: [PATCH] added micro_common implementation and python interfaces (#18) --- 3rdparty/HalideIR | 1 + 3rdparty/dlpack | 2 +- 3rdparty/dmlc-core | 2 +- python/tvm/contrib/binutil.py | 41 +++++++++----------- src/runtime/micro/micro_common.cc | 25 ++++++------ src/runtime/micro/micro_device_api.cc | 55 ++++++++++++++++----------- src/runtime/micro/micro_session.h | 28 +++++--------- tests/python/contrib/test_binutil.py | 10 +++-- 8 files changed, 84 insertions(+), 80 deletions(-) create mode 160000 3rdparty/HalideIR diff --git a/3rdparty/HalideIR b/3rdparty/HalideIR new file mode 160000 index 0000000000000..ec9585a5a5df3 --- /dev/null +++ b/3rdparty/HalideIR @@ -0,0 +1 @@ +Subproject commit ec9585a5a5df3de91e8916ac2d27a4a509eac5fc diff --git a/3rdparty/dlpack b/3rdparty/dlpack index 0acb731e0e43d..5c792cef3aee5 160000 --- a/3rdparty/dlpack +++ b/3rdparty/dlpack @@ -1 +1 @@ -Subproject commit 0acb731e0e43d15deee27b66f10e4c5b4e667913 +Subproject commit 5c792cef3aee54ad8b7000111c9dc1797f327b59 diff --git a/3rdparty/dmlc-core b/3rdparty/dmlc-core index 3943914eed664..82bf4c2e2af31 160000 --- a/3rdparty/dmlc-core +++ b/3rdparty/dmlc-core @@ -1 +1 @@ -Subproject commit 3943914eed66470bd010df581e29e4dca4f7df6f +Subproject commit 82bf4c2e2af312b3d52513aa727483803a2f8734 diff --git a/python/tvm/contrib/binutil.py b/python/tvm/contrib/binutil.py index 105f42c29a0ae..6cbccf25e4bee 100644 --- a/python/tvm/contrib/binutil.py +++ b/python/tvm/contrib/binutil.py @@ -7,25 +7,25 @@ @register_func("tvm_get_section_size") -def tvm_get_section_size(binary_name, section): +def tvm_callback_get_section_size(binary_path, section): """Finds size of the section in the binary. Assumes "size" shell command exists (typically works only on Linux machines) Parameters ---------- - binary_name : string - name of the binary file + binary_path : str + path of the binary file - section : string + section : str type of section Return ------ - size : integer + size : integer size of the section in bytes """ section_map = {"text": "1", "data": "2", "bss": "3"} - p1 = subprocess.Popen(["size", binary_name], stdout=subprocess.PIPE) + p1 = subprocess.Popen(["size", binary_path], stdout=subprocess.PIPE) p2 = subprocess.Popen(["awk", "{print $" + section_map[section] + "}"], stdin=p1.stdout, stdout=subprocess.PIPE) p3 = subprocess.Popen(["tail", "-1"], stdin=p2.stdout, stdout=subprocess.PIPE) @@ -40,21 +40,21 @@ def tvm_get_section_size(binary_name, section): @register_func("tvm_relocate_binary") -def tvm_relocate_binary(binary_name, text, data, bss): +def tvm_callback_relocate_binary(binary_path, text, data, bss): """Relocates sections in the binary to new addresses Parameters ---------- - binary_name : string - name of the binary file + binary_path : str + path of the binary file - text : string + text : str text section address - data : string + data : str data section address - bss : string + bss : str bss section address Return @@ -64,7 +64,7 @@ def tvm_relocate_binary(binary_name, text, data, bss): """ tmp_dir = util.tempdir() rel_obj = tmp_dir.relpath("relocated.o") - p1 = subprocess.Popen(["ld", binary_name, + p1 = subprocess.Popen(["ld", binary_path, "-Ttext", text, "-Tdata", data, "-Tbss", bss, @@ -81,15 +81,15 @@ def tvm_relocate_binary(binary_name, text, data, bss): @register_func("tvm_read_binary_section") -def tvm_read_binary_section(binary, section): +def tvm_callback_read_binary_section(binary_path, section): """Returns the contents of the specified section in the binary file Parameters ---------- - binary : bytearray - contents of the binary + binary_path : str + path of the binary file - section : string + section : str type of section Return @@ -98,13 +98,10 @@ def tvm_read_binary_section(binary, section): contents of the read section """ tmp_dir = util.tempdir() - tmp_bin = tmp_dir.relpath("temp.bin") tmp_section = tmp_dir.relpath("tmp_section.bin") - with open(tmp_bin, "wb") as out_file: - out_file.write(bytes(binary)) p1 = subprocess.Popen(["objcopy", "--dump-section", "." + section + "=" + tmp_section, - tmp_bin], + binary_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (out, _) = p1.communicate() @@ -122,7 +119,7 @@ def tvm_read_binary_section(binary, section): @register_func("tvm_get_symbol_map") -def tvm_get_symbol_map(binary): +def tvm_callback_get_symbol_map(binary): """Obtains a map of symbols to addresses in the passed binary Parameters diff --git a/src/runtime/micro/micro_common.cc b/src/runtime/micro/micro_common.cc index 007c2e66da78a..bffa16296f746 100644 --- a/src/runtime/micro/micro_common.cc +++ b/src/runtime/micro/micro_common.cc @@ -31,7 +31,7 @@ const char* SectionToString(SectionKind section) { void* GetSymbol(std::unordered_map symbol_map, std::string name, - const void* base_addr) { + void* base_addr) { void* symbol_addr = symbol_map[name]; return (void*)((uint8_t*) symbol_addr - (uint8_t*) base_addr); } @@ -50,8 +50,9 @@ std::string RelocateBinarySections(std::string binary_name, void* text, void* data, void* bss) { - const auto* f = Registry::Get("tvm_relocate_binary"); - CHECK(f != nullptr) << "Require tvm_relocate_binary to exist in registry"; + const auto* f = Registry::Get("tvm_callback_relocate_binary"); + CHECK(f != nullptr) + << "Require tvm_callback_relocate_binary to exist in registry"; std::string relocated_bin = (*f)(binary_name, AddrToString(text), AddrToString(data), @@ -59,27 +60,29 @@ std::string RelocateBinarySections(std::string binary_name, return relocated_bin; } -std::string ReadSection(std::string binary, SectionKind section) { +std::string ReadSection(std::string binary_name, SectionKind section) { CHECK(section == kText || section == kData || section == kBss) << "ReadSection requires section to be one of text, data or bss."; - const auto* f = Registry::Get("tvm_read_binary_section"); - CHECK(f != nullptr) << "Require tvm_read_binary_section to exist in registry"; - std::string section_contents = (*f)(binary, SectionToString(section)); + const auto* f = Registry::Get("tvm_callback_read_binary_section"); + CHECK(f != nullptr) + << "Require tvm_callback_read_binary_section to exist in registry"; + std::string section_contents = (*f)(binary_name, SectionToString(section)); return section_contents; } size_t GetSectionSize(std::string binary_name, SectionKind section) { CHECK(section == kText || section == kData || section == kBss) << "GetSectionSize requires section to be one of text, data or bss."; - const auto* f = Registry::Get("tvm_get_section_size"); - CHECK(f != nullptr) << "Require tvm_get_section_size to exist in registry"; + const auto* f = Registry::Get("tvm_callback_get_section_size"); + CHECK(f != nullptr) + << "Require tvm_callback_get_section_size to exist in registry"; size_t size = (*f)(binary_name, SectionToString(section)); return size; } std::unordered_map GetSymbolMap(std::string binary) { - const auto* f = Registry::Get("tvm_get_symbol_map"); - CHECK(f != nullptr) << "Require tvm_get_symbol_map to exist in registry"; + const auto* f = Registry::Get("tvm_callback_get_symbol_map"); + CHECK(f != nullptr) << "Require tvm_callback_get_symbol_map to exist in registry"; TVMByteArray arr; arr.data = &binary[0]; arr.size = binary.length(); diff --git a/src/runtime/micro/micro_device_api.cc b/src/runtime/micro/micro_device_api.cc index ec246ac12c564..65a1b7df3402e 100644 --- a/src/runtime/micro/micro_device_api.cc +++ b/src/runtime/micro/micro_device_api.cc @@ -16,6 +16,10 @@ namespace runtime { */ class MicroDeviceAPI final : public DeviceAPI { public: + MicroDeviceAPI() { + session_ = MicroSession::Global(); + } + void SetDevice(TVMContext ctx) final {} void GetAttr(TVMContext ctx, DeviceAttrKind kind, TVMRetValue* rv) final { @@ -28,15 +32,12 @@ class MicroDeviceAPI final : public DeviceAPI { size_t nbytes, size_t alignment, TVMType type_hint) final { - // TODO: can make this a private member, but where to best init it? - std::shared_ptr session = MicroSession::Global(); - void* alloc_ptr = session->AllocateInSection(kHeap, nbytes); + void* alloc_ptr = session_->AllocateInSection(kHeap, nbytes); return alloc_ptr; } void FreeDataSpace(TVMContext ctx, void* ptr) final { - std::shared_ptr session = MicroSession::Global(); - session->FreeInSection(kHeap, ptr); + session_->FreeInSection(kHeap, ptr); } void CopyDataFromTo(const void* from, @@ -48,27 +49,33 @@ class MicroDeviceAPI final : public DeviceAPI { TVMContext ctx_to, TVMType type_hint, TVMStreamHandle stream) final { - std::shared_ptr session = MicroSession::Global(); - uint8_t buffer[size]; constexpr int micro_devtype = kDLMicroDev; std::tuple type_from_to(ctx_from.device_type, ctx_to.device_type); if (type_from_to == std::make_tuple(micro_devtype, micro_devtype)) { - // TODO: ignored ctx because we assume only one low-level micro_dev - is ok? - std::shared_ptr from_lld = session->low_level_device(); - std::shared_ptr to_lld = session->low_level_device(); - from_lld->Read((uint8_t*)(from) + from_offset, buffer, size); - to_lld->Write((uint8_t*)(to) + to_offset, buffer, size); - + CHECK(ctx_from.device_id == ctx_to.device_id) + << "can only copy between the same micro device"; + std::string buffer; + const std::shared_ptr& from_lld = session_->low_level_device(); + const std::shared_ptr& to_lld = session_->low_level_device(); + from_lld->Read( + const_cast(static_cast(from)) + from_offset, + const_cast(&buffer[0]), size); + to_lld->Write( + const_cast(static_cast(to)) + to_offset, + const_cast(&buffer[0]), size); } else if (type_from_to == std::make_tuple(micro_devtype, kDLCPU)) { - std::shared_ptr from_lld = session->low_level_device(); - from_lld->Read((uint8_t*)(from) + from_offset, buffer, size); - memcpy(static_cast(to) + to_offset, buffer, size); + const std::shared_ptr& from_lld = session_->low_level_device(); + from_lld->Read( + const_cast(static_cast(from)) + from_offset, + const_cast(static_cast(to)), size); } else if (type_from_to == std::make_tuple(micro_devtype, kDLCPU)) { - std::shared_ptr to_lld = session->low_level_device(); - to_lld->Write((uint8_t*)(to) + to_offset, - (uint8_t*)(from) + from_offset, size); + const std::shared_ptr& to_lld = session_->low_level_device(); + to_lld->Write( + const_cast(static_cast(to)) + to_offset, + const_cast(static_cast(from)) + from_offset, + size); } else { LOG(FATAL) << "Expect copy from/to micro_dev or between micro_dev\n"; @@ -81,15 +88,13 @@ class MicroDeviceAPI final : public DeviceAPI { // TODO: what about ctx? void* AllocWorkspace(TVMContext ctx, size_t size, TVMType type_hint) final { - std::shared_ptr session = MicroSession::Global(); - void* alloc_ptr = session->AllocateInSection(kWorkspace, size); + void* alloc_ptr = session_->AllocateInSection(kWorkspace, size); return alloc_ptr; } // TODO: what about ctx? void FreeWorkspace(TVMContext ctx, void* data) final { - std::shared_ptr session = MicroSession::Global(); - session->FreeInSection(kWorkspace, data); + session_->FreeInSection(kWorkspace, data); } /*! @@ -101,6 +106,10 @@ class MicroDeviceAPI final : public DeviceAPI { std::make_shared(); return inst; } + + private: + /*! \brief pointer to global session */ + MicroSession* session_; }; // register device that can be obtained from Python frontend diff --git a/src/runtime/micro/micro_session.h b/src/runtime/micro/micro_session.h index 4b5f0fd900eef..e277b7a473693 100644 --- a/src/runtime/micro/micro_session.h +++ b/src/runtime/micro/micro_session.h @@ -28,7 +28,7 @@ class MicroSectionAllocator { * \param section_start start address of the section * \param section_end end address of the section (non inclusive) */ - MicroSectionAllocator(void* section_start, void* section_end) + MicroSectionAllocator(void* section_start, void* section_end) : section_start_(section_start), section_end_(section_end), section_max_(section_start) { } @@ -96,17 +96,7 @@ class MicroSession { * \brief get MicroSession global singleton * \return pointer to the micro session global singleton */ - static std::shared_ptr& Global() { - static std::shared_ptr inst = std::make_shared(); - return inst; - } - - /*! - * \brief initializes session by setting up low_level_device_ - * \param args TVMArgs passed into the micro.init packedfunc - * \note must be called upon first call to Global() - */ - void InitSession(TVMArgs args); + static MicroSession* Global(); /*! * \brief allocate memory in section @@ -134,7 +124,7 @@ class MicroSession { * \brief returns low-level device pointer * \note assumes low_level_device_ is initialized */ - const std::shared_ptr low_level_device() const { + const std::shared_ptr& low_level_device() const { return low_level_device_; } @@ -181,17 +171,17 @@ class MicroSession { void AllocateTVMArgs(TVMArgs args); void TargetAwareWrite(int64_t val, AllocatorStream* stream); - + void TargetAwareWrite(uint64_t val, AllocatorStream* stream); - + void TargetAwareWrite(double val, AllocatorStream* stream); - + void TargetAwareWrite(const char* val, AllocatorStream* stream); - + void TargetAwareWrite(TVMType val, AllocatorStream* stream); - + void TargetAwareWrite(TVMContext* val, AllocatorStream* stream); - + void TargetAwareWrite(TVMArray* val, AllocatorStream* stream); }; } // namespace runtime diff --git a/tests/python/contrib/test_binutil.py b/tests/python/contrib/test_binutil.py index 2288eb3b3817d..38d680932e4a8 100644 --- a/tests/python/contrib/test_binutil.py +++ b/tests/python/contrib/test_binutil.py @@ -60,10 +60,14 @@ def verify(): def test_tvm_read_binary_section(binary): + tmp_dir = util.tempdir() + tmp_bin = tmp_dir.relpath("obj.bin") + with open(tmp_bin, "wb") as f: + f.write(binary) def verify(): - text_bin = tvm_read_binary_section(binary, "text") - data_bin = tvm_read_binary_section(binary, "data") - bss_bin = tvm_read_binary_section(binary, "bss") + text_bin = tvm_read_binary_section(tmp_bin, "text") + data_bin = tvm_read_binary_section(tmp_bin, "data") + bss_bin = tvm_read_binary_section(tmp_bin, "bss") print("Read text section part of binary? %r" % (text_bin in binary)) print("Read data section part of binary? %r" % (data_bin in binary)) print("Read bss section part of binary? %r" % (bss_bin in binary))