#pragma once #include #include #include #include #include #include #include #include #include #include namespace host::ffi { using tvm::ffi::Tensor, tvm::ffi::TensorView, tvm::ffi::ShapeView; inline Tensor empty(ShapeView shape, DLDataType dtype, DLDevice device) { return Tensor::FromEnvAlloc(::TVMFFIEnvTensorAlloc, shape, dtype, device); } inline Tensor empty_like(TensorView tensor) { return empty(tensor.shape(), tensor.dtype(), tensor.device()); } struct _dummy_deleter { void operator()(void*) const {} }; // template template struct FromBlobContext { [[no_unique_address]] Fn deleter; int64_t dimension; int64_t* get_shape() { return reinterpret_cast(this + 1); } int64_t* get_stride() { return this->get_shape() + dimension; } }; template inline Tensor from_blob( void* data, ShapeView shape, DLDataType dtype, DLDevice device, Fn&& deleter = {}, std::optional stride = {}, uint64_t byte_offset = 0) { using Context = FromBlobContext>; const auto ndim = shape.size(); const auto ctx = [&] { auto ptr = std::malloc(sizeof(Context) + sizeof(int64_t) * ndim * 2); auto ctx = static_cast(ptr); std::construct_at(ctx, std::forward(deleter), static_cast(ndim)); stdr::copy_n(shape.data(), ndim, ctx->get_shape()); if (stride.has_value()) { RuntimeCheck(stride->size() == ndim, "Stride ndim mismatch!"); stdr::copy_n(stride->data(), ndim, ctx->get_stride()); } else { int64_t stride_val = 1; for (const auto i : irange(ndim)) { const auto j = ndim - 1 - i; ctx->get_stride()[j] = stride_val; stride_val *= shape[j]; } } return ctx; }(); const auto tensor = DLTensor{ .data = data, .device = device, .ndim = static_cast(ndim), .dtype = dtype, .shape = ctx->get_shape(), .strides = ctx->get_stride(), .byte_offset = byte_offset, }; const auto blob_deleter = [](DLManagedTensor* self) { auto ctx = static_cast(self->manager_ctx); ctx->deleter(self->dl_tensor.data); std::destroy_at(ctx); std::free(ctx); }; auto managed_tensor = DLManagedTensor{tensor, ctx, blob_deleter}; return Tensor::FromDLPack(&managed_tensor); } template inline Tensor from_blob_like( void* data, TensorView t, Fn&& deleter = {}, bool is_contiguous = false, // if override to true, the stride will be ignored uint64_t byte_offset = 0) { const auto stride = is_contiguous ? std::nullopt : std::optional{t.strides()}; return from_blob(data, t.shape(), t.dtype(), t.device(), std::forward(deleter), stride, byte_offset); } } // namespace host::ffi