{"id":18388592,"url":"https://github.com/mattkretz/vir-simd","last_synced_at":"2025-07-02T09:05:24.245Z","repository":{"id":79250245,"uuid":"536945709","full_name":"mattkretz/vir-simd","owner":"mattkretz","description":"improve the usage experience of std::experimental::simd (Parallelism TS 2)","archived":false,"fork":false,"pushed_at":"2025-06-13T13:32:31.000Z","size":909,"stargazers_count":28,"open_issues_count":1,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-13T14:44:56.038Z","etag":null,"topics":["cpp","cpp17-library","parallelism-ts","simd","simd-library"],"latest_commit_sha":null,"homepage":"https://mattkretz.github.io/vir-simd/master/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattkretz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-15T09:04:32.000Z","updated_at":"2025-06-13T13:32:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"bacc9a69-9516-4026-9cd3-97dce1fc21a8","html_url":"https://github.com/mattkretz/vir-simd","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/mattkretz/vir-simd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattkretz%2Fvir-simd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattkretz%2Fvir-simd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattkretz%2Fvir-simd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattkretz%2Fvir-simd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattkretz","download_url":"https://codeload.github.com/mattkretz/vir-simd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattkretz%2Fvir-simd/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263108810,"owners_count":23415005,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp17-library","parallelism-ts","simd","simd-library"],"created_at":"2024-11-06T01:34:58.887Z","updated_at":"2025-07-02T09:05:24.234Z","avatar_url":"https://github.com/mattkretz.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vir::stdx::simd\n\n[![Conan Center](https://img.shields.io/conan/v/vir-simd)](https://conan.io/center/recipes/vir-simd)\n[![GCC](https://github.com/mattkretz/vir-simd/actions/workflows/GCC.yml/badge.svg)](https://github.com/mattkretz/vir-simd/actions/workflows/GCC.yml)\n[![Clang](https://github.com/mattkretz/vir-simd/actions/workflows/Clang.yml/badge.svg)](https://github.com/mattkretz/vir-simd/actions/workflows/Clang.yml)\n[![MSVC](https://github.com/mattkretz/vir-simd/actions/workflows/MSVC.yml/badge.svg)](https://github.com/mattkretz/vir-simd/actions/workflows/MSVC.yml)\n[![Emscripten](https://github.com/mattkretz/vir-simd/actions/workflows/Emscripten.yml/badge.svg)](https://github.com/mattkretz/vir-simd/actions/workflows/Emscripten.yml)\n[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7789153.svg)](https://doi.org/10.5281/zenodo.7789153)\n[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/6916/badge)](https://bestpractices.coreinfrastructure.org/projects/6916)\n[![REUSE status](https://github.com/mattkretz/vir-simd/actions/workflows/reuse.yml/badge.svg)](https://github.com/mattkretz/vir-simd/actions/workflows/reuse.yml)\n[![fair-software.eu](https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green)](https://fair-software.eu)\n\nThis project aims to provide a fallback std::experimental::simd (Parallelism TS 2)\nimplementation with additional features. Not every user can rely on GCC 11+ \nand its standard library to be present on all target systems. Therefore, the \nheader `vir/simd.h` provides a fallback implementation of the TS specification \nthat only implements the `scalar` and `fixed_size\u003cN\u003e` ABI tags. Thus, your code \ncan still compile and run correctly, even if it is missing the performance \ngains a proper implementation provides.\n\n## Table of Contents\n\n* [Installation](#installation)\n* [Usage](#usage)\n* [Options](#options)\n* [Additional Features](#additional-features)\n  - [Simple iota `simd` constants](#simple-iota-simd-constants)\n  - [Making `simd` conversions more \n    convenient](#making-simd-conversions-more-convenient)\n  - [Permutations](#permutations-paper)\n  - [SIMD execution policy](#simd-execution-policy-p0350)\n    + [Usable algorithms](#usable-algorithms)\n    + [Example](#example)\n    + [Execution policy modifiers](#execution-policy-modifiers)\n  - [Bitwise operators for floating-point \n    `simd`](#bitwise-operators-for-floating-point-simd)\n  - [Conversion between `std::bitset` and \n    `simd_mask`](#conversion-between-stdbitset-and-simd_mask)\n  - [vir::simd_resize and \n    vir::simd_size_cast](#virsimd_resize-and-virsimd_size_cast)\n  - [vir::simd_bit_cast](#virsimd_bit_cast)\n  - [Concepts](#concepts)\n  - [simdize type transformation](#simdize-type-transformation)\n  - [Benchmark support functions](#benchmark-support-functions)\n  - [`constexpr_wrapper`: function arguments as constant \n    expressions](#constexpr_wrapper-function-arguments-as-constant-expressions)\n    + [Example](#example-1)\n  - [Testing for the version of the vir::stdx::simd (vir-simd) \n    library](#testing-for-the-version-of-the-virstdxsimd-vir-simd-library)\n    + [Semantics of version numbers](#semantics-of-version-numbers)\n  - [Debugging](#debugging)\n\n## Installation\n\nThis is a header-only library. Installation is a simple copy of the headers to \nwherever you want them. Per default `make install` copies the headers into \n`/usr/local/include/vir/`.\n\nExamples:\n```sh\n# installs to $HOME/.local/include/vir\nmake install prefix=~/.local\n\n# installs to $HOME/src/myproject/3rdparty/vir\nmake install includedir=~/src/myproject/3rdparty\n```\n\n\n## Usage\n\n```c++\n#include \u003cvir/simd.h\u003e\n\nnamespace stdx = vir::stdx;\n\nusing floatv = stdx::native_simd\u003cfloat\u003e;\n// ...\n```\n\nThe `vir/simd.h` header will include `\u003cexperimental/simd\u003e` if it is available, \nso you don't have to add any buildsystem support. It should just work.\n\n\n## Options\n\n* `VIR_SIMD_TS_DROPIN`: Define the macro `VIR_SIMD_TS_DROPIN` before including \n`\u003cvir/simd.h\u003e` to define everything in the namespace specified in the \nParallelism TS 2 (namely `std::experimental::parallelism_v2`).\n\n* `VIR_DISABLE_STDX_SIMD`: Do not include `\u003cexperimental/simd\u003e` even if it is \navailable. This allows compiling your code with the `\u003cvir/simd.h\u003e` \nimplementation unconditionally. This is useful for testing.\n\n\n## Additional Features\n\nThe TS curiously forgot to add `simd_cast` and `static_simd_cast` overloads for \n`simd_mask`. With `vir::stdx::(static_)simd_cast`, casts will also work for \n`simd_mask`. This does not require any additional includes.\n\n### Simple iota `simd` constants\n\n*Requires Concepts (C++20).*\n\n```c++\n#include \u003cvir/simd_iota.h\u003e\n\nconstexpr auto a = vir::iota_v\u003cstdx::simd\u003cfloat\u003e\u003e * 3; // 0, 3, 6, 9, ...\n```\n\nThe variable template `vir::iota_v\u003cT\u003e` can be instantiated with arithmetic \ntypes, array types (`std::array` and C-arrays), and `simd` types. In all cases, \nthe elements of the variable will be initialized to `0, 1, 2, 3, 4, ...`, \ndepending on the number of elements in `T`. For arithmetic types \n`vir::iota_v\u003cT\u003e` is always just `0`.\n\n### Making `simd` conversions more convenient\n\n*Requires Concepts (C++20).*\n\nThe TS is way too strict about conversions, requiring verbose \n`std::experimental::static_simd_cast\u003cT\u003e(x)` instead of a concise `T(x)` or \n`static_cast\u003cT\u003e(x)`. (`std::simd` in C++26 will fix this.)\n\n`vir::cvt(x)` provides a tool to make `x` implicitly convertible into whatever \nthe expression wants in order to be well-formed. This only works, if there is \nan unambiguous type that is required.\n\n```c++\n#include \u003cvir/simd_cvt.h\u003e\n\nusing floatv = stdx::native_simd\u003cfloat\u003e;\nusing intv = stdx::rebind_simd_t\u003cint, floatv\u003e;\n\nvoid f(intv x) {\n  using vir::cvt;\n  // the floatv constructor and intv assignment operator clearly determine the\n  // destination type:\n  x = cvt(10 * sin(floatv(cvt(x))));\n\n  // without vir::cvt, one would have write:\n  x = stdx::static_simd_cast\u003cintv\u003e(10 * sin(stdx::static_simd_cast\u003cfloatv\u003e(x)));\n\n  // probably don't do this too often:\n  auto y = cvt(x); // y is a const-ref to x, but so much more convertible\n                   // y is of type cvt\u003cintv\u003e\n}\n```\n\nNote that `vir::cvt` also works for `simd_mask` and non-`simd` types. Thus, \n`cvt` becomes an important building block for writing \"`simd`-generic\" code \n(i.e. well-formed for `T` and `simd\u003cT\u003e`).\n\n\n### Permutations ([paper](https://wg21.link/P2664))\n\n*Requires Concepts (C++20).*\n\n```c++\n#include \u003cvir/simd_permute.h\u003e\n\n// v = {0, 1, 2, 3} -\u003e {1, 0, 3, 2}\nvir::simd_permute(v, vir::simd_permutations::swap_neighbors);\n\n// v = {1, 2, 3, 4} -\u003e {2, 2, 2, 2}\nvir::simd_permute(v, [](unsigned) { return 1; });\n\n// v = {1, 2, 3, 4} -\u003e {3, 3, 3, 3}\nvir::simd_permute(v, [](unsigned) { return -2; });\n```\n\nThe following permutations are pre-defined:\n\n* `vir::simd_permutations::duplicate_even`: copy values at even indices to \n  neighboring odd position\n\n* `vir::simd_permutations::duplicate_odd`: copy values at odd indices to \n  neighboring even position\n\n* `vir::simd_permutations::swap_neighbors\u003cN\u003e`: swap `N` consecutive values with \nthe following `N` consecutive values\n\n* `vir::simd_permutations::broadcast\u003cIdx\u003e`: copy the value at index `Idx` to \nall other values\n\n* `vir::simd_permutations::broadcast_first`: alias for `broadcast\u003c0\u003e`\n\n* `vir::simd_permutations::broadcast_last`: alias for `broadcast\u003c-1\u003e`\n\n* `vir::simd_permutations::reverse`: reverse the order of all values\n\n* `vir::simd_permutations::rotate\u003cOffset\u003e`: positive `Offset` rotates values to \n  the left, negative `Offset` rotates values to the right (i.e. \n  `rotate\u003cOffset\u003e` moves values from index `(i + Offset) % size` to `i`)\n\n* `vir::simd_permutations::shift\u003cOffset\u003e`: positive `Offset` shifts values to \n  the left, negative `Offset` shifts values to the right; shifting in zeros.\n\nA `vir::simd_permute(x, idx_perm)` overload, where `x` is of *vectorizable* \ntype, is also included, facilitating generic code.\n\nA special permutation `vir::simd_shift_in\u003cN\u003e(x, ...)` shifts by N elements \nshifting in elements from additional `simd` objects passed via the pack. \nExample:\n```c++\n// v = {1, 2, 3, 4}, w = {5, 6, 7, 8} -\u003e {2, 3, 4, 5}\nvir::simd_shift_in\u003c1\u003e(v, w);\n```\n\n### SIMD execution policy ([P0350](https://wg21.link/P0350))\n\n*Requires Concepts (C++20).*\n\nAdds an execution policy `vir::execution::simd`. The execution policy can be \nused with the algorithms implemented in the `vir` namespace. These algorithms \nare additionally overloaded in the `std` namespace.\n\nAt this point, the implementation of the execution policy requires contiguous \nranges / iterators.\n\n#### Usable algorithms\n\n* `std::for_each` / `vir::for_each`\n* `std::count_if` / `vir::count_if`\n* `std::transform` / `vir::transform`\n* `std::transform_reduce` / `vir::transform_reduce`\n* `std::reduce` / `vir::reduce`\n\n#### Example\n\n```c++\n#include \u003cvir/simd_execution.h\u003e\n\nvoid increment_all(std::vector\u003cfloat\u003e data) {\n  std::for_each(vir::execution::simd, data.begin(), data.end(),\n    [](auto\u0026 v) {\n      v += 1.f;\n    });\n}\n\n// or\n\nvoid increment_all(std::vector\u003cfloat\u003e data) {\n  vir::for_each(vir::execution::simd, data,\n    [](auto\u0026 v) {\n      v += 1.f;\n    });\n}\n```\n\n#### Execution policy modifiers\n\nThe `vir::execution::simd` execution policy supports a few settings modifying \nits behavior:\n\n* `vir::execution::simd.prefer_size\u003cN\u003e()`:\n  Start with chunking the range into parts of `N` elements, calling the \n  user-supplied function(s) with objects of type `resize_simd_t\u003cN, simd\u003cT\u003e\u003e`. \n\n* `vir::execution::simd.unroll_by\u003cM\u003e()`:\n  Iterate over the range in chunks of `simd::size() * M` instead of just \n  `simd::size()`. The algorithm will execute `M` loads (or stores) together \n  before/after calling the user-supplied function(s). The user-supplied \n  function may be called with `M` `simd` objects instead of one `simd` object. \n  Note that prologue and epilogue will typically still call the user-supplied \n  function with a single `simd` object.\n  Algorithms like `std::count_if` require a return value from the user-supplied \n  function and therefore still call the function with a single `simd` (to avoid \n  the need for returning an `array` or `tuple` of `simd_mask`). Such algorithms \n  will still make use of unrolling inside their implementation.\n\n* `vir::execution::simd.assume_matching_size()`:\n  Add a precondition to the algorithm, that the given range size is a multiple \n  of the SIMD width (but not the SIMD width multiplied by the above unroll \n  factor). This modifier is only valid without prologue (the following two \n  modifiers). The algorithm consequently does not implement an epilogue and all \n  given callables are called with a single simd type (same width and ABI tag). \n  This can reduce code size significantly.\n\n* `vir::execution::simd.prefer_aligned()`:\n  Unconditionally iterate using smaller chunks, until the main iteration can \n  load (and store) chunks from/to aligned addresses. This can be more efficient \n  if the range is large, avoiding cache-line splits. (e.g. with AVX-512, \n  unaligned iteration leads to cache-line splits on every iteration; with AVX \n  on every second iteration)\n\n* `vir::execution::simd.auto_prologue()`\n  (still testing its viability, may be removed):\n  Determine from run-time information (i.e. add a branch) whether a prologue \n  for alignment of the main chunked iteration might be more efficient.\n\n### Bitwise operators for floating-point `simd`\n\n```c++\n#include \u003cvir/simd_float_ops.h\u003e\n\nusing namespace vir::simd_float_ops;\n```\nThen the `\u0026`, `|`, and `^` binary operators can be used with objects of type \n`simd\u003c`floating-point`, A\u003e`.\n\n\n### Conversion between `std::bitset` and `simd_mask`\n\n```c++\n#include \u003cvir/simd_bitset.h\u003e\n\nvir::stdx::simd_mask\u003cint\u003e k;\nstd::bitset b = vir::to_bitset(k);\nvir::stdx::simd_mask k2 = vir::to_simd_mask\u003cfloat\u003e;\n```\n\nThere are two overloads of `vir::to_simd_mask`:\n```c++\nto_simd_mask\u003cT, A\u003e(bitset\u003csimd_size_v\u003cT, A\u003e\u003e)\n```\nand\n```c++\nto_simd_mask\u003cT, N\u003e(bitset\u003cN\u003e)\n```\n\n\n### vir::simd_resize and vir::simd_size_cast\n\nThe header\n```c++\n#include \u003cvir/simd_resize.h\u003e\n```\ndeclares the functions\n\n* `vir::simd_resize\u003cN\u003e(simd)`,\n\n* `vir::simd_resize\u003cN\u003e(simd_mask)`,\n\n* `vir::simd_size_cast\u003cV\u003e(simd)`, and\n\n* `vir::simd_size_cast\u003cM\u003e(simd_mask)`.\n\nThese functions can resize a given `simd` or `simd_mask` object. If the return \ntype requires more elements than the input parameter, the new elements are \ndefault-initialized and appended at the end. Both functions do not allow a \nchange of the `value_type`. However, implicit conversions can happen on \nparameter passing to `simd_size_cast`.\n\n\n### vir::simd_bit_cast\n\nThe header\n```c++\n#include \u003cvir/simd_bit.h\u003e\n```\ndeclares the function `vir::simd_bit_cast\u003cTo\u003e(from)`. This function serves the \nsame purpose as `std::bit_cast` but additionally works in cases where a `simd` \ntype is not trivially copyable.\n\n\n### Concepts\n\n*Requires Concepts (C++20).*\n\nThe header\n```c++\n#include \u003cvir/simd_concepts.h\u003e\n```\ndefines the following concepts:\n\n* `vir::arithmetic\u003cT\u003e`: What `std::arithmetic\u003cT\u003e` should be: satisfied if `T` \n  is an arithmetic type (as specified by the C++ core language).\n\n* `vir::vectorizable\u003cT\u003e`: Satisfied if `T` is a valid element type for \n  `stdx::simd` and `stdx::simd_mask`.\n\n* `vir::simd_abi_tag\u003cT\u003e`: Satisfied if `T` is a valid ABI tag for `stdx::simd` \n  and `stdx::simd_mask`.\n\n* `vir::any_simd\u003cV\u003e`: Satisfied if `V` is a specialization of `stdx::simd\u003cT, \n  Abi\u003e` and the types `T` and `Abi` satisfy `vir::vectorizable\u003cT\u003e` and \n  `vir::simd_abi_tag\u003cAbi\u003e`.\n\n* `vir::any_simd_mask\u003cV\u003e`: Analogue to `vir::any_simd\u003cV\u003e` for `stdx::simd_mask` \n  instead of `stdx::simd`.\n\n* `vir::typed_simd\u003cV, T\u003e`: Satisfied if `vir::any_simd\u003cV\u003e` and `T` is the \n  element type of `V`.\n\n* `vir::sized_simd\u003cV, Width\u003e`: Satisfied if `vir::any_simd\u003cV\u003e` and `Width` is \n  the width of `V`.\n\n* `vir::sized_simd_mask\u003cV, Width\u003e`: Analogue to `vir::sized_simd\u003cV, Width\u003e` for \n  `stdx::simd_mask` instead of `stdx::simd`.\n\n\n### simdize type transformation\n\n*Requires Concepts (C++20).*\n\n:warning: consider this interface under :construction:\n\nThe header\n```c++\n#include \u003cvir/simdize.h\u003e\n```\ndefines the following types and constants:\n\n* `vir::simdize\u003cT, N\u003e`: `N` is optional. Type alias for a `simd` or \n  `vir::simd_tuple` type determined from the type `T`.\n\n  - If `vir::vectorizable\u003cT\u003e` is satisfied, then `stdx::simd\u003cT, Abi\u003e` is \n    produced. `Abi` is determined from `N` and will be `simd_abi::native\u003cT\u003e` if \n    `N` was omitted.\n\n  - If `T` is a `std::tuple` or aggregate that can be reflected, then a \n    specialization of `vir::simd_tuple` is produced. If `T` is a template \n    specialization (without NTTPs), the metafunction tries vectorization via \n    applying `simdize` to all template arguments. If this doesn't yield the \n    same data structure layout as member-only vectorization, then the type \n    behaves similar to a `std::tuple` with additional API to make the type \n    similar to `stdx::simd` (see below).\n    This specialization will be derived from `std::tuple` and the tuple \n    elements will either be `vir::simd_tuple` or `stdx::simd` types. \n    `vir::simdize` is applied recursively to the `std::tuple`/aggregate data \n    members.\n\n  - Otherwise, `T` cannot be simdized (e.g. void, no data members, \n    `std::tuple\u003c\u003e`) then no transformation is applied and `simdize\u003cT\u003e` is an \n    alias for `T`.\n\n  - If `N` was omitted, the resulting width of *all* `simd` types in the \n    resulting type will match the largest `native_simd` width.\n\n  Example: `vir::simdize\u003cstd::tuple\u003cdouble, short\u003e\u003e` produces a tuple with the \n  element types `stdx::rebind_simd_t\u003cdouble, stdx::native_simd\u003cshort\u003e\u003e` and\n  `stdx::native_simd\u003cshort\u003e`.\n\n* `vir::simd_tuple\u003creflectable_struct T, size_t N\u003e`: Don't use this class \n  template directly. Let `vir::simdize` instantiate specializations of this \n  class template. `vir::simd_tuple` mostly behaves like a `std::tuple` and adds \n  the following interface on top of `std::tuple`:\n\n  - `value_type`\n\n  - `mask_type`\n\n  - `size`\n\n  - tuple-like constructors\n\n  - broadcast and/or conversion constructors\n\n  - load constructor\n\n  - `as_tuple()`: Returns the data members as a `std::tuple`.\n\n  - `operator[](size_t)`: Copy of a single `T` stored in the `simd_tuple`. This \n  is not a cheap operation because there are no `T` objects stored in the \n  `simd_tuple`.\n\n  - `copy_from(std::contiguous_iterator)`: :construction: unoptimized load from \n  a contiguous array of struct (e.g. `std::vector\u003cT\u003e`).\n\n  - `copy_to(std::contiguous_iterator)`: :construction: unoptimized store to a \n  contiguous array of struct.\n\n* `vir::simd_tuple\u003cvectorizable_struct_template T, size_t N\u003e`: TODO\n\n* `vir::get\u003cI\u003e(simd_tuple)`: Access to the `I`-th data member (a `simd`).\n\n* `vir::simdize_size\u003cT\u003e`, `vir::simdize_size_v\u003cT\u003e`\n\n\n### Benchmark support functions\n\n*Requires Concepts (C++20) and GNU compatible inline-asm.*\n\nThe header\n```c++\n#include \u003cvir/simd_benchmarking.h\u003e\n```\ndefines the following functions:\n\n* `vir::fake_modify(...)`: Let the compiler assume that all arguments passed to \n  this functions are modified. This inhibits constant propagation, hoisting of \n  code sections, and dead-code elimination.\n\n* `vir::fake_read(...)`: Let the compiler assume that all arguments passed to \n  this function are read (in the cheapest manner). This inhibits dead-code \n  elimination leading up to the results passed to this function.\n\n\n### `constexpr_wrapper`: function arguments as constant expressions\n\nThe header\n```c++\n#include \u003cvir/constexpr_wrapper.h\u003e\n```\ndefines the following tools:\n\n* `vir::constexpr_value` (concept): Satisfied by any type with a static \n  `::value` member that can be used in a constant expression.\n\n* `vir::constexpr_wrapper\u003cauto\u003e` (class template): A type storing the value of \n  its NTTP (non-type template parameter) and overloading all operators to \n  return another `constexpr_wrapper`. `constexpr_wrapper` objects are \n  implicitly convertible to their value type (a `constexpr_wrapper` \n  automatically unwraps its constant expression).\n\n* `vir::cw\u003cauto\u003e` (variable template): Shorthand for producing \n  `constexpr_wrapper` objects with the given value.\n\n* `vir::literals` (namespace with `_cw` UDL): Shorthand for producing \n  `constexpr_wrapper` objects of the integer literal in front of the `_cw` \n  suffix. The type will be deduced automatically from the value of the literal \n  to be the smallest signed integral type, or if the value is larger, `unsigned \n  long long`. If the value is too large for an `unsigned long long`, the \n  program is ill-formed.\n\n`constexpr_wrapper` may appear unrelated to `simd`. However, it is an important \ntool used in many places in the implementation and on interfaces of vir-simd \ntools. `vir::constexpr_wrapper` is very similar to `std::integral_constant`, \nwhich is used in the `simd` TS interface for generator constructors.\n\n#### Example\n\n```c++\n#include \u003cvir/constexpr_wrapper.h\u003e\n\nauto f(vir::constexpr_value auto N)\n{\n  std::array\u003cint, N\u003e x = {};\n  return x;\n}\n\nstd::array a = f(vir::cw\u003c4\u003e); // array\u003cint, 4\u003e\n\nusing namespace vir::literals;\n\nstd::array b = f(10_cw); // array\u003cint, 10\u003e\n```\n\nThis example cannot work with a signature `constexpr auto f(int n)` (or \n`consteval`) because `n` will never be considered a constant expression in the \nbody of the function.\n\n\n### Testing for the version of the vir::stdx::simd (vir-simd) library\n\nThe header\n```c++\n#include \u003cvir/simd_version.h\u003e\n```\n(which is also included from `\u003cvir/simd.h\u003e`) defines the type and constant\n```c++\nnamespace vir\n{\n  struct simd_version_t { int major, minor, patchlevel; };\n\n  constexpr simd_version_t simd_version;\n}\n```\nin addition to the macros `VIR_SIMD_VERSION`, `VIR_SIMD_VERSION_MAJOR`, \n`VIR_SIMD_VERSION_MINOR`, and `VIR_SIMD_VERSION_PATCHLEVEL`.\n\n`simd_version_t` implements all comparison operators, allowing e.g.\n```c++\nstatic_assert(vir::simd_version \u003e= vir::simd_version_t{0,4,0});\n```\n\n#### Semantics of version numbers\n\n* An increment of the major version number implies a breaking change.\n\n* An increment of the minor version number implies new features without \n  breaking changes.\n\n* An increment of the patchlevel is used for bug fixes.\n\n* Odd patchlevel numbers indicate a development (not released) version.\n\n## Debugging\n\nCompile with `-D _GLIBCXX_DEBUG_UB` to get runtime checks for undefined \nbehavior in the `simd` implementation(s). Otherwise, `-fsanitize=undefined` \nwithout the macro definition will also find the problems, but without \nadditional error message.\n\nPreconditions in the vir::stdx::simd implementation and extensions are \ncontrolled via the `-D VIR_CHECK_PRECONDITIONS=N` macro, which defaults to `3`. \nCompile-time diagnostics are only possible if the compiler's optimizer can \ndetect the precondition failure. If you get a bogus compile-time failure, you \nneed to introduce the necessary assumption into your calling function, which is \ntypically a missing precondition check in your function.\n\n| **Option** | **at compile-time** | **at run-time** |\n|:--------------------------|:-------------------:|:---------------:|\n| `-DVIR_CHECK_PRECONDITIONS=0` | warning | invoke UB/unreachable |\n| `-DVIR_CHECK_PRECONDITIONS=1` |   error | invoke UB/unreachable |\n| `-DVIR_CHECK_PRECONDITIONS=2` | warning | trap |\n| `-DVIR_CHECK_PRECONDITIONS=3` |   error | trap |\n| `-DVIR_CHECK_PRECONDITIONS=4` | warning | print error and abort |\n| `-DVIR_CHECK_PRECONDITIONS=5` |   error | print error and abort |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattkretz%2Fvir-simd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattkretz%2Fvir-simd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattkretz%2Fvir-simd/lists"}