blob: 0be891227b4f5d7d6e53ce46ccdcb41297e6fc18 [file] [log] [blame]
Geoffrey Martin-Noble552d3f82021-05-25 17:56:09 -07001// Copyright 2019 The IREE Authors
powderluv2e2fc9c2019-10-23 10:09:44 -07002//
Geoffrey Martin-Noble552d3f82021-05-25 17:56:09 -07003// Licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
powderluv2e2fc9c2019-10-23 10:09:44 -07006
Stella Laurenzo3b44a0a2022-04-18 19:57:57 -07007#include "./vm.h"
powderluv2e2fc9c2019-10-23 10:09:44 -07008
Stella Laurenzoabe91fa2023-06-26 16:57:30 -07009#include <ios>
10#include <sstream>
11#include <unordered_set>
12
13#include "./buffer_interop.h"
Stella Laurenzo3b44a0a2022-04-18 19:57:57 -070014#include "./status_utils.h"
Stella Laurenzodc513de2019-12-19 10:19:57 -080015#include "iree/base/api.h"
Stella Laurenzo89a41d92023-06-19 19:17:42 -070016
Stella Laurenzo7b066572022-07-31 18:38:15 -070017// TODO: We shouldn't need the HAL API but it is used for direct printing
18// summaries of HAL objects in lists. We should have a better way of doing this
19// dynamically vs hard depending on a type switch here.
Ben Vanik6ada2932021-06-21 09:09:32 -070020#include "iree/modules/hal/module.h"
Eugene Zhulenev6f81ceb2023-05-16 19:23:26 -070021#include "iree/tooling/modules/resolver.h"
Ben Vanikc3a13e62020-11-16 01:54:22 -080022#include "iree/vm/api.h"
powderluv2e2fc9c2019-10-23 10:09:44 -070023
Stella Laurenzoabe91fa2023-06-26 16:57:30 -070024using namespace nanobind::literals;
Stella Laurenzo89a41d92023-06-19 19:17:42 -070025
powderluv2e2fc9c2019-10-23 10:09:44 -070026namespace iree {
27namespace python {
28
Stella Laurenzoedc8a982019-12-20 09:22:37 -080029namespace {
30
Stella Laurenzo89a41d92023-06-19 19:17:42 -070031static const char kFromBufferDocstring[] =
32 R"(Creates a Vmmodule from a Python buffer.
33
34This is intended as a quick and dirty way to instantiate a VmModule from
35a binary blob. It will implicitly make a copy if alignment is not sufficient.
36
37It is recommended to use one of the other construction methods for maximum
38determinism and efficiency:
39
40* `mmap` : To memory map from a file.
41* `wrap_buffer` : To directly wrap a Python buffer that is known to be
42 aligned properly.
43* `copy_buffer` : To always make a copy of a Python buffer such that it is
44 aligned properly.
45
46This was historically called `from_flatbuffer`. It is recommended that new
47code use `flat_buffer`.
48
49Args:
50 instance: A VmInstance.
51 buffer: An object implementing the Python buffer protocol. Typically a
52 bytes, bytearray, memoryview, etc.
53 warn_if_copy: Raises a warning if alignment is not sufficient to use the
54 buffer directly, resulting in a copy. Defaults to True.
55)";
56
57static const char kCopyBufferDocstring[] =
58 R"(Creates a VmModule by making a copy of a Python buffer.
59
60Args:
61 instance: A VmInstance.
62 buffer: An object implementing the Python buffer protocol. Typically a
63 bytes, bytearray, memoryview, etc.
64)";
65
66static const char kWrapBufferDocstring[] =
67 R"(Creates a VmModule by directly using the backing memory of a Python buffer.
68
69Args:
70 instance: A VmInstance.
71 buffer: An object implementing the Python buffer protocol. Typically a
72 bytes, bytearray, memoryview, etc.
73 destroy_callback: A no-argument callback that is invoked when the backing
74 buffer is no longer in use.
75 close_buffer: Whether to call the `close` method on the `buffer` (prior to
76 invoking `destroy_callback`). Defaults to False.
77
78Raises:
79 ValueError if alignment is not satisfied.
80)";
81
82static const char kMMapDocstring[] =
83 R"(Create a VmModule by mmap'ing a file.
84
85When backed by a file, this is generally the most effective way to create a
86VmModule. Especially for large modules, this will result in the fewest
87copies and the most effective use of the system cache across invocations.
88
89Note that mmap behavior differs between Posix and Windows. Whereas the former
90will allow the backing file to be open before an mmap call and deleted
91immediately after, Windows generally allows neither. For compatibility,
92make sure that the backing file is not open for writing before calling this
93method and that if it needs to be deleted when done, that is done in a
94`destroy_callback`.
95
96Args:
97 instance: A VmInstance.
98 filepath: Path to the file on the file system.
99 destroy_callback: A no-argument callback that is invoked when the backing
100 buffer is no longer in use.
101)";
102
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700103// RAII wrapper for a Py_buffer which calls PyBuffer_Release when it goes
104// out of scope.
105class PyBufferReleaser {
106 public:
107 PyBufferReleaser(Py_buffer& b) : b_(b) {}
108 ~PyBufferReleaser() { PyBuffer_Release(&b_); }
109
110 private:
111 Py_buffer& b_;
112};
113
114py::dict GetFunctionReflectionDict(iree_vm_function_t& f) {
115 py::dict attrs;
Ben Vanik1d60c182022-06-28 12:37:40 -0700116 for (iree_host_size_t i = 0;; ++i) {
117 iree_string_pair_t attr;
118 auto status = iree_vm_function_get_attr(f, i, &attr);
119 if (iree_status_is_out_of_range(status)) {
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700120 iree_status_ignore(status);
121 break;
122 }
123 CheckApiStatus(status, "Error getting reflection attr");
Ben Vanik1d60c182022-06-28 12:37:40 -0700124 py::str key_str(attr.key.data, attr.key.size);
125 py::str value_str(attr.value.data, attr.value.size);
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700126 attrs[std::move(key_str)] = std::move(value_str);
127 }
128 return attrs;
129}
130
Stella Laurenzoedc8a982019-12-20 09:22:37 -0800131} // namespace
132
Stella Laurenzodc513de2019-12-19 10:19:57 -0800133//------------------------------------------------------------------------------
Stella Laurenzo4f400802024-04-04 09:55:24 -0700134// VmBuffer
135//------------------------------------------------------------------------------
136
137int VmBuffer::HandleBufferProtocol(Py_buffer* view, int flags) {
138 view->buf = raw_ptr()->data.data;
139 view->len = raw_ptr()->data.data_length;
140 view->readonly = !(raw_ptr()->access & IREE_VM_BUFFER_ACCESS_MUTABLE);
141 view->itemsize = 1;
142 view->format = (char*)"B"; // Byte
143 view->ndim = 1;
144 view->shape = nullptr;
145 view->strides = nullptr;
146 view->suboffsets = nullptr;
147 view->internal = nullptr;
148 return 0;
149}
150
151//------------------------------------------------------------------------------
Stella Laurenzodc513de2019-12-19 10:19:57 -0800152// VmInstance
153//------------------------------------------------------------------------------
154
155VmInstance VmInstance::Create() {
Ben Vanikcc436802023-06-10 08:53:52 -0700156 IREE_TRACE_SCOPE_NAMED("VmInstance::Create");
Ben Vanik9aa83ed2022-08-06 12:55:34 -0700157
158 iree_vm_instance_t* instance = NULL;
Ben Vanik09630d62023-04-13 14:21:40 -0700159 auto status = iree_vm_instance_create(IREE_VM_TYPE_CAPACITY_DEFAULT,
160 iree_allocator_system(), &instance);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800161 CheckApiStatus(status, "Error creating instance");
Ben Vanik9aa83ed2022-08-06 12:55:34 -0700162
163 // The python bindings assume the HAL is always available for use.
164 // We register the types here so modules can be loaded using the HAL types
165 // in any order.
166 CheckApiStatus(iree_hal_module_register_all_types(instance),
167 "registering HAL types");
168
Stella Laurenzo65690a12022-01-31 12:43:00 -0800169 return VmInstance::StealFromRawPtr(instance);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800170}
171
172//------------------------------------------------------------------------------
173// VmContext
174//------------------------------------------------------------------------------
175
176VmContext VmContext::Create(VmInstance* instance,
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700177 std::optional<std::vector<VmModule*>>& modules) {
Ben Vanikcc436802023-06-10 08:53:52 -0700178 IREE_TRACE_SCOPE_NAMED("VmContext::Create");
Stella Laurenzodc513de2019-12-19 10:19:57 -0800179 iree_vm_context_t* context;
180 if (!modules) {
181 // Simple create with open allowed modules.
Ben Vanik89e95302021-10-05 17:05:39 -0700182 auto status =
183 iree_vm_context_create(instance->raw_ptr(), IREE_VM_CONTEXT_FLAG_NONE,
184 iree_allocator_system(), &context);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800185 CheckApiStatus(status, "Error creating vm context");
186 } else {
187 // Closed set of modules.
Ben Vanikb9b794b2021-06-19 20:11:08 -0700188 std::vector<iree_vm_module_t*> module_handles;
Stella Laurenzodc513de2019-12-19 10:19:57 -0800189 module_handles.resize(modules->size());
190 for (size_t i = 0, e = module_handles.size(); i < e; ++i) {
191 module_handles[i] = (*modules)[i]->raw_ptr();
192 }
193 auto status = iree_vm_context_create_with_modules(
Ben Vanik70c2bb02022-06-09 12:42:54 -0700194 instance->raw_ptr(), IREE_VM_CONTEXT_FLAG_NONE, module_handles.size(),
195 module_handles.data(), iree_allocator_system(), &context);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800196 CheckApiStatus(status, "Error creating vm context with modules");
197 }
198
Ben Vanik91c75fb2022-06-23 19:08:12 -0700199 IREE_ASSERT(context);
Stella Laurenzo65690a12022-01-31 12:43:00 -0800200 return VmContext::StealFromRawPtr(context);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800201}
202
203void VmContext::RegisterModules(std::vector<VmModule*> modules) {
Ben Vanikb9b794b2021-06-19 20:11:08 -0700204 std::vector<iree_vm_module_t*> module_handles;
Stella Laurenzodc513de2019-12-19 10:19:57 -0800205 module_handles.resize(modules.size());
206 for (size_t i = 0, e = module_handles.size(); i < e; ++i) {
207 module_handles[i] = modules[i]->raw_ptr();
208 }
Ben Vanik70c2bb02022-06-09 12:42:54 -0700209 auto status = iree_vm_context_register_modules(
210 raw_ptr(), module_handles.size(), &module_handles[0]);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800211 CheckApiStatus(status, "Error registering modules");
212}
213
Stella Laurenzod5efc482019-12-21 04:59:36 -0800214void VmContext::Invoke(iree_vm_function_t f, VmVariantList& inputs,
215 VmVariantList& outputs) {
Stella Laurenzod2009dc2022-03-08 17:59:40 -0800216 iree_status_t status;
217 {
218 py::gil_scoped_release release;
219 status = iree_vm_invoke(raw_ptr(), f, IREE_VM_INVOCATION_FLAG_NONE, nullptr,
220 inputs.raw_ptr(), outputs.raw_ptr(),
221 iree_allocator_system());
222 }
223 CheckApiStatus(status, "Error invoking function");
Stella Laurenzod5efc482019-12-21 04:59:36 -0800224}
225
Stella Laurenzodc513de2019-12-19 10:19:57 -0800226//------------------------------------------------------------------------------
227// VmModule
228//------------------------------------------------------------------------------
229
Eugene Zhulenev6f81ceb2023-05-16 19:23:26 -0700230VmModule VmModule::ResolveModuleDependency(VmInstance* instance,
231 const std::string& name,
232 uint32_t minimum_version) {
Ben Vanikcc436802023-06-10 08:53:52 -0700233 IREE_TRACE_SCOPE_NAMED("VmModule::ResolveModuleDependency");
Eugene Zhulenev6f81ceb2023-05-16 19:23:26 -0700234 iree_vm_module_t* module = nullptr;
235
236 iree_vm_module_dependency_t dependency = {
237 iree_make_cstring_view(name.c_str()), minimum_version,
238 IREE_VM_MODULE_DEPENDENCY_FLAG_REQUIRED};
239
240 auto status = iree_tooling_resolve_module_dependency(
241 instance->raw_ptr(), &dependency, iree_allocator_system(), &module);
242
243 assert(module != nullptr);
244
245 CheckApiStatus(status, "Error resolving module dependency");
246 auto py_module = VmModule::StealFromRawPtr(module);
247 return py_module;
248}
249
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700250VmModule VmModule::MMap(VmInstance* instance, std::string filepath,
251 py::object destroy_callback) {
252 IREE_TRACE_SCOPE_NAMED("VmModule::MMap");
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700253 auto mmap_module = py::module_::import_("mmap");
254 auto open_func = py::module_::import_("io").attr("open");
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700255 auto file_obj = open_func(filepath, "r+b");
256 // The signature of mmap is different on Windows vs others. On others,
257 // we use explicit flags and protection attributes for better control,
258 // triggering off of the presence of the MAP_SHARED flag constant (which
259 // is not present on Windows).
260 py::object mapped_file;
261 if (py::hasattr(mmap_module, "MAP_SHARED")) {
262 // Posix mmap signature.
263 auto flags = py::cast<int64_t>(mmap_module.attr("MAP_SHARED"));
264 // MAP_POPULATE isn't available on all versions/platforms.
265 if (py::hasattr(mmap_module, "MAP_POPULATE")) {
266 flags |= py::cast<int64_t>(mmap_module.attr("MAP_POPULATE"));
267 }
268 auto prot = py::cast<int64_t>(mmap_module.attr("PROT_READ"));
269 mapped_file = mmap_module.attr("mmap")(file_obj.attr("fileno")(), 0,
270 "flags"_a = flags, "prot"_a = prot);
271 } else {
272 // Windows mmap signature.
273 mapped_file =
274 mmap_module.attr("mmap")(file_obj.attr("fileno")(), 0,
275 "access"_a = mmap_module.attr("ACCESS_READ"));
276 }
277 // Backing file can be closed after a successful mmap call.
278 file_obj.attr("close")();
279
280 // MADV_RANDOM is not available on Windows (and possibly others?).
281 if (py::hasattr(mmap_module, "MADV_RANDOM")) {
282 mapped_file.attr("madvise")(mmap_module.attr("MADV_RANDOM"));
283 }
284 return WrapBuffer(instance, std::move(mapped_file),
285 std::move(destroy_callback),
286 /*close_buffer=*/true);
287}
288
289VmModule VmModule::WrapBuffer(VmInstance* instance, py::object buffer_obj,
290 py::object destroy_callback, bool close_buffer) {
291 IREE_TRACE_SCOPE_NAMED("VmModule::FromAlignedMemory");
Stella Laurenzo92df2b42023-12-19 17:32:30 -0800292 // State object that is retained for the life of the module.
293 // It is responsible for keeping the backing resources alive and
294 // holding the user-level destroy callback.
295 // Note that the original buffer_obj is not captured explicitly but
296 // is available as part of the Py_buffer underlying the PyBufferRequest.
297 // Aside from being more efficient, avoiding redundant capture removes
298 // destruction race potential.
299 struct BufferState {
300 BufferState(py::object buffer_obj, py::object destroy_callback,
301 bool close_buffer)
302 : buffer_info(buffer_obj, PyBUF_SIMPLE),
303 destroy_callback(std::move(destroy_callback)),
304 close_buffer(close_buffer) {}
305 PyBufferRequest buffer_info;
306 py::object destroy_callback;
307 bool close_buffer;
308
309 py::handle get_buffer() { return py::handle(buffer_info.view().obj); }
310 };
311 BufferState* state =
312 new BufferState(buffer_obj, destroy_callback, close_buffer);
313 PyBufferRequest& buffer_info = state->buffer_info;
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700314 if (!iree_host_size_has_alignment((uintptr_t)buffer_info.view().buf,
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700315 IREE_HAL_HEAP_BUFFER_ALIGNMENT)) {
316 std::stringstream err;
317 err << "VmModule.from_aligned_memory received an unaligned buffer. ";
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700318 err << "Got 0x" << (void*)buffer_info.view().buf << ", expected alignment ";
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700319 err << IREE_HAL_HEAP_BUFFER_ALIGNMENT;
320 throw std::invalid_argument(err.str());
321 }
322
Ben Vanik9aa83ed2022-08-06 12:55:34 -0700323 iree_vm_module_t* module = nullptr;
Ben Vanik5ca3e182021-07-12 07:46:23 -0700324 auto ctl_fn = +([](void* self, iree_allocator_command_t command,
325 const void* params, void** inout_ptr) {
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700326 py::gil_scoped_acquire gil;
Ben Vanik5ca3e182021-07-12 07:46:23 -0700327 assert(command == IREE_ALLOCATOR_COMMAND_FREE);
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700328 try {
Stella Laurenzo92df2b42023-12-19 17:32:30 -0800329 // Destruction sequencing is tricky. We must have released the
330 // PyBufferRequest before calling close, so we first get what we
331 // need out of the state into local variables, then delete the state
332 // (releasing the PyBufferRequest), then closing and issuing the
333 // destroy callback. Getting the order wrong will result in an
334 // unrecoverable exception indicating the the buffer cannot be closed
335 // with outstanding mappings.
336 BufferState* state = static_cast<BufferState*>(self);
337 py::object destroy_callback = std::move(state->destroy_callback);
338 py::object buffer_to_close;
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700339 if (state->close_buffer) {
Stella Laurenzo92df2b42023-12-19 17:32:30 -0800340 buffer_to_close = py::borrow(state->get_buffer());
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700341 }
342 delete state;
Stella Laurenzo92df2b42023-12-19 17:32:30 -0800343
344 if (buffer_to_close) {
345 buffer_to_close.attr("close")();
346 }
347
348 if (!destroy_callback.is_none()) {
349 destroy_callback();
350 }
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700351 } catch (std::exception& e) {
352 // There are many situations where deallocation exceptions can be
353 // swallowed, so carp loudly. This is almost always a critical issue
354 // that needs to be visible.
355 fprintf(
356 stderr,
357 "error: exception raised while deallocating storage for an "
358 "iree.runtime.VmModule. This is unrecoverable and likely indicates a "
359 "serious problem, minimally resulting in memory leaks: %s",
360 e.what());
361 return iree_make_status(
362 IREE_STATUS_UNKNOWN,
363 "exception raised while deallocating storage for an "
364 "iree.runtime.VmModule. This is unrecoverable and likely indicates a "
365 "serious problem, minimally resulting in memory leaks: %s",
366 e.what());
367 }
Ben Vanik5ca3e182021-07-12 07:46:23 -0700368 return iree_ok_status();
Stella Laurenzoe2b0d7f2020-01-06 13:14:40 -0800369 });
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700370 iree_allocator_t deallocator{/*self=*/state, /*ctl=*/ctl_fn};
Stella Laurenzoe2b0d7f2020-01-06 13:14:40 -0800371
Stella Laurenzodc513de2019-12-19 10:19:57 -0800372 auto status = iree_vm_bytecode_module_create(
Ben Vanik9aa83ed2022-08-06 12:55:34 -0700373 instance->raw_ptr(),
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700374 {static_cast<const uint8_t*>(buffer_info.view().buf),
375 static_cast<iree_host_size_t>(buffer_info.view().len)},
Ben Vanikf5bd6fd2020-08-05 21:14:57 -0700376 deallocator, iree_allocator_system(), &module);
Ben Vanikaf5401f2020-08-06 21:31:15 -0700377 if (!iree_status_is_ok(status)) {
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700378 delete state;
Stella Laurenzodc513de2019-12-19 10:19:57 -0800379 }
380
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700381 CheckApiStatus(status, "Error creating vm module from aligned memory");
Stella Laurenzo65690a12022-01-31 12:43:00 -0800382 auto py_module = VmModule::StealFromRawPtr(module);
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700383 // Stash a reference to the flatbuffer at the Python instance level. This
384 // is exposed to the tracing API, allowing it to get at the backing contents.
385 py_module.stashed_flatbuffer_blob = buffer_obj;
Stella Laurenzo73045212021-06-25 16:18:57 -0700386 return py_module;
Stella Laurenzodc513de2019-12-19 10:19:57 -0800387}
388
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700389VmModule VmModule::CopyBuffer(VmInstance* instance, py::object buffer_obj) {
390 IREE_TRACE_SCOPE_NAMED("VmModule::CopyBuffer");
391 auto alignment =
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700392 py::cast<uintptr_t>(py::module_::import_("mmap").attr("PAGESIZE"));
393 auto bytearray_ctor = py::module_::import_("builtins").attr("bytearray");
394 PyBufferRequest src_buffer_info(buffer_obj, PyBUF_SIMPLE);
395 auto src_buffer_size = src_buffer_info.view().len;
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700396
397 // Need to allocate an extra page because there is no control at the Python
398 // level for the alignment it may have.
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700399 auto dst_buffer = bytearray_ctor(src_buffer_size + alignment);
400 PyBufferRequest dst_buffer_info(dst_buffer, PyBUF_SIMPLE);
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700401 void* dst_aligned =
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700402 (void*)iree_host_align((uintptr_t)dst_buffer_info.view().buf, alignment);
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700403 uintptr_t dst_offset =
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700404 (uintptr_t)dst_aligned - (uintptr_t)dst_buffer_info.view().buf;
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700405
406 // Now create a memoryview over the unaligned bytearray and slice into that
407 // to get the aligned Python buffer.
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700408 auto dst_slice =
409 py::slice(py::cast(dst_offset), py::cast(dst_offset + src_buffer_size),
410 py::cast(1));
411
412 py::object dst_view = py::steal<py::object>(
413 PyMemoryView_GetContiguous(dst_buffer.ptr(), PyBUF_READ, 'C'));
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700414 py::object dst_view_aligned = dst_view[dst_slice];
415
416 // If any of the indexing math was wrong, Python exceptions will be raised
417 // above, so this is implicitly guarding the memcpy if it is done last.
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700418 std::memcpy(dst_aligned, src_buffer_info.view().buf, src_buffer_size);
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700419 return WrapBuffer(instance, std::move(dst_view_aligned),
420 /*destroy_callback=*/py::none(),
421 /*close_buffer=*/false);
422}
423
424VmModule VmModule::FromBuffer(VmInstance* instance, py::object buffer_obj,
425 bool warn_if_copy) {
426 IREE_TRACE_SCOPE_NAMED("VmModule::FromBuffer");
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700427 PyBufferRequest buffer_info(buffer_obj, PyBUF_SIMPLE);
428
429 if (iree_host_size_has_alignment((uintptr_t)buffer_info.view().buf,
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700430 IREE_HAL_HEAP_BUFFER_ALIGNMENT)) {
431 return WrapBuffer(instance, std::move(buffer_obj),
432 /*destroy_callback=*/py::none(), /*close_buffer=*/false);
433 } else {
434 if (warn_if_copy) {
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700435 py::module_::import_("warnings")
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700436 .attr("warn")(
437 "Making copy of unaligned VmModule buffer. It is recommended to "
438 "make this deterministic by calling `copy_buffer` to always make "
439 "a copy or `mmap` to efficiently load from a file. This warning "
440 "can be silenced by adding `warn_if_copy=False` to "
441 "`from_buffer`");
442 }
443 return CopyBuffer(instance, std::move(buffer_obj));
444 }
445}
446
Ben Vanikdf89eb02021-06-20 18:01:16 -0700447std::optional<iree_vm_function_t> VmModule::LookupFunction(
Stella Laurenzodc513de2019-12-19 10:19:57 -0800448 const std::string& name, iree_vm_function_linkage_t linkage) {
449 iree_vm_function_t f;
Ben Vanikfcd368b2019-12-20 10:55:37 -0800450 auto status = iree_vm_module_lookup_function_by_name(
Scott Todd60b07642023-06-15 09:41:01 -0700451 raw_ptr(), linkage,
452 {name.data(), static_cast<iree_host_size_t>(name.size())}, &f);
Ben Vanikaf5401f2020-08-06 21:31:15 -0700453 if (iree_status_is_not_found(status)) {
454 iree_status_ignore(status);
Ben Vanikdf89eb02021-06-20 18:01:16 -0700455 return std::nullopt;
Stella Laurenzodc513de2019-12-19 10:19:57 -0800456 }
457 CheckApiStatus(status, "Error looking up function");
458 return f;
459}
460
Stella Laurenzo480e6ef2019-12-23 10:07:52 -0800461//------------------------------------------------------------------------------
Stella Laurenzo95dff502022-07-31 14:16:12 -0700462// VmRef
463//------------------------------------------------------------------------------
464
465const char* const VmRef::kRefAttr = "__iree_vm_ref__";
466const char* const VmRef::kCastAttr = "__iree_vm_cast__";
Ben Vanik09630d62023-04-13 14:21:40 -0700467const char* const VmRef::kTypeAttr = "__iree_vm_type__";
Stella Laurenzo95dff502022-07-31 14:16:12 -0700468
Stella Laurenzo7b066572022-07-31 18:38:15 -0700469py::object VmRef::Deref(py::object ref_object_class, bool optional) {
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700470 py::object casted = ref_object_class.attr(kCastAttr)(this);
Stella Laurenzo7b066572022-07-31 18:38:15 -0700471 if (!optional && casted.is_none()) {
472 throw py::type_error("Cannot dereference to specific type");
473 }
474 return casted;
Stella Laurenzo95dff502022-07-31 14:16:12 -0700475}
476
477bool VmRef::IsInstance(py::object ref_object_class) {
Ben Vanik09630d62023-04-13 14:21:40 -0700478 auto type = py::cast<iree_vm_ref_type_t>(ref_object_class.attr(kTypeAttr)());
479 return type == ref_.type;
Stella Laurenzo95dff502022-07-31 14:16:12 -0700480}
481
482std::string VmRef::ToString() {
483 if (!ref_.ptr) {
484 return "<VmRef NULL>";
485 }
486 iree_string_view_t type_name = iree_vm_ref_type_name(ref_.type);
487 std::stringstream ss;
488 ss << "<VmRef ";
489 ss.write(type_name.data, type_name.size);
490 ss << " at " << std::hex << "0x" << reinterpret_cast<uintptr_t>(ref_.ptr)
491 << ">";
492 return ss.str();
493}
494
495//------------------------------------------------------------------------------
Stella Laurenzo480e6ef2019-12-23 10:07:52 -0800496// VmVariantList
497//------------------------------------------------------------------------------
498
Stella Laurenzo99956042021-05-14 10:32:38 -0700499void VmVariantList::PushFloat(double fvalue) {
500 // Note that Python floats are f64.
501 iree_vm_value_t value = iree_vm_value_make_f64(fvalue);
502 CheckApiStatus(iree_vm_list_push_value(raw_ptr(), &value),
503 "Could not push float");
504}
505
506void VmVariantList::PushInt(int64_t ivalue) {
507 // Note that Python ints are unbounded, so just use the largest type we
508 // have.
509 iree_vm_value_t value = iree_vm_value_make_i64(ivalue);
510 CheckApiStatus(iree_vm_list_push_value(raw_ptr(), &value),
511 "Could not push int");
512}
513
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700514void VmVariantList::PushList(VmVariantList& other) {
515 iree_vm_ref_t retained = iree_vm_list_retain_ref(other.raw_ptr());
516 iree_vm_list_push_ref_move(raw_ptr(), &retained);
517}
518
Stella Laurenzo7b066572022-07-31 18:38:15 -0700519void VmVariantList::PushRef(py::handle ref_or_object) {
520 py::object py_ref = ref_or_object.attr(VmRef::kRefAttr);
521 VmRef& ref = py::cast<VmRef&>(py_ref);
522 CheckApiStatus(iree_vm_list_push_ref_retain(raw_ptr(), &ref.ref()),
523 "Failed to push ref");
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700524}
525
Stella Laurenzo99956042021-05-14 10:32:38 -0700526py::object VmVariantList::GetAsList(int index) {
Stella Laurenzo5ba8a7a2021-05-11 21:29:23 -0700527 iree_vm_ref_t ref = {0};
528 CheckApiStatus(iree_vm_list_get_ref_assign(raw_ptr(), index, &ref),
529 "Could not access list element");
530 iree_vm_list_t* sub_list = NULL;
531 CheckApiStatus(iree_vm_list_check_deref(ref, &sub_list),
532 "Could not deref list (wrong type?)");
533 iree_vm_list_retain(sub_list);
Stella Laurenzo95dff502022-07-31 14:16:12 -0700534 return py::cast(VmVariantList::StealFromRawPtr(sub_list));
Stella Laurenzo99956042021-05-14 10:32:38 -0700535}
536
537py::object VmVariantList::GetVariant(int index) {
538 iree_vm_variant_t v = iree_vm_variant_empty();
Ben Vanik78fc0e32023-03-06 16:20:08 -0800539 CheckApiStatus(iree_vm_list_get_variant_assign(raw_ptr(), index, &v),
Stella Laurenzo99956042021-05-14 10:32:38 -0700540 "Could not access list element");
Ben Vanik09630d62023-04-13 14:21:40 -0700541 if (iree_vm_variant_is_empty(v)) {
542 return py::none();
543 } else if (iree_vm_variant_is_value(v)) {
Stella Laurenzo99956042021-05-14 10:32:38 -0700544 // Convert a value type.
Ben Vanik09630d62023-04-13 14:21:40 -0700545 switch (iree_vm_type_def_as_value(v.type)) {
Stella Laurenzo99956042021-05-14 10:32:38 -0700546 case IREE_VM_VALUE_TYPE_I8:
547 return py::cast(v.i8);
548 case IREE_VM_VALUE_TYPE_I16:
549 return py::cast(v.i16);
550 case IREE_VM_VALUE_TYPE_I32:
551 return py::cast(v.i32);
552 case IREE_VM_VALUE_TYPE_I64:
553 return py::cast(v.i64);
554 case IREE_VM_VALUE_TYPE_F32:
555 return py::cast(v.f32);
556 case IREE_VM_VALUE_TYPE_F64:
557 return py::cast(v.f64);
558 default:
559 throw RaiseValueError("Unsupported VM value type conversion");
560 }
Stella Laurenzo7b066572022-07-31 18:38:15 -0700561 } else if (iree_vm_variant_is_ref(v)) {
562 VmRef ref;
563 iree_vm_ref_retain(&v.ref, &ref.ref());
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700564 return py::cast(ref, py::rv_policy::move);
Stella Laurenzo99956042021-05-14 10:32:38 -0700565 }
566
567 throw RaiseValueError("Unsupported VM to Python Type Conversion");
Stella Laurenzo5ba8a7a2021-05-11 21:29:23 -0700568}
569
Stella Laurenzo73045212021-06-25 16:18:57 -0700570py::object VmVariantList::GetAsSerializedTraceValue(int index) {
571 iree_vm_variant_t v = iree_vm_variant_empty();
Ben Vanik78fc0e32023-03-06 16:20:08 -0800572 CheckApiStatus(iree_vm_list_get_variant_assign(raw_ptr(), index, &v),
Stella Laurenzo73045212021-06-25 16:18:57 -0700573 "Could not access list element");
Ben Vanik09630d62023-04-13 14:21:40 -0700574 if (iree_vm_variant_is_empty(v)) {
575 py::dict record;
576 record["type"] = "null";
577 return std::move(record);
578 } else if (iree_vm_variant_is_value(v)) {
Stella Laurenzo73045212021-06-25 16:18:57 -0700579 // Convert a value type.
580 py::dict record;
Ben Vanik09630d62023-04-13 14:21:40 -0700581 switch (iree_vm_type_def_as_value(v.type)) {
Stella Laurenzo73045212021-06-25 16:18:57 -0700582 case IREE_VM_VALUE_TYPE_I8:
583 record["i8"] = py::cast(v.i8);
584 break;
585 case IREE_VM_VALUE_TYPE_I16:
586 record["i16"] = py::cast(v.i16);
587 break;
588 case IREE_VM_VALUE_TYPE_I32:
589 record["i32"] = py::cast(v.i32);
590 break;
591 case IREE_VM_VALUE_TYPE_I64:
592 record["i64"] = py::cast(v.i64);
593 break;
594 case IREE_VM_VALUE_TYPE_F32:
595 record["f32"] = py::cast(v.f32);
596 break;
597 case IREE_VM_VALUE_TYPE_F64:
598 record["f64"] = py::cast(v.f64);
599 break;
600 default:
601 throw RaiseValueError("Unsupported VM value type conversion");
602 }
603 record["type"] = py::cast("value");
604 return std::move(record);
Ben Vanik09630d62023-04-13 14:21:40 -0700605 } else if (iree_vm_variant_is_ref(v)) {
Stella Laurenzo73045212021-06-25 16:18:57 -0700606 // Convert reference type.
607 if (iree_vm_list_isa(v.ref)) {
608 py::dict record;
Ben Vanikabe6c762021-06-27 21:57:07 -0700609 record["type"] = "vm.list";
Stella Laurenzo73045212021-06-25 16:18:57 -0700610 py::list items;
611 iree_vm_list_t* sub_list = NULL;
612 CheckApiStatus(iree_vm_list_check_deref(v.ref, &sub_list),
613 "Could not deref list (wrong type?)");
614 iree_vm_list_retain(sub_list);
Stella Laurenzo95dff502022-07-31 14:16:12 -0700615 VmVariantList sub_list_object = VmVariantList::StealFromRawPtr(sub_list);
Stella Laurenzo73045212021-06-25 16:18:57 -0700616 for (int i = 0, e = sub_list_object.size(); i < e; ++i) {
617 items.append(sub_list_object.GetAsSerializedTraceValue(i));
618 }
619 record["items"] = std::move(items);
620 return std::move(record);
621 } else if (iree_hal_buffer_view_isa(v.ref)) {
622 py::dict record;
Ben Vanikabe6c762021-06-27 21:57:07 -0700623 record["type"] = "hal.buffer_view";
Stella Laurenzo73045212021-06-25 16:18:57 -0700624 iree_hal_buffer_view_t* buffer_view = iree_hal_buffer_view_deref(v.ref);
625 if (!buffer_view) {
626 throw RaiseValueError(
627 "Could not deref result buffer view (wrong type?)");
628 }
629 iree_hal_buffer_t* raw_buffer = iree_hal_buffer_view_buffer(buffer_view);
630 if (!raw_buffer) {
631 throw RaiseValueError("Could not deref result buffer (wrong type?)");
632 }
633
634 // Extract dims from the buffer view.
Scott Todd60b07642023-06-15 09:41:01 -0700635 iree_host_size_t rank = 0;
Ben Vanik49fee302022-05-16 21:18:59 -0700636 std::vector<iree_hal_dim_t> dims(6);
Stella Laurenzo73045212021-06-25 16:18:57 -0700637 iree_status_t status = iree_hal_buffer_view_shape(
638 buffer_view, dims.capacity(), dims.data(), &rank);
639 if (iree_status_is_out_of_range(status)) {
640 dims.resize(rank);
641 status = iree_hal_buffer_view_shape(buffer_view, dims.capacity(),
642 dims.data(), &rank);
643 }
644 CheckApiStatus(status, "Error extracting shape");
645 dims.resize(rank);
646 record["shape"] = py::cast(std::move(dims));
647
648 // Element type.
649 iree_hal_element_type_t element_type =
650 iree_hal_buffer_view_element_type(buffer_view);
Ben Vanik40876b92023-03-06 11:26:53 -0800651 char element_type_str[64] = {0};
652 iree_host_size_t element_type_length = 0;
653 CheckApiStatus(
654 iree_hal_format_element_type(element_type, sizeof(element_type_str),
655 element_type_str, &element_type_length),
656 "Formatting element type");
657 record["element_type"] =
658 std::string(element_type_str, element_type_length);
Stella Laurenzo73045212021-06-25 16:18:57 -0700659
660 // Map memory.
661 iree_device_size_t byte_length = iree_hal_buffer_byte_length(raw_buffer);
Ben Vanikeff1bb52021-12-16 11:04:21 -0800662 iree_hal_buffer_mapping_t mapped_memory = {{0}};
Stella Laurenzo73045212021-06-25 16:18:57 -0700663 CheckApiStatus(iree_hal_buffer_map_range(
Ben Vanikb036c632021-12-14 14:21:16 -0800664 raw_buffer, IREE_HAL_MAPPING_MODE_SCOPED,
665 IREE_HAL_MEMORY_ACCESS_READ, 0 /* element_offset */,
666 byte_length, &mapped_memory),
Stella Laurenzo73045212021-06-25 16:18:57 -0700667 "Could not map memory");
668 record["contents"] =
669 py::bytes(reinterpret_cast<const char*>(mapped_memory.contents.data),
670 mapped_memory.contents.data_length);
671 iree_hal_buffer_unmap_range(&mapped_memory);
672
673 return std::move(record);
674 }
675 }
676
677 throw RaiseValueError("Unsupported VM to Python Type Conversion");
678}
679
Stella Laurenzo7b066572022-07-31 18:38:15 -0700680py::object VmVariantList::GetAsRef(int index) {
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700681 iree_vm_variant_t v = iree_vm_variant_empty();
Ben Vanik78fc0e32023-03-06 16:20:08 -0800682 CheckApiStatus(iree_vm_list_get_variant_assign(raw_ptr(), index, &v),
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700683 "Could not access list element");
Stella Laurenzo7b066572022-07-31 18:38:15 -0700684 if (!iree_vm_variant_is_ref(v)) {
685 throw std::invalid_argument("list element is not a ref");
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700686 }
Stella Laurenzo7b066572022-07-31 18:38:15 -0700687 VmRef ref;
688 iree_vm_ref_retain(&v.ref, &ref.ref());
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700689 return py::cast(ref, py::rv_policy::move);
Stella Laurenzo7b066572022-07-31 18:38:15 -0700690}
691
692py::object VmVariantList::GetAsObject(int index, py::object clazz) {
693 return clazz.attr(VmRef::kCastAttr)(GetAsRef(index));
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700694}
695
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700696namespace {
697
Ben Vanikb9b794b2021-06-19 20:11:08 -0700698static std::string ToHexString(const uint8_t* data, size_t length) {
699 static constexpr char kHexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
700 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
701 std::string s(length * 2, ' ');
702 for (size_t i = 0; i < length; ++i) {
703 s[2 * i + 0] = kHexChars[(data[i] & 0xF0) >> 4];
704 s[2 * i + 1] = kHexChars[(data[i] & 0x0F) >> 0];
705 }
706 return s;
707}
708static std::string ToHexString(uint32_t value) {
709 return ToHexString((const uint8_t*)&value, sizeof(value));
710}
711
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700712void AppendListContents(std::string& out, iree_vm_list_t* list,
713 std::unordered_set<iree_vm_list_t*>& visited) {
714 for (iree_host_size_t i = 0, e = iree_vm_list_size(list); i < e; ++i) {
715 iree_vm_variant_t variant = iree_vm_variant_empty();
Ben Vanik78fc0e32023-03-06 16:20:08 -0800716 iree_status_t status = iree_vm_list_get_variant_assign(list, i, &variant);
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700717 if (!iree_status_is_ok(status)) {
718 iree_status_ignore(status);
719 out.append("Error");
720 continue;
721 }
722 if (i > 0) out.append(", ");
723
724 if (iree_vm_variant_is_value(variant)) {
Sean Silvabef60b42021-09-09 20:51:05 -0700725 // Convert a value type to a string.
Ben Vanik09630d62023-04-13 14:21:40 -0700726 switch (iree_vm_type_def_as_value(variant.type)) {
Sean Silvabef60b42021-09-09 20:51:05 -0700727 case IREE_VM_VALUE_TYPE_I8: {
728 out += std::to_string(variant.i8);
729 break;
730 }
731 case IREE_VM_VALUE_TYPE_I16: {
732 out += std::to_string(variant.i16);
733 break;
734 }
735 case IREE_VM_VALUE_TYPE_I32: {
736 out += std::to_string(variant.i32);
737 break;
738 }
739 case IREE_VM_VALUE_TYPE_I64: {
740 out += std::to_string(variant.i64);
741 break;
742 }
743 case IREE_VM_VALUE_TYPE_F32: {
744 out += std::to_string(variant.f32);
745 break;
746 }
747 case IREE_VM_VALUE_TYPE_F64: {
748 out += std::to_string(variant.f64);
749 break;
750 }
751 default:
752 throw RaiseValueError("Unsupported VM value type to string");
753 }
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700754 } else if (iree_vm_variant_is_ref(variant)) {
755 // Pretty print a subset of ABI impacting known types.
756 if (iree_hal_buffer_isa(variant.ref)) {
757 auto* hal_buffer = iree_hal_buffer_deref(variant.ref);
758 assert(hal_buffer);
Ben Vanikb9b794b2021-06-19 20:11:08 -0700759 out += std::string("HalBuffer(") +
760 std::to_string(iree_hal_buffer_byte_length(hal_buffer)) + ")";
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700761 } else if (iree_hal_buffer_view_isa(variant.ref)) {
762 auto hal_bv = iree_hal_buffer_view_deref(variant.ref);
Ben Vanikb9b794b2021-06-19 20:11:08 -0700763 out += "HalBufferView(";
Ben Vanik49fee302022-05-16 21:18:59 -0700764 std::vector<iree_hal_dim_t> shape(
765 iree_hal_buffer_view_shape_rank(hal_bv));
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700766 iree_hal_buffer_view_shape(hal_bv, shape.size(), shape.data(), nullptr);
Ben Vanikb9b794b2021-06-19 20:11:08 -0700767 for (size_t i = 0; i < shape.size(); ++i) {
768 if (i > 0) out += 'x';
769 out += std::to_string(shape[i]);
770 }
771 out += ":0x" +
772 ToHexString(static_cast<uint32_t>(
773 iree_hal_buffer_view_element_type(hal_bv))) +
774 ")";
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700775 } else if (iree_vm_list_isa(variant.ref)) {
776 out.append("List[");
777 iree_vm_list_t* sub_list = iree_vm_list_deref(variant.ref);
778 if (visited.insert(sub_list).second) {
779 AppendListContents(out, sub_list, visited);
780 } else {
781 out.append("...circular...");
782 }
783 out.append("]");
Stanley Winata094d9ba2023-10-23 22:37:55 -0700784 } else if (iree_hal_fence_isa(variant.ref)) {
785 out.append("fence(");
786 auto* hal_fence = iree_hal_fence_deref(variant.ref);
787 iree_host_size_t timepoint_count =
788 iree_hal_fence_timepoint_count(hal_fence);
789 out.append(std::to_string(timepoint_count) + ")");
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700790 } else {
Ben Vanik09630d62023-04-13 14:21:40 -0700791 out += "Unknown(" +
792 std::to_string(iree_vm_type_def_as_ref(variant.type)) + ")";
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700793 }
794 } else {
795 out.append("None");
796 }
797 }
798}
799
800} // namespace
801
Stella Laurenzo480e6ef2019-12-23 10:07:52 -0800802std::string VmVariantList::DebugString() const {
803 // The variant list API requires mutability, so we const cast to it internally
804 // so we can maintain a const DebugString() for callers.
805 auto mutable_this = const_cast<VmVariantList*>(this);
Ben Vanikb9b794b2021-06-19 20:11:08 -0700806 std::string s =
807 std::string("<VmVariantList(") + std::to_string(size()) + "): [";
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700808 iree_vm_list_t* list = mutable_this->raw_ptr();
809 std::unordered_set<iree_vm_list_t*> visited;
810 visited.insert(list);
811 AppendListContents(s, list, visited);
812 s.append("]>");
Stella Laurenzo480e6ef2019-12-23 10:07:52 -0800813 return s;
814}
815
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700816void SetupVmBindings(nanobind::module_ m) {
Ben Vanik4c0a53e2020-06-16 10:33:19 -0700817 py::enum_<enum iree_vm_function_linkage_e>(m, "Linkage")
Stella Laurenzodc513de2019-12-19 10:19:57 -0800818 .value("INTERNAL", IREE_VM_FUNCTION_LINKAGE_INTERNAL)
819 .value("IMPORT", IREE_VM_FUNCTION_LINKAGE_IMPORT)
Ben Vanik79495f42022-04-18 10:49:21 -0700820 .value("IMPORT_OPTIONAL", IREE_VM_FUNCTION_LINKAGE_IMPORT_OPTIONAL)
Stella Laurenzodc513de2019-12-19 10:19:57 -0800821 .value("EXPORT", IREE_VM_FUNCTION_LINKAGE_EXPORT)
Ben Vanik96c9bfb2024-07-08 15:44:51 -0700822 .value("EXPORT_OPTIONAL", IREE_VM_FUNCTION_LINKAGE_EXPORT_OPTIONAL)
Stella Laurenzodc513de2019-12-19 10:19:57 -0800823 .export_values();
824
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700825 auto vm_buffer = py::class_<VmBuffer>(m, "VmBuffer");
Ben Vanik09630d62023-04-13 14:21:40 -0700826 VmRef::BindRefProtocol(vm_buffer, iree_vm_buffer_type,
Stella Laurenzo7b066572022-07-31 18:38:15 -0700827 iree_vm_buffer_retain_ref, iree_vm_buffer_deref,
828 iree_vm_buffer_isa);
Stella Laurenzo4f400802024-04-04 09:55:24 -0700829 BindBufferProtocol<VmBuffer>(vm_buffer);
Stella Laurenzo7b066572022-07-31 18:38:15 -0700830 vm_buffer
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700831 .def(
832 "__init__",
833 [](VmBuffer* self, iree_host_size_t length,
834 iree_host_size_t alignment, bool is_mutable) {
835 iree_vm_buffer_access_t access = 0;
836 if (is_mutable) {
837 access |= IREE_VM_BUFFER_ACCESS_MUTABLE;
838 }
839 iree_vm_buffer_t* raw_buffer;
840 CheckApiStatus(
841 iree_vm_buffer_create(access, length, alignment,
842 iree_allocator_system(), &raw_buffer),
843 "Error creating buffer");
844
845 new (self) VmBuffer();
846 *self = VmBuffer::StealFromRawPtr(raw_buffer);
847 },
848 py::arg("length"), py::arg("alignment") = 0,
849 py::arg("mutable") = true)
Stella Laurenzo7b066572022-07-31 18:38:15 -0700850 .def("__repr__", [](VmBuffer& self) {
851 std::stringstream ss;
852 ss << "<VmBuffer size " << self.raw_ptr()->data.data_length << " at 0x"
853 << std::hex << reinterpret_cast<uintptr_t>(self.raw_ptr()->data.data)
854 << ">";
855 return ss.str();
856 });
857
Stella Laurenzodc513de2019-12-19 10:19:57 -0800858 // Mutation and inspection of the variant list is mostly opaque to python.
Stella Laurenzo95dff502022-07-31 14:16:12 -0700859 auto vm_list = py::class_<VmVariantList>(m, "VmVariantList");
Ben Vanik09630d62023-04-13 14:21:40 -0700860 VmRef::BindRefProtocol(vm_list, iree_vm_list_type, iree_vm_list_retain_ref,
Stella Laurenzo7b066572022-07-31 18:38:15 -0700861 iree_vm_list_deref, iree_vm_list_isa);
Stella Laurenzo95dff502022-07-31 14:16:12 -0700862 vm_list
863 // User Methods.
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700864 .def(
865 "__init__",
866 [](VmVariantList* self, iree_host_size_t capacity) {
867 new (self) VmVariantList();
868 *self = VmVariantList::Create(capacity);
869 },
870 py::arg("capacity"))
871 .def_prop_ro("size", &VmVariantList::size)
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700872 .def("__len__", &VmVariantList::size)
Stella Laurenzo7b066572022-07-31 18:38:15 -0700873 .def("get_as_ref", &VmVariantList::GetAsRef)
874 .def("get_as_object", &VmVariantList::GetAsObject)
Stella Laurenzo5ba8a7a2021-05-11 21:29:23 -0700875 .def("get_as_list", &VmVariantList::GetAsList)
Stella Laurenzo99956042021-05-14 10:32:38 -0700876 .def("get_variant", &VmVariantList::GetVariant)
Stella Laurenzo73045212021-06-25 16:18:57 -0700877 .def("get_serialized_trace_value",
878 &VmVariantList::GetAsSerializedTraceValue)
Stella Laurenzo99956042021-05-14 10:32:38 -0700879 .def("push_float", &VmVariantList::PushFloat)
880 .def("push_int", &VmVariantList::PushInt)
Stella Laurenzo397c4fc2021-05-08 00:19:22 -0700881 .def("push_list", &VmVariantList::PushList)
Stella Laurenzo7b066572022-07-31 18:38:15 -0700882 .def("push_ref", &VmVariantList::PushRef)
Stella Laurenzo480e6ef2019-12-23 10:07:52 -0800883 .def("__repr__", &VmVariantList::DebugString);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800884
885 py::class_<iree_vm_function_t>(m, "VmFunction")
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700886 .def_ro("linkage", &iree_vm_function_t::linkage)
887 .def_ro("ordinal", &iree_vm_function_t::ordinal)
888 .def_prop_ro("name",
889 [](iree_vm_function_t& self) {
890 iree_string_view_t name = iree_vm_function_name(&self);
891 return py::str(name.data, name.size);
892 })
893 .def_prop_ro("module_name",
894 [](iree_vm_function_t& self) {
895 iree_string_view_t name = iree_vm_module_name(self.module);
896 return py::str(name.data, name.size);
897 })
898 .def_prop_ro("reflection",
899 [](iree_vm_function_t& self) {
900 return GetFunctionReflectionDict(self);
901 })
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700902 .def("__repr__", [](iree_vm_function_t& self) {
903 iree_string_view_t name = iree_vm_function_name(&self);
904 std::string repr("<VmFunction ");
905 repr.append(name.data, name.size);
906
907 iree_vm_function_signature_t sig = iree_vm_function_signature(&self);
908 repr.append("(");
909 repr.append(sig.calling_convention.data, sig.calling_convention.size);
910 repr.append("), reflection = ");
911 py::dict reflection = GetFunctionReflectionDict(self);
912 repr.append(py::cast<std::string>(py::repr(reflection)));
913 repr.append(">");
914 return repr;
915 });
Stella Laurenzodc513de2019-12-19 10:19:57 -0800916
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700917 py::class_<VmInstance>(m, "VmInstance").def("__init__", [](VmInstance* self) {
918 new (self) VmInstance();
919 *self = VmInstance::Create();
920 });
Stella Laurenzodc513de2019-12-19 10:19:57 -0800921 py::class_<VmContext>(m, "VmContext")
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700922 .def(
923 "__init__",
924 [](VmContext* self, VmInstance* instance,
925 std::optional<std::vector<VmModule*>> modules) {
926 new (self) VmContext();
927 *self = VmContext::Create(instance, modules);
928 },
929 py::arg("instance"),
930 py::arg("modules") = std::optional<std::vector<VmModule*>>())
Stella Laurenzodc513de2019-12-19 10:19:57 -0800931 .def("register_modules", &VmContext::RegisterModules)
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700932 .def_prop_ro("context_id", &VmContext::context_id)
Stella Laurenzod5efc482019-12-21 04:59:36 -0800933 .def("invoke", &VmContext::Invoke);
Stella Laurenzodc513de2019-12-19 10:19:57 -0800934
935 py::class_<VmModule>(m, "VmModule")
Eugene Zhulenev6f81ceb2023-05-16 19:23:26 -0700936 .def_static("resolve_module_dependency",
937 &VmModule::ResolveModuleDependency)
Stella Laurenzo89a41d92023-06-19 19:17:42 -0700938 .def_static("from_flatbuffer", &VmModule::FromBuffer, py::arg("instance"),
939 py::arg("buffer"), py::arg("warn_if_copy") = true,
940 kFromBufferDocstring)
941 .def_static("from_buffer", &VmModule::FromBuffer, py::arg("instance"),
942 py::arg("buffer"), py::arg("warn_if_copy") = true,
943 kFromBufferDocstring)
944 .def_static("copy_buffer", &VmModule::CopyBuffer, py::arg("instance"),
945 py::arg("buffer"), kCopyBufferDocstring)
946 .def_static("wrap_buffer", &VmModule::WrapBuffer, py::arg("instance"),
947 py::arg("buffer"), py::arg("destroy_callback") = py::none(),
948 py::arg("close_buffer") = false, kWrapBufferDocstring)
949 .def_static("mmap", &VmModule::MMap, py::arg("instance"),
950 py::arg("filepath"), py::arg("destroy_callback") = py::none(),
951 kMMapDocstring)
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700952 .def_prop_ro("name", &VmModule::name)
953 .def_prop_ro("version",
954 [](VmModule& self) {
955 iree_vm_module_signature_t sig =
956 iree_vm_module_signature(self.raw_ptr());
957 return sig.version;
958 })
Stella Laurenzodc513de2019-12-19 10:19:57 -0800959 .def("lookup_function", &VmModule::LookupFunction, py::arg("name"),
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700960 py::arg("linkage") = IREE_VM_FUNCTION_LINKAGE_EXPORT)
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700961 .def_prop_ro(
Stella Laurenzo73045212021-06-25 16:18:57 -0700962 "stashed_flatbuffer_blob",
963 [](VmModule& self) { return self.get_stashed_flatbuffer_blob(); })
Stella Laurenzoabe91fa2023-06-26 16:57:30 -0700964 .def_prop_ro("function_names",
965 [](VmModule& self) {
966 py::list names;
967 iree_vm_module_signature_t sig =
968 iree_vm_module_signature(self.raw_ptr());
969 for (size_t ordinal = 0;
970 ordinal < sig.export_function_count; ++ordinal) {
971 iree_vm_function_t f;
972 auto status = iree_vm_module_lookup_function_by_ordinal(
973 self.raw_ptr(), IREE_VM_FUNCTION_LINKAGE_EXPORT,
974 ordinal, &f);
975 if (iree_status_is_not_found(status)) {
976 iree_status_ignore(status);
977 break;
978 }
979 CheckApiStatus(status, "Error enumerating module");
980 iree_string_view_t fname = iree_vm_function_name(&f);
981 py::str name(fname.data, fname.size);
982 names.append(name);
983 }
984 return names;
985 })
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700986 .def("__repr__", [](VmModule& self) {
987 std::string repr("<VmModule ");
988 iree_string_view_t name = iree_vm_module_name(self.raw_ptr());
989 repr.append(name.data, name.size);
990
991 iree_vm_module_signature_t sig =
992 iree_vm_module_signature(self.raw_ptr());
993 repr.append(" : [");
994 for (size_t ordinal = 0; ordinal < sig.export_function_count;
995 ++ordinal) {
996 iree_vm_function_t f;
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700997 auto status = iree_vm_module_lookup_function_by_ordinal(
Ben Vanik6c4dd5b2021-10-05 15:29:23 -0700998 self.raw_ptr(), IREE_VM_FUNCTION_LINKAGE_EXPORT, ordinal, &f);
Stella Laurenzoaeaaad62021-05-03 13:44:52 -0700999 if (iree_status_is_not_found(status)) {
1000 iree_status_ignore(status);
1001 break;
1002 }
1003 CheckApiStatus(status, "Error enumerating module");
1004 iree_string_view_t fname = iree_vm_function_name(&f);
1005 if (ordinal > 0) {
1006 repr.append(", ");
1007 }
1008 repr.append(fname.data, fname.size);
1009 }
1010 repr.append("]");
1011 repr.append(">");
1012 return repr;
1013 });
Stella Laurenzo95dff502022-07-31 14:16:12 -07001014
1015 py::class_<VmRef>(m, "VmRef")
1016 .def("isinstance", &VmRef::IsInstance)
Stella Laurenzo7b066572022-07-31 18:38:15 -07001017 .def("deref", &VmRef::Deref, py::arg("value"),
1018 py::arg("optional") = false)
Stella Laurenzo95dff502022-07-31 14:16:12 -07001019 .def("__repr__", &VmRef::ToString)
Stella Laurenzoabe91fa2023-06-26 16:57:30 -07001020 .def_prop_ro(VmRef::kRefAttr, [](py::object self) { return self; })
Stella Laurenzo95dff502022-07-31 14:16:12 -07001021 .def("__eq__",
1022 [](VmRef& self, VmRef& other) {
1023 return self.ref().ptr == other.ref().ptr;
1024 })
1025 .def("__eq__", [](VmRef& self, py::object& other) { return false; });
powderluv2e2fc9c2019-10-23 10:09:44 -07001026}
1027
1028} // namespace python
1029} // namespace iree