<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><span class="">Hi,</span><div class=""><span class=""><br class=""></span><span class="">I would like to provide a convenient way for our users to perform additional operations like min, max, abs and round on vector (and possibly matrix) types. To do so, I’d like to propose adding a new set of builtins that operate on vector-like types. The new builtins can be used to perform each operation element wise and as reduction on the elements of a vector or a matrix. The proposal also includes arithmetic reductions. Those are performed pairwise, rather then sequential to ensure they can lowered directly to vector instructions on targets like AArch64 and X86.</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">I considered overloading the existing math builtins, but think we can provide a better and more consistent user-experience with a new set of builtins. The drawbacks of overloading math builtins is discussed at the end.</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">Below is the proposed specification for element-wise and reductions builtins. They will be lowered to the corresponding LLVM intrinsic. Note that not all possible builtins are listed here. Once agreed on the general scheme, it should be easy to add additional builtins. </span><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><br class=""></span><b class=""><span class="">Specification of the element-wise builtins with 1 operands</span><span class=""><br class=""></span></b><span class=""><br class=""></span><span class="">Provided builtins:</span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_abs<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_ceil<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_floor<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_rint<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_round<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_trunc<br class=""></div></span><span class=""><br class=""></span><span class="">---- </span><span class=""><br class=""></span><span class="">T__builtin_elementwise_<name>(T x)</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">T must be one of the following types:</span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• an integer type (as in C2x 6.2.5p19), but excluding enumerated types and _Bool<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• the standard floating types float or double<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• a half-precision floating point type, if one is supported on the target<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• a vector or matrix type.<br class=""></div><div class=""><br class=""></div></span><span class="">For scalar types, consider the operation applied to a vector with a single element.</span> For matrix types, consider them as a vector formed by concatenating its columns for the definitions below.<span class=""><br class=""></span><span class=""><br class=""></span><span class="">Returns: A vector Res equivalent to applying </span><span class="">fn</span><span class=""> elementwise to the input, where </span><span class="">fn</span><span class=""> depends on (name, element type of VT):</span><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (abs, floating point type) → return the absolute value of a floating-point number x<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (abs, integer ty) → (a < 0) ? a * -1 : a<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (ceil, floating point type) → return the smallest integral value greater than or equal to x<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (ceil, integer type) → invalid<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (floor, floating point type) → return the largest integral value less than or equal to x<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (floor, integer type) → invalid<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (rint, floating point type) → return the integral value nearest to x (according to the prevailing rounding mode) in floating-point format<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (rint, integer type) → invalid<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (round, floating point type) → return the integral value nearest to x rounding half-way cases away from zero, regardless of the current rounding direction<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (round, integer type) → invalid<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (trunc, floating point type) → return the integral value nearest to but no larger in magnitude than x<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• (trunc, integer type) → invalid<br class=""></div></span><span class=""><br class=""></span><span class="">Special values:</span><span class=""><br class=""></span><span class="">Unless specified otherwise fn(±0)= ±0 and fn(±infinity) = ±infinity</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">T Res</span><span class=""><br class=""></span><span class="">for (int I = 0; I < NumElements; ++I)</span><span class=""><br class=""></span><span class=""> Res[I] = fn(a[I])</span><span class=""><br class=""></span><span class="">----</span><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><br class=""></span><b class=""><span class="">Specification of the element-wise builtins with 2 operands:</span><span class=""><br class=""></span></b><span class=""><br class=""></span><span class="">Provided builtins:</span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_max<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_elementwise_min<br class=""></div></span><span class=""><br class=""></span><span class="">---- </span><span class=""><br class=""></span><span class="">T __builtin_elementwise_<name>(T x, T y)</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">T must be one of the following types:</span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• an integer type (as in C2x 6.2.5p19), but excluding enumerated types and _Bool<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• the standard floating types float or double<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• a half-precision floating point type, if one is supported on the target<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• a vector or matrix type.<br class=""></div><div class=""><br class=""></div></span><span class="">For scalar types, consider the operation applied to a vector with a single element. </span>For matrix types, consider them as a vector formed by concatenating its columns for the definitions below.<span class=""><br class=""></span><span class=""><br class=""></span><span class="">Returns: A vector Res equivalent to applying </span><span class="">fn(x, y)</span><span class=""> elementwise to the inputs, where </span><span class="">fn</span><span class=""> depends on the name:</span><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• min → return x or y, whichever is smaller<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• max → return x or y, whichever is larger<br class=""></div></span><span class=""><br class=""></span><span class="">Special values:</span><span class=""><br class=""></span><span class="">Unless otherwise specified, the following holds. If exactly one argument is a NaN, return the other argument. If both arguments are NaNs, fmax() return a NaN.</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">VT Res</span><span class=""><br class=""></span><span class="">for (int I = 0; I < NumElements; ++I)</span><span class=""><br class=""></span><span class=""> Res[I] = fn(a[I], b[I])</span><span class=""><br class=""></span><span class="">----</span><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><br class=""></span><b class=""><span class="">Specification of reduction builtins:</span><span class=""><br class=""></span></b><span class=""><br class=""></span><span class="">Provided builtins:</span><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_min<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_max<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_add<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_and<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_or<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• __builtin_reduce_xor<br class=""></div></span><span class=""><br class=""></span><span class="">---- </span><span class=""><br class=""></span><span class="">ET__builtin_reduce_<name>(VT a)</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">VT must be a vector or matrix type with element type ET. For matrix types, consider them as a vector formed by concatenating its columns for the definitions below.</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">Returns: A scalar Res equivalent to applying </span><span class="">fn</span><span class=""> as pairwise tree reduction to the input, where </span><span class="">fn(x, y)</span><span class=""> depends on the name :</span></div><div class=""><span class=""><br class=""></span><span class=""><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• min → return x or y, whichever is smaller; If exactly one argument is a NaN, return the other argument. If both arguments are NaNs, fmax() return a NaN.<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• max → return x or y, whichever is larger; If exactly one argument is a NaN, return the other argument. If both arguments are NaNs, fmax() return a NaN.<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• add → +<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• and → & (integer (element) types only)<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• or → | (integer (element) types only)<br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>• xor → ^ (integer (element) types only)<br class=""></div></span><span class="">----</span><span class=""><br class=""></span><span class=""></span><span class=""><br class=""><br class=""></span><span class="">The intended semantics should allow for the following lowering using LLVM intrinsics (using </span><span class="">__builtin_{elementwise,reduce}_min</span><span class=""> as example):</span><span class=""><br class=""></span><span class=""><br class=""></span><span class="">declare float @llvm.vector.reduce.fmin.v4f32(<4 x float>)<br class="">define float @float_min_red(<4 x float> %a) {<br class=""> %r = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> %a)<br class=""> ret float %r<br class="">}<br class=""><br class="">declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>)<br class="">define <4 x float> @float_min(<4 x float> %a, <4 x float> %b) {<br class=""> %r = call <4 x float> @llvm.minnum.v4f32(<4 x float> %a, <4 x float> %b)<br class=""> ret <4 x float> %r<br class="">}<br class=""><br class="">declare <4 x i32> @llvm.smin.v4i32(<4 x i32>, <4 x i32>)<br class="">define <4 x i32> @int_min(<4 x i32> %a, <4 x i32> %b) {<br class=""> %r = call <4 x i32> @llvm.smin.v4i32(<4 x i32> %a, <4 x i32> %b)<br class=""> ret <4 x i32> %r<br class="">}<br class=""><br class="">declare i32 @llvm.vector.reduce.smin.v4i32(<4 x i32>)<br class="">define i32 @int_min_red(<4 x i32> %b) {<br class=""> %r = call i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> %b)<br class=""> ret i32 %r<br class="">}<br class=""></span><span class=""><a href="https://llvm.godbolt.org/z/7Yv3v8aP9" class="">https://llvm.godbolt.org/z/7Yv3v8aP9</a></span></div><div class=""><span class=""><br class=""></span><span class=""><br class=""></span><span class=""><b class="">Alternatives Considered<br class=""></b><br class=""></span><span class="">Instead of adding a set of completely new builtins, we could overload the existing libm-based builtins (</span><span class="">__builtin_fminf</span><span class="">& co). The main issue with that approach is convenience for the users I think. The libm-based builtins encode the type in their name which allows us to only cover a small subset of types. For example, AFAIK there’s no builtin for integer min/max or floating point max for 16 bit floating point types. The proposal above provides a set of more user friendly builtins that work across a large range of types, similar to the existing math operators (i.e. there’s a single </span><span class="">__builtin_max</span><span class="">which works with both integer and floating points, as well as vector & matrix versions). The reduction versions are also explicitly marked in the name.</span><br class=""></div><div class=""><span class=""><br class=""></span></div><div class=""><span class=""><br class=""></span></div><div class=""><span class="">Cheers</span></div><div class=""><span class="">Florian</span></div></body></html>