handle.hpp (14711B)
1 /** @addtogroup base 2 * @{ 3 */ 4 /* 5 Copyright (C) 2016-2023 Dan Cazarin (https://www.kfrlib.com) 6 This file is part of KFR 7 8 KFR is free software: you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation, either version 2 of the License, or 11 (at your option) any later version. 12 13 KFR is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with KFR. 20 21 If GPL is not suitable for your project, you must purchase a commercial license to use KFR. 22 Buying a commercial license is mandatory as soon as you develop commercial activities without 23 disclosing the source code of your own applications. 24 See https://www.kfrlib.com for details. 25 */ 26 #pragma once 27 28 #include "../cometa/memory.hpp" 29 #include "../simd/vec.hpp" 30 #include "basic_expressions.hpp" 31 #include <memory> 32 33 namespace kfr 34 { 35 36 template <typename T, index_t Dims> 37 struct expression_handle; 38 39 template <typename T> 40 constexpr size_t maximum_expression_width = vector_width_for<T, cpu_t::highest> * 2; 41 42 template <typename T, template <T...> typename Tpl, typename Pack> 43 struct expand_cvals; 44 45 template <typename T, template <T...> typename Tpl, T... vals> 46 struct expand_cvals<T, Tpl, cvals_t<T, vals...>> 47 { 48 using type = Tpl<vals...>; 49 }; 50 51 inline namespace CMT_ARCH_NAME 52 { 53 54 namespace internal 55 { 56 57 template <typename Expression, typename T, index_t Dims, size_t key = 0> 58 KFR_INTRINSIC bool invoke_substitute(Expression& expr, expression_handle<T, Dims> new_handle, 59 csize_t<key> = {}); 60 } 61 } // namespace CMT_ARCH_NAME 62 63 template <typename T, index_t Dims> 64 struct expression_vtable 65 { 66 constexpr static const size_t Nsizes = 1 + ilog2(maximum_expression_width<T>); 67 constexpr static const size_t Nmax = 1 << Nsizes; 68 69 using func_get = void (*)(void*, shape<Dims>, T*); 70 using func_set = void (*)(void*, shape<Dims>, const T*); 71 using func_shapeof = void (*)(void*, shape<Dims>&); 72 using func_substitute = bool (*)(void*, expression_handle<T, Dims>); 73 using func_pass = void (*)(void*, shape<Dims>, shape<Dims>); 74 75 func_shapeof fn_shapeof; 76 func_substitute fn_substitute; 77 func_pass fn_begin_pass; 78 func_pass fn_end_pass; 79 std::array<std::array<func_get, Nsizes>, Dims> fn_get_elements; 80 std::array<std::array<func_set, Nsizes>, Dims> fn_set_elements; 81 82 template <typename Expression> 83 KFR_MEM_INTRINSIC expression_vtable(ctype_t<Expression> t) 84 { 85 fn_shapeof = &static_shapeof<Expression>; 86 fn_substitute = &static_substitute<Expression>; 87 fn_begin_pass = &static_begin_pass<Expression>; 88 fn_end_pass = &static_end_pass<Expression>; 89 cforeach(csizeseq<Nsizes>, 90 [&](auto size_) CMT_INLINE_LAMBDA 91 { 92 cforeach(csizeseq<Dims>, 93 [&](auto axis_) CMT_INLINE_LAMBDA 94 { 95 constexpr size_t size = decltype(size_)::value; 96 constexpr size_t axis = decltype(axis_)::value; 97 fn_get_elements[axis][size] = 98 &static_get_elements<Expression, 1 << size, axis>; 99 fn_set_elements[axis][size] = 100 &static_set_elements<Expression, 1 << size, axis>; 101 }); 102 }); 103 } 104 105 template <typename Expression, size_t N, index_t VecAxis> 106 static void static_get_elements(void* instance, shape<Dims> index, T* dest) 107 { 108 if constexpr (is_input_expression<Expression>) 109 { 110 write(dest, get_elements(*static_cast<Expression*>(instance), index, axis_params_v<VecAxis, N>)); 111 } 112 else 113 { 114 } 115 } 116 template <typename Expression, size_t N, index_t VecAxis> 117 static void static_set_elements(void* instance, shape<Dims> index, const T* src) 118 { 119 if constexpr (is_output_expression<Expression>) 120 { 121 set_elements(*static_cast<Expression*>(instance), index, axis_params_v<VecAxis, N>, read<N>(src)); 122 } 123 else 124 { 125 } 126 } 127 template <typename Expression> 128 static void static_shapeof(void* instance, shape<Dims>& result) 129 { 130 result = expression_traits<Expression>::get_shape(*static_cast<Expression*>(instance)); 131 } 132 template <typename Expression> 133 static bool static_substitute(void* instance, expression_handle<T, Dims> ptr) 134 { 135 return internal::invoke_substitute(*static_cast<Expression*>(instance), std::move(ptr)); 136 } 137 template <typename Expression> 138 static void static_begin_pass(void* instance, shape<Dims> start, shape<Dims> stop) 139 { 140 begin_pass(*static_cast<Expression*>(instance), start, stop); 141 } 142 template <typename Expression> 143 static void static_end_pass(void* instance, shape<Dims> start, shape<Dims> stop) 144 { 145 end_pass(*static_cast<Expression*>(instance), start, stop); 146 } 147 }; 148 149 struct expression_resource 150 { 151 virtual ~expression_resource() {} 152 virtual void* instance() { return nullptr; } 153 }; 154 155 template <typename E> 156 struct expression_resource_impl : expression_resource 157 { 158 expression_resource_impl(E&& e) CMT_NOEXCEPT : e(std::move(e)) {} 159 virtual ~expression_resource_impl() {} 160 KFR_INTRINSIC virtual void* instance() override final { return &e; } 161 162 public: 163 #ifdef __cpp_aligned_new 164 static void operator delete(void* p, std::align_val_t al) noexcept { details::aligned_release(p); } 165 #endif 166 167 private: 168 E e; 169 }; 170 171 template <typename E> 172 KFR_INTRINSIC std::shared_ptr<expression_resource> make_resource(E&& e) 173 { 174 using T = expression_resource_impl<std::decay_t<E>>; 175 return std::static_pointer_cast<expression_resource>(std::shared_ptr<T>( 176 new (aligned_allocate<T>()) T(std::move(e)), [](T* pi) { aligned_deallocate<T>(pi); })); 177 } 178 179 template <typename T, index_t Dims = 1> 180 struct expression_handle 181 { 182 void* instance; 183 const expression_vtable<T, Dims>* vtable; 184 std::shared_ptr<expression_resource> resource; 185 186 expression_handle() CMT_NOEXCEPT : instance(nullptr), vtable(nullptr) {} 187 expression_handle(const void* instance, const expression_vtable<T, Dims>* vtable, 188 std::shared_ptr<expression_resource> resource = nullptr) 189 : instance(const_cast<void*>(instance)), vtable(vtable), resource(std::move(resource)) 190 { 191 } 192 193 explicit operator bool() const { return instance != nullptr; } 194 195 bool substitute(expression_handle<T, Dims> new_handle) 196 { 197 return vtable->fn_substitute(instance, std::move(new_handle)); 198 } 199 }; 200 201 template <typename T, index_t Dims> 202 struct expression_traits<expression_handle<T, Dims>> : expression_traits_defaults 203 { 204 using value_type = T; 205 constexpr static size_t dims = Dims; 206 constexpr static shape<dims> get_shape(const expression_handle<T, Dims>& self) 207 { 208 shape<dims> result; 209 self.vtable->fn_shapeof(self.instance, result); 210 return result; 211 } 212 constexpr static shape<dims> get_shape() { return shape<dims>(undefined_size); } 213 214 constexpr static inline bool random_access = false; 215 }; 216 217 inline namespace CMT_ARCH_NAME 218 { 219 220 template <typename T, index_t NDims> 221 KFR_INTRINSIC void begin_pass(const expression_handle<T, NDims>& self, shape<NDims> start, shape<NDims> stop) 222 { 223 self.vtable->fn_begin_pass(self.instance, start, stop); 224 } 225 template <typename T, index_t NDims> 226 KFR_INTRINSIC void end_pass(const expression_handle<T, NDims>& self, shape<NDims> start, shape<NDims> stop) 227 { 228 self.vtable->fn_end_pass(self.instance, start, stop); 229 } 230 231 template <typename T, index_t NDims, index_t Axis, size_t N> 232 KFR_INTRINSIC vec<T, N> get_elements(const expression_handle<T, NDims>& self, const shape<NDims>& index, 233 const axis_params<Axis, N>& sh) 234 { 235 static_assert(is_poweroftwo(N) && N >= 1); 236 constexpr size_t Nsize = ilog2(N); 237 if constexpr (Nsize >= expression_vtable<T, NDims>::Nsizes) 238 { 239 constexpr size_t Nhalf = N / 2; 240 auto low = get_elements(self, index, axis_params_v<Axis, Nhalf>); 241 auto high = get_elements(self, index.add_at(Nhalf, cval<index_t, Axis>), axis_params_v<Axis, Nhalf>); 242 return concat(low, high); 243 } 244 else 245 { 246 portable_vec<T, N> result; 247 self.vtable->fn_get_elements[Axis][Nsize](self.instance, index, result.elem); 248 return result; 249 } 250 } 251 252 template <typename T, index_t NDims, index_t Axis, size_t N> 253 KFR_INTRINSIC void set_elements(const expression_handle<T, NDims>& self, const shape<NDims>& index, 254 const axis_params<Axis, N>& sh, const identity<vec<T, N>>& value) 255 { 256 static_assert(is_poweroftwo(N) && N >= 1); 257 constexpr size_t Nsize = ilog2(N); 258 if constexpr (Nsize >= expression_vtable<T, NDims>::Nsizes) 259 { 260 constexpr size_t Nhalf = N / 2; 261 set_elements(self, index, axis_params_v<Axis, Nhalf>, slice<0, Nhalf>(value)); 262 set_elements(self, index.add_at(Nhalf, cval<index_t, Axis>), axis_params_v<Axis, Nhalf>, 263 slice<Nhalf, Nhalf>(value)); 264 } 265 else 266 { 267 self.vtable->fn_set_elements[Axis][Nsize](self.instance, index, &value.front()); 268 } 269 } 270 } // namespace CMT_ARCH_NAME 271 272 inline namespace CMT_ARCH_NAME 273 { 274 275 namespace internal 276 { 277 278 template <typename T, index_t Dims, typename E> 279 KFR_INTRINSIC expression_vtable<T, Dims>* make_expression_vtable() 280 { 281 static expression_vtable<T, Dims> vtable{ ctype_t<std::decay_t<E>>{} }; 282 return &vtable; 283 } 284 } // namespace internal 285 286 } // namespace CMT_ARCH_NAME 287 288 /** @brief Converts the given expression into an opaque object. 289 * This overload takes reference to the expression. 290 * @warning Use with caution with local variables. 291 */ 292 template <typename E, typename T = expression_value_type<E>, index_t Dims = expression_dims<E>> 293 KFR_INTRINSIC expression_handle<T, Dims> to_handle(E& expr) 294 { 295 return expression_handle<T>(std::addressof(expr), internal::make_expression_vtable<T, Dims, E>()); 296 } 297 298 /** @brief Converts the given expression into an opaque object. 299 * This overload takes ownership of the expression (Move semantics). 300 * @note Use std::move to force use of this overload. 301 */ 302 template <typename E, typename T = expression_value_type<E>, index_t Dims = expression_dims<E>> 303 KFR_INTRINSIC expression_handle<T, Dims> to_handle(E&& expr) 304 { 305 std::shared_ptr<expression_resource> ptr = make_resource(std::move(expr)); 306 void* instance = ptr->instance(); 307 return expression_handle<T, Dims>(instance, internal::make_expression_vtable<T, Dims, E>(), 308 std::move(ptr)); 309 } 310 311 template <typename T, index_t Dims = 1, size_t Key = 0> 312 struct expression_placeholder 313 { 314 public: 315 using value_type = T; 316 expression_placeholder() CMT_NOEXCEPT = default; 317 expression_handle<T, Dims> handle; 318 }; 319 320 template <typename T, index_t Dims, size_t Key> 321 struct expression_traits<expression_placeholder<T, Dims, Key>> : public expression_traits_defaults 322 { 323 using value_type = T; 324 constexpr static size_t dims = Dims; 325 constexpr static shape<dims> get_shape(const expression_placeholder<T, Dims, Key>& self) 326 { 327 return self.handle ? ::kfr::get_shape(self.handle) : shape<dims>(infinite_size); 328 } 329 constexpr static shape<dims> get_shape() { return shape<dims>(undefined_size); } 330 }; 331 332 inline namespace CMT_ARCH_NAME 333 { 334 335 template <typename T, index_t Dims, size_t Key, index_t VecAxis, size_t N> 336 KFR_INTRINSIC vec<T, N> get_elements(const expression_placeholder<T, Dims, Key>& self, shape<Dims> index, 337 axis_params<VecAxis, N> sh) 338 { 339 return self.handle ? get_elements(self.handle, index, sh) : 0; 340 } 341 } // namespace CMT_ARCH_NAME 342 343 template <typename T, index_t Dims = 1, size_t Key = 0> 344 KFR_INTRINSIC expression_placeholder<T, Dims, Key> placeholder(csize_t<Key> = csize_t<Key>{}) 345 { 346 return expression_placeholder<T, Dims, Key>(); 347 } 348 349 template <typename... Args> 350 KFR_INTRINSIC bool substitute(const internal_generic::anything&, Args&&...) 351 { 352 return false; 353 } 354 355 inline namespace CMT_ARCH_NAME 356 { 357 namespace internal 358 { 359 template <typename... Args, typename T, index_t Dims, size_t Key, size_t... indices> 360 KFR_INTRINSIC bool substitute_helper(expression_with_arguments<Args...>& expr, 361 expression_handle<T, Dims> new_handle, csize_t<Key>, 362 csizes_t<indices...>); 363 } 364 } // namespace CMT_ARCH_NAME 365 366 template <typename T, index_t Dims, size_t Key = 0> 367 KFR_INTRINSIC bool substitute(expression_placeholder<T, Dims, Key>& expr, 368 expression_handle<T, Dims> new_handle, csize_t<Key> = csize_t<Key>{}) 369 { 370 expr.handle = std::move(new_handle); 371 return true; 372 } 373 374 template <typename... Args, typename T, index_t Dims, size_t Key = 0> 375 KFR_INTRINSIC bool substitute(expression_with_arguments<Args...>& expr, expression_handle<T, Dims> new_handle, 376 csize_t<Key> = csize_t<Key>{}) 377 { 378 return internal::substitute_helper(expr, std::move(new_handle), csize_t<Key>{}, indicesfor_t<Args...>{}); 379 } 380 381 template <typename T, index_t Dims, size_t Key = 0> 382 KFR_INTRINSIC bool substitute(expression_handle<T, Dims>& expr, expression_handle<T, Dims> new_handle, 383 csize_t<Key> = csize_t<Key>{}) 384 { 385 static_assert(Key == 0, "expression_handle supports only Key = 0"); 386 return expr.substitute(std::move(new_handle)); 387 } 388 389 inline namespace CMT_ARCH_NAME 390 { 391 namespace internal 392 { 393 394 template <typename... Args, typename T, index_t Dims, size_t Key, size_t... indices> 395 KFR_INTRINSIC bool substitute_helper(expression_with_arguments<Args...>& expr, 396 expression_handle<T, Dims> new_handle, csize_t<Key>, 397 csizes_t<indices...>) 398 { 399 return (substitute(std::get<indices>(expr.args), std::move(new_handle), csize_t<Key>()) || ...); 400 } 401 402 template <typename Expression, typename T, index_t Dims, size_t Key> 403 KFR_INTRINSIC bool invoke_substitute(Expression& expr, expression_handle<T, Dims> new_handle, csize_t<Key>) 404 { 405 return kfr::substitute(expr, std::move(new_handle), csize_t<Key>{}); 406 } 407 408 } // namespace internal 409 410 } // namespace CMT_ARCH_NAME 411 412 } // namespace kfr