Developer Guide

Intel oneAPI DPC++/C++ Compiler Handbook for Intel FPGAs

ID 785441
Date 5/08/2024
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

ivdep Attribute

Include the ivdep attribute in your single task kernel to direct the Intel® oneAPI DPC++/C++ Compiler to ignore memory dependencies carried by the loop that the attribute is applied to. This attribute applies only to the loop it is applied to, and not to any of the future loops that might appear as a result of the [[intel::loop_coalesce(N)]] attribute.

Syntax

[[intel::ivdep]]
[[intel::ivdep(safelen)]]
[[intel::ivdep(array)]]
[[intel::ivdep(array, safelen)]]
[[intel::ivdep(safelen, array)]]
CAUTION:

Applying the ivdep attribute incorrectly results in functionally incorrect hardware and potential functional differences between the hardware run and emulation. The ivdep attribute is ignored in emulation.

During compilation, the Intel® oneAPI DPC++/C++ Compiler creates hardware that ensures load and store instructions operate within dependency constraints. An example of a dependency constraint is that dependent load and store instructions must execute in order. The presence of the ivdep attribute instructs the Intel® oneAPI DPC++/C++ Compiler to remove the extra hardware between load and store instructions in the loop that immediately follows the attribute declaration in the kernel code. Removing the extra hardware may reduce logic utilization and lower the II value.

You can provide more information about loop dependencies by specifying a safelen parameter to the attribute by adding an integer type C++ constant expression argument to the attribute. The safelen parameter specifies the maximum number of consecutive loop iterations without loop-carried dependencies. For example, [[intel::ivdep(32)]] indicates to the compiler that there are at least 32 iterations of the loop before loop-carried dependencies is introduced. That is, while the [[intel::ivdep]] attribute guarantees to the compiler that there are no implicit memory dependencies between any iteration of this loop, [[intel::ivdep(32)]] guarantees that there does not exist a loop-carried dependence with a dependence distance less than 32. For example, if an iteration reads from memory, the preceding 31 iterations and succeeding 31 iterations are guaranteed not to write to the same memory location.

NOTE:
A safelen setting of 1 or 0 is allowed as a convenience, but it has no effect on the loop and the compiler generates a warning.

To specify that accesses to a particular memory array inside a loop do not cause loop-carried dependencies, add the array parameter to the attribute by specifying the array variable name as an argument to the attribute. The array specified by the ivdep attribute must be a local or private memory array, or a pointer variable that points to a global, local, or private memory storage. The array specified by the ivdep attribute can also be an array or a pointer member of a struct.

Examples

// No loop-carried dependencies for accesses to arrays A and B
[[intel::ivdep]]
for (int i = 0; i < N; i++) {
  A[i] = A[i - X[i]];
  B[i] = B[i - Y[i]];
}

// No loop-carried dependencies for accesses to array A
// Compiler inserts hardware that reinforces dependency constraints for B
[[intel::ivdep(A)]]
for (int i = 0; i < N; i++) {
  A[i] = A[i - X[i]];
  B[i] = B[i - Y[i]];
}
// No loop-carried dependencies for array A inside struct
[[intel::ivdep(S.A)]]
for (int i = 0; i < N; i++) {
  S.A[i] = S.A[i - X[i]];
}
// No loop-carried dependencies for array A inside the struct pointed by S
[[intel::ivdep(S->X[2][3].A)]]
for (int i = 0; i < N; i++) {
  S->X[2][3].A[i] = S.A[i - X[i]];
}

RESTRICTION:
Do not apply the ivdep attribute to a variable that is used in a lambda function or a variable that is passed as a function argument. The ivdep attribute is not propagated to the lambda function or function argument. Because the attribute is not propagated, its effects are ignored and can result in functional failures in your kernel.
// The ivdep directive will not be applied to usages of accessorA inside the lambda foo or the function bar
[[intel::ivdep(accessorA, safelen)]]
for (;;){
  auto foo = [=](){ ...; // Uses of accessorA }
  bar(accessorA, ...);
}

CAUTION:

When specifying the array name an ivdep attribute must apply to, the compiler still claims there is a memory dependency on that array. Consider the following example code:

[[intel::ivdep( kSafeLen, histogram.data_ )]] 
for( uint32_t n = 0; n < kInitNumInputs; ++n ) {
  // Compute the Histogram index to increment
  uint32_t hist_group = input[n] % kNumOutputs;
  auto hist_count = histogram.read( hist_group );
  hist_count++;
  histogram.write( hist_group, hist_count );
}

This for loop returns an II of 2 and reports memory dependency on the histogram.data_ array, to which the compiler must apply the ivdep attribute. histogram.data_ is accessed in the histogram.write() and histogram.read() function calls, which prevents the ivdep directive from working.





You can apply [[intel::ivdep(safelen, array)]] only to accesses of that specific array by name in the code that is lexicographically in the loop, which means the array is not traced through function calls.

For additional information, refer to the FPGA tutorial sample "Loop IVDEP" listed in the Intel® oneAPI Samples Browser on Linux* or Windows*, or access the code sample on GitHub.