Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 107 additions & 2 deletions docs/source/API/core/macros-special/if_on_host_or_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,113 @@ accessible outside of it.
``constexpr`` Context
---------------------

These macros cannot be used in a context that requires a ``constexpr``
(constant expression).
These macros **must not** be used in a context that requires a ``constexpr``
(constant expression). Using ``KOKKOS_IF_ON_HOST`` or ``KOKKOS_IF_ON_DEVICE``
within ``constexpr`` functions or to initialize ``constexpr`` variables can lead to
**One Definition Rule (ODR) violations** and undefined behavior.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**One Definition Rule (ODR) violations** and undefined behavior.
**One Definition Rule (ODR) violations** and cause undefined behavior.


Why This Is Problematic
^^^^^^^^^^^^^^^^^^^^^^^^

Unlike runtime function calls, ``constexpr`` functions and variables generate
compile-time values that can affect the structure of types, the size of objects,
and template instantiations. When ``KOKKOS_IF_ON_HOST`` and
``KOKKOS_IF_ON_DEVICE`` are used in ``constexpr`` contexts, they cause the same
function or variable to have different compile-time values or types on the host versus
the device, potentially leading to ODR violations.

Examples of ODR Violations
^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Problematic: Using in** ``constexpr`` **function**

.. code-block:: cpp

// DO NOT DO THIS - causes ODR violation
static constexpr KOKKOS_FUNCTION int compute_block_size() {
KOKKOS_IF_ON_HOST((return 4;))
KOKKOS_IF_ON_DEVICE((return 2;))
}

struct Functor {
int data[compute_block_size()]; // Size differs on host vs device!
// This creates an ODR violation and undefined behavior
};

In this example, the ``Functor`` struct would have different sizes on the host
and device, causing serious memory corruption issues when passed between them.

**Problematic: Lambda capture dependency**

.. code-block:: cpp

// DO NOT DO THIS - causes ODR violation
void foo() {
int a = 0;
double b = 1.0;
auto lambda = KOKKOS_LAMBDA(int) {
KOKKOS_IF_ON_HOST((printf("%i\n", a);)) // Captures 'a'
KOKKOS_IF_ON_DEVICE((printf("%lf\n", b);)) // Captures 'b'
};
// Lambda has different size on host vs device due to different captures
}

The lambda object has different sizes on host and device because of the
different captures, violating the ODR.
Comment on lines +131 to +147
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this problematic example never gets resolved, unlike the one above? If so, I think this example should be first, then the one that gets resolved should be second and it should be clear that the "Correct Alternatives" section is a subsection of that example.


Correct Alternatives
^^^^^^^^^^^^^^^^^^^^

**Alternative 1 (Preferred): Use template specialization**

If possible use template specialization on execution
spaces:

.. code-block:: cpp

// This is OK - different specializations
template<typename ExecutionSpace>
struct BlockSize {
static constexpr int value = 2; // Default for devices
};

template<>
struct BlockSize<Kokkos::DefaultHostExecutionSpace> {
static constexpr int value = 4; // Specialized for host
};

**Alternative 2: Use non-**``constexpr`` **runtime function**

If the value doesn't need to be a compile-time constant, simply remove
``constexpr``:

.. code-block:: cpp

// This is OK - runtime function
static KOKKOS_FUNCTION int compute_block_size() {
KOKKOS_IF_ON_HOST((return 4;))
KOKKOS_IF_ON_DEVICE((return 2;))
}

**Alternative 3: Move** ``KOKKOS_IF_ON_*`` **to calling context**

If you need compile-time constants, move the conditional compilation up one
level:

.. code-block:: cpp

// This is OK - separate constexpr values in each branch
KOKKOS_INLINE_FUNCTION void process() {
KOKKOS_IF_ON_HOST((
constexpr int block_size = 4;
// Use block_size here...
))
KOKKOS_IF_ON_DEVICE((
constexpr int block_size = 2;
// Use block_size here...
))
}


Best Practices
--------------
Expand Down