1 // Simd scalar ABI specific implementations -*- C++ -*-
2 
3 // Copyright (C) 2020-2021 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 #ifndef _GLIBCXX_EXPERIMENTAL_SIMD_SCALAR_H_
26 #define _GLIBCXX_EXPERIMENTAL_SIMD_SCALAR_H_
27 #if __cplusplus >= 201703L
28 
29 #include <cmath>
30 
31 _GLIBCXX_SIMD_BEGIN_NAMESPACE
32 
33 // __promote_preserving_unsigned{{{
34 // work around crazy semantics of unsigned integers of lower rank than int:
35 // Before applying an operator the operands are promoted to int. In which case
36 // over- or underflow is UB, even though the operand types were unsigned.
37 template <typename _Tp>
decltype(auto)38   _GLIBCXX_SIMD_INTRINSIC constexpr decltype(auto)
39   __promote_preserving_unsigned(const _Tp& __x)
40   {
41     if constexpr (is_signed_v<decltype(+__x)> && is_unsigned_v<_Tp>)
42       return static_cast<unsigned int>(__x);
43     else
44       return __x;
45   }
46 
47 // }}}
48 
49 struct _CommonImplScalar;
50 struct _CommonImplBuiltin;
51 struct _SimdImplScalar;
52 struct _MaskImplScalar;
53 
54 // simd_abi::_Scalar {{{
55 struct simd_abi::_Scalar
56 {
57   template <typename _Tp>
58     static constexpr size_t _S_size = 1;
59 
60   template <typename _Tp>
61     static constexpr size_t _S_full_size = 1;
62 
63   template <typename _Tp>
64     static constexpr bool _S_is_partial = false;
65 
66   struct _IsValidAbiTag : true_type {};
67 
68   template <typename _Tp>
69     struct _IsValidSizeFor : true_type {};
70 
71   template <typename _Tp>
72     struct _IsValid : __is_vectorizable<_Tp> {};
73 
74   template <typename _Tp>
75     static constexpr bool _S_is_valid_v = _IsValid<_Tp>::value;
76 
_S_masked_Scalar77   _GLIBCXX_SIMD_INTRINSIC static constexpr bool _S_masked(bool __x)
78   { return __x; }
79 
80   using _CommonImpl = _CommonImplScalar;
81   using _SimdImpl = _SimdImplScalar;
82   using _MaskImpl = _MaskImplScalar;
83 
84   template <typename _Tp, bool = _S_is_valid_v<_Tp>>
85     struct __traits : _InvalidTraits {};
86 
87   template <typename _Tp>
88     struct __traits<_Tp, true>
89     {
90       using _IsValid = true_type;
91       using _SimdImpl = _SimdImplScalar;
92       using _MaskImpl = _MaskImplScalar;
93       using _SimdMember = _Tp;
94       using _MaskMember = bool;
95 
96       static constexpr size_t _S_simd_align = alignof(_SimdMember);
97       static constexpr size_t _S_mask_align = alignof(_MaskMember);
98 
99       // nothing the user can spell converts to/from simd/simd_mask
100       struct _SimdCastType { _SimdCastType() = delete; };
101       struct _MaskCastType { _MaskCastType() = delete; };
102       struct _SimdBase {};
103       struct _MaskBase {};
104     };
105 };
106 
107 // }}}
108 // _CommonImplScalar {{{
109 struct _CommonImplScalar
110 {
111   // _S_store {{{
112   template <typename _Tp>
113     _GLIBCXX_SIMD_INTRINSIC static void _S_store(_Tp __x, void* __addr)
114     { __builtin_memcpy(__addr, &__x, sizeof(_Tp)); }
115 
116   // }}}
117   // _S_store_bool_array(_BitMask) {{{
118   template <size_t _Np, bool _Sanitized>
119     _GLIBCXX_SIMD_INTRINSIC static constexpr void
120     _S_store_bool_array(_BitMask<_Np, _Sanitized> __x, bool* __mem)
121     {
122       __make_dependent_t<decltype(__x), _CommonImplBuiltin>::_S_store_bool_array(
123 	__x, __mem);
124     }
125 
126   // }}}
127 };
128 
129 // }}}
130 // _SimdImplScalar {{{
131 struct _SimdImplScalar
132 {
133   // member types {{{2
134   using abi_type = simd_abi::scalar;
135 
136   template <typename _Tp>
137     using _TypeTag = _Tp*;
138 
139   // _S_broadcast {{{2
140   template <typename _Tp>
141     _GLIBCXX_SIMD_INTRINSIC static constexpr _Tp _S_broadcast(_Tp __x) noexcept
142     { return __x; }
143 
144   // _S_generator {{{2
145   template <typename _Fp, typename _Tp>
146     _GLIBCXX_SIMD_INTRINSIC static constexpr _Tp _S_generator(_Fp&& __gen,
147 							      _TypeTag<_Tp>)
148     { return __gen(_SizeConstant<0>()); }
149 
150   // _S_load {{{2
151   template <typename _Tp, typename _Up>
152     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_load(const _Up* __mem,
153 					       _TypeTag<_Tp>) noexcept
154     { return static_cast<_Tp>(__mem[0]); }
155 
156   // _S_masked_load {{{2
157   template <typename _Tp, typename _Up>
158     static inline _Tp _S_masked_load(_Tp __merge, bool __k,
159 				     const _Up* __mem) noexcept
160     {
161       if (__k)
162 	__merge = static_cast<_Tp>(__mem[0]);
163       return __merge;
164     }
165 
166   // _S_store {{{2
167   template <typename _Tp, typename _Up>
168     static inline void _S_store(_Tp __v, _Up* __mem, _TypeTag<_Tp>) noexcept
169     { __mem[0] = static_cast<_Up>(__v); }
170 
171   // _S_masked_store {{{2
172   template <typename _Tp, typename _Up>
173     static inline void _S_masked_store(const _Tp __v, _Up* __mem,
174 				       const bool __k) noexcept
175     { if (__k) __mem[0] = __v; }
176 
177   // _S_negate {{{2
178   template <typename _Tp>
179     static constexpr inline bool _S_negate(_Tp __x) noexcept
180     { return !__x; }
181 
182   // _S_reduce {{{2
183   template <typename _Tp, typename _BinaryOperation>
184     static constexpr inline _Tp
185     _S_reduce(const simd<_Tp, simd_abi::scalar>& __x, const _BinaryOperation&)
186     { return __x._M_data; }
187 
188   // _S_min, _S_max {{{2
189   template <typename _Tp>
190     static constexpr inline _Tp _S_min(const _Tp __a, const _Tp __b)
191     { return std::min(__a, __b); }
192 
193   template <typename _Tp>
194     static constexpr inline _Tp _S_max(const _Tp __a, const _Tp __b)
195     { return std::max(__a, __b); }
196 
197   // _S_complement {{{2
198   template <typename _Tp>
199     static constexpr inline _Tp _S_complement(_Tp __x) noexcept
200     { return static_cast<_Tp>(~__x); }
201 
202   // _S_unary_minus {{{2
203   template <typename _Tp>
204     static constexpr inline _Tp _S_unary_minus(_Tp __x) noexcept
205     { return static_cast<_Tp>(-__x); }
206 
207   // arithmetic operators {{{2
208   template <typename _Tp>
209     static constexpr inline _Tp _S_plus(_Tp __x, _Tp __y)
210     {
211       return static_cast<_Tp>(__promote_preserving_unsigned(__x)
212 			      + __promote_preserving_unsigned(__y));
213     }
214 
215   template <typename _Tp>
216     static constexpr inline _Tp _S_minus(_Tp __x, _Tp __y)
217     {
218       return static_cast<_Tp>(__promote_preserving_unsigned(__x)
219 			      - __promote_preserving_unsigned(__y));
220     }
221 
222   template <typename _Tp>
223     static constexpr inline _Tp _S_multiplies(_Tp __x, _Tp __y)
224     {
225       return static_cast<_Tp>(__promote_preserving_unsigned(__x)
226 			      * __promote_preserving_unsigned(__y));
227     }
228 
229   template <typename _Tp>
230     static constexpr inline _Tp _S_divides(_Tp __x, _Tp __y)
231     {
232       return static_cast<_Tp>(__promote_preserving_unsigned(__x)
233 			      / __promote_preserving_unsigned(__y));
234     }
235 
236   template <typename _Tp>
237     static constexpr inline _Tp _S_modulus(_Tp __x, _Tp __y)
238     {
239       return static_cast<_Tp>(__promote_preserving_unsigned(__x)
240 			      % __promote_preserving_unsigned(__y));
241     }
242 
243   template <typename _Tp>
244     static constexpr inline _Tp _S_bit_and(_Tp __x, _Tp __y)
245     {
246       if constexpr (is_floating_point_v<_Tp>)
247 	{
248 	  using _Ip = __int_for_sizeof_t<_Tp>;
249 	  return __bit_cast<_Tp>(__bit_cast<_Ip>(__x) & __bit_cast<_Ip>(__y));
250 	}
251       else
252 	return static_cast<_Tp>(__promote_preserving_unsigned(__x)
253 				& __promote_preserving_unsigned(__y));
254     }
255 
256   template <typename _Tp>
257     static constexpr inline _Tp _S_bit_or(_Tp __x, _Tp __y)
258     {
259       if constexpr (is_floating_point_v<_Tp>)
260 	{
261 	  using _Ip = __int_for_sizeof_t<_Tp>;
262 	  return __bit_cast<_Tp>(__bit_cast<_Ip>(__x) | __bit_cast<_Ip>(__y));
263 	}
264       else
265 	return static_cast<_Tp>(__promote_preserving_unsigned(__x)
266 				| __promote_preserving_unsigned(__y));
267     }
268 
269   template <typename _Tp>
270     static constexpr inline _Tp _S_bit_xor(_Tp __x, _Tp __y)
271     {
272       if constexpr (is_floating_point_v<_Tp>)
273 	{
274 	  using _Ip = __int_for_sizeof_t<_Tp>;
275 	  return __bit_cast<_Tp>(__bit_cast<_Ip>(__x) ^ __bit_cast<_Ip>(__y));
276 	}
277       else
278 	return static_cast<_Tp>(__promote_preserving_unsigned(__x)
279 				^ __promote_preserving_unsigned(__y));
280     }
281 
282   template <typename _Tp>
283     static constexpr inline _Tp _S_bit_shift_left(_Tp __x, int __y)
284     { return static_cast<_Tp>(__promote_preserving_unsigned(__x) << __y); }
285 
286   template <typename _Tp>
287     static constexpr inline _Tp _S_bit_shift_right(_Tp __x, int __y)
288     { return static_cast<_Tp>(__promote_preserving_unsigned(__x) >> __y); }
289 
290   // math {{{2
291   // frexp, modf and copysign implemented in simd_math.h
292   template <typename _Tp>
293     using _ST = _SimdTuple<_Tp, simd_abi::scalar>;
294 
295   template <typename _Tp>
296     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_acos(_Tp __x)
297     { return std::acos(__x); }
298 
299   template <typename _Tp>
300     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_asin(_Tp __x)
301     { return std::asin(__x); }
302 
303   template <typename _Tp>
304     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_atan(_Tp __x)
305     { return std::atan(__x); }
306 
307   template <typename _Tp>
308     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_cos(_Tp __x)
309     { return std::cos(__x); }
310 
311   template <typename _Tp>
312     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_sin(_Tp __x)
313     { return std::sin(__x); }
314 
315   template <typename _Tp>
316     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_tan(_Tp __x)
317     { return std::tan(__x); }
318 
319   template <typename _Tp>
320     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_acosh(_Tp __x)
321     { return std::acosh(__x); }
322 
323   template <typename _Tp>
324     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_asinh(_Tp __x)
325     { return std::asinh(__x); }
326 
327   template <typename _Tp>
328     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_atanh(_Tp __x)
329     { return std::atanh(__x); }
330 
331   template <typename _Tp>
332     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_cosh(_Tp __x)
333     { return std::cosh(__x); }
334 
335   template <typename _Tp>
336     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_sinh(_Tp __x)
337     { return std::sinh(__x); }
338 
339   template <typename _Tp>
340     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_tanh(_Tp __x)
341     { return std::tanh(__x); }
342 
343   template <typename _Tp>
344     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_atan2(_Tp __x, _Tp __y)
345     { return std::atan2(__x, __y); }
346 
347   template <typename _Tp>
348     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_exp(_Tp __x)
349     { return std::exp(__x); }
350 
351   template <typename _Tp>
352     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_exp2(_Tp __x)
353     { return std::exp2(__x); }
354 
355   template <typename _Tp>
356     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_expm1(_Tp __x)
357     { return std::expm1(__x); }
358 
359   template <typename _Tp>
360     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_log(_Tp __x)
361     { return std::log(__x); }
362 
363   template <typename _Tp>
364     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_log10(_Tp __x)
365     { return std::log10(__x); }
366 
367   template <typename _Tp>
368     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_log1p(_Tp __x)
369     { return std::log1p(__x); }
370 
371   template <typename _Tp>
372     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_log2(_Tp __x)
373     { return std::log2(__x); }
374 
375   template <typename _Tp>
376     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_logb(_Tp __x)
377     { return std::logb(__x); }
378 
379   template <typename _Tp>
380     _GLIBCXX_SIMD_INTRINSIC static _ST<int> _S_ilogb(_Tp __x)
381     { return {std::ilogb(__x)}; }
382 
383   template <typename _Tp>
384     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_pow(_Tp __x, _Tp __y)
385     { return std::pow(__x, __y); }
386 
387   template <typename _Tp>
388     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_abs(_Tp __x)
389     { return std::abs(__x); }
390 
391   template <typename _Tp>
392     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fabs(_Tp __x)
393     { return std::fabs(__x); }
394 
395   template <typename _Tp>
396     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_sqrt(_Tp __x)
397     { return std::sqrt(__x); }
398 
399   template <typename _Tp>
400     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_cbrt(_Tp __x)
401     { return std::cbrt(__x); }
402 
403   template <typename _Tp>
404     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_erf(_Tp __x)
405     { return std::erf(__x); }
406 
407   template <typename _Tp>
408     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_erfc(_Tp __x)
409     { return std::erfc(__x); }
410 
411   template <typename _Tp>
412     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_lgamma(_Tp __x)
413     { return std::lgamma(__x); }
414 
415   template <typename _Tp>
416     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_tgamma(_Tp __x)
417     { return std::tgamma(__x); }
418 
419   template <typename _Tp>
420     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_trunc(_Tp __x)
421     { return std::trunc(__x); }
422 
423   template <typename _Tp>
424     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_floor(_Tp __x)
425     { return std::floor(__x); }
426 
427   template <typename _Tp>
428     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_ceil(_Tp __x)
429     { return std::ceil(__x); }
430 
431   template <typename _Tp>
432     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_nearbyint(_Tp __x)
433     { return std::nearbyint(__x); }
434 
435   template <typename _Tp>
436     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_rint(_Tp __x)
437     { return std::rint(__x); }
438 
439   template <typename _Tp>
440     _GLIBCXX_SIMD_INTRINSIC static _ST<long> _S_lrint(_Tp __x)
441     { return {std::lrint(__x)}; }
442 
443   template <typename _Tp>
444     _GLIBCXX_SIMD_INTRINSIC static _ST<long long> _S_llrint(_Tp __x)
445     { return {std::llrint(__x)}; }
446 
447   template <typename _Tp>
448     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_round(_Tp __x)
449     { return std::round(__x); }
450 
451   template <typename _Tp>
452     _GLIBCXX_SIMD_INTRINSIC static _ST<long> _S_lround(_Tp __x)
453     { return {std::lround(__x)}; }
454 
455   template <typename _Tp>
456     _GLIBCXX_SIMD_INTRINSIC static _ST<long long> _S_llround(_Tp __x)
457     { return {std::llround(__x)}; }
458 
459   template <typename _Tp>
460     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_ldexp(_Tp __x, _ST<int> __y)
461     { return std::ldexp(__x, __y.first); }
462 
463   template <typename _Tp>
464     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_scalbn(_Tp __x, _ST<int> __y)
465     { return std::scalbn(__x, __y.first); }
466 
467   template <typename _Tp>
468     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_scalbln(_Tp __x, _ST<long> __y)
469     { return std::scalbln(__x, __y.first); }
470 
471   template <typename _Tp>
472     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fmod(_Tp __x, _Tp __y)
473     { return std::fmod(__x, __y); }
474 
475   template <typename _Tp>
476     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_remainder(_Tp __x, _Tp __y)
477     { return std::remainder(__x, __y); }
478 
479   template <typename _Tp>
480     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_nextafter(_Tp __x, _Tp __y)
481     { return std::nextafter(__x, __y); }
482 
483   template <typename _Tp>
484     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fdim(_Tp __x, _Tp __y)
485     { return std::fdim(__x, __y); }
486 
487   template <typename _Tp>
488     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fmax(_Tp __x, _Tp __y)
489     { return std::fmax(__x, __y); }
490 
491   template <typename _Tp>
492     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fmin(_Tp __x, _Tp __y)
493     { return std::fmin(__x, __y); }
494 
495   template <typename _Tp>
496     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_fma(_Tp __x, _Tp __y, _Tp __z)
497     { return std::fma(__x, __y, __z); }
498 
499   template <typename _Tp>
500     _GLIBCXX_SIMD_INTRINSIC static _Tp _S_remquo(_Tp __x, _Tp __y, _ST<int>* __z)
501     { return std::remquo(__x, __y, &__z->first); }
502 
503   template <typename _Tp>
504     _GLIBCXX_SIMD_INTRINSIC constexpr static _ST<int> _S_fpclassify(_Tp __x)
505     { return {std::fpclassify(__x)}; }
506 
507   template <typename _Tp>
508     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isfinite(_Tp __x)
509     { return std::isfinite(__x); }
510 
511   template <typename _Tp>
512     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isinf(_Tp __x)
513     { return std::isinf(__x); }
514 
515   template <typename _Tp>
516     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isnan(_Tp __x)
517     { return std::isnan(__x); }
518 
519   template <typename _Tp>
520     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isnormal(_Tp __x)
521     { return std::isnormal(__x); }
522 
523   template <typename _Tp>
524     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_signbit(_Tp __x)
525     { return std::signbit(__x); }
526 
527   template <typename _Tp>
528     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isgreater(_Tp __x, _Tp __y)
529     { return std::isgreater(__x, __y); }
530 
531   template <typename _Tp>
532     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isgreaterequal(_Tp __x,
533 								    _Tp __y)
534     { return std::isgreaterequal(__x, __y); }
535 
536   template <typename _Tp>
537     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isless(_Tp __x, _Tp __y)
538     { return std::isless(__x, __y); }
539 
540   template <typename _Tp>
541     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_islessequal(_Tp __x, _Tp __y)
542     { return std::islessequal(__x, __y); }
543 
544   template <typename _Tp>
545     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_islessgreater(_Tp __x,
546 								   _Tp __y)
547     { return std::islessgreater(__x, __y); }
548 
549   template <typename _Tp>
550     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_isunordered(_Tp __x,
551 								 _Tp __y)
552     { return std::isunordered(__x, __y); }
553 
554   // _S_increment & _S_decrement{{{2
555   template <typename _Tp>
556     constexpr static inline void _S_increment(_Tp& __x)
557     { ++__x; }
558 
559   template <typename _Tp>
560     constexpr static inline void _S_decrement(_Tp& __x)
561     { --__x; }
562 
563 
564   // compares {{{2
565   template <typename _Tp>
566     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_equal_to(_Tp __x, _Tp __y)
567     { return __x == __y; }
568 
569   template <typename _Tp>
570     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_not_equal_to(_Tp __x,
571 								  _Tp __y)
572     { return __x != __y; }
573 
574   template <typename _Tp>
575     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_less(_Tp __x, _Tp __y)
576     { return __x < __y; }
577 
578   template <typename _Tp>
579     _GLIBCXX_SIMD_INTRINSIC constexpr static bool _S_less_equal(_Tp __x,
580 								_Tp __y)
581     { return __x <= __y; }
582 
583   // smart_reference access {{{2
584   template <typename _Tp, typename _Up>
585     constexpr static void _S_set(_Tp& __v, [[maybe_unused]] int __i,
586 				 _Up&& __x) noexcept
587     {
588       _GLIBCXX_DEBUG_ASSERT(__i == 0);
589       __v = static_cast<_Up&&>(__x);
590     }
591 
592   // _S_masked_assign {{{2
593   template <typename _Tp>
594     _GLIBCXX_SIMD_INTRINSIC constexpr static void
595     _S_masked_assign(bool __k, _Tp& __lhs, _Tp __rhs)
596     { if (__k) __lhs = __rhs; }
597 
598   // _S_masked_cassign {{{2
599   template <typename _Op, typename _Tp>
600     _GLIBCXX_SIMD_INTRINSIC constexpr static void
601     _S_masked_cassign(const bool __k, _Tp& __lhs, const _Tp __rhs, _Op __op)
602     { if (__k) __lhs = __op(_SimdImplScalar{}, __lhs, __rhs); }
603 
604   // _S_masked_unary {{{2
605   template <template <typename> class _Op, typename _Tp>
606     _GLIBCXX_SIMD_INTRINSIC constexpr static _Tp _S_masked_unary(const bool __k,
607 								 const _Tp __v)
608     { return static_cast<_Tp>(__k ? _Op<_Tp>{}(__v) : __v); }
609 
610   // }}}2
611 };
612 
613 // }}}
614 // _MaskImplScalar {{{
615 struct _MaskImplScalar
616 {
617   // member types {{{
618   template <typename _Tp>
619     using _TypeTag = _Tp*;
620 
621   // }}}
622   // _S_broadcast {{{
623   template <typename>
624     _GLIBCXX_SIMD_INTRINSIC static constexpr bool _S_broadcast(bool __x)
625     { return __x; }
626 
627   // }}}
628   // _S_load {{{
629   template <typename>
630     _GLIBCXX_SIMD_INTRINSIC static constexpr bool _S_load(const bool* __mem)
631     { return __mem[0]; }
632 
633   // }}}
634   // _S_to_bits {{{
635   _GLIBCXX_SIMD_INTRINSIC static constexpr _SanitizedBitMask<1>
636   _S_to_bits(bool __x)
637   { return __x; }
638 
639   // }}}
640   // _S_convert {{{
641   template <typename, bool _Sanitized>
642     _GLIBCXX_SIMD_INTRINSIC static constexpr bool
643     _S_convert(_BitMask<1, _Sanitized> __x)
644     { return __x[0]; }
645 
646   template <typename, typename _Up, typename _UAbi>
647     _GLIBCXX_SIMD_INTRINSIC static constexpr bool
648     _S_convert(simd_mask<_Up, _UAbi> __x)
649     { return __x[0]; }
650 
651   // }}}
652   // _S_from_bitmask {{{2
653   template <typename _Tp>
654     _GLIBCXX_SIMD_INTRINSIC constexpr static bool
655     _S_from_bitmask(_SanitizedBitMask<1> __bits, _TypeTag<_Tp>) noexcept
656     { return __bits[0]; }
657 
658   // _S_masked_load {{{2
659   _GLIBCXX_SIMD_INTRINSIC constexpr static bool
660   _S_masked_load(bool __merge, bool __mask, const bool* __mem) noexcept
661   {
662     if (__mask)
663       __merge = __mem[0];
664     return __merge;
665   }
666 
667   // _S_store {{{2
668   _GLIBCXX_SIMD_INTRINSIC static void _S_store(bool __v, bool* __mem) noexcept
669   { __mem[0] = __v; }
670 
671   // _S_masked_store {{{2
672   _GLIBCXX_SIMD_INTRINSIC static void
673   _S_masked_store(const bool __v, bool* __mem, const bool __k) noexcept
674   {
675     if (__k)
676       __mem[0] = __v;
677   }
678 
679   // logical and bitwise operators {{{2
680   static constexpr bool _S_logical_and(bool __x, bool __y)
681   { return __x && __y; }
682 
683   static constexpr bool _S_logical_or(bool __x, bool __y)
684   { return __x || __y; }
685 
686   static constexpr bool _S_bit_not(bool __x)
687   { return !__x; }
688 
689   static constexpr bool _S_bit_and(bool __x, bool __y)
690   { return __x && __y; }
691 
692   static constexpr bool _S_bit_or(bool __x, bool __y)
693   { return __x || __y; }
694 
695   static constexpr bool _S_bit_xor(bool __x, bool __y)
696   { return __x != __y; }
697 
698   // smart_reference access {{{2
699   constexpr static void _S_set(bool& __k, [[maybe_unused]] int __i,
700 			       bool __x) noexcept
701   {
702     _GLIBCXX_DEBUG_ASSERT(__i == 0);
703     __k = __x;
704   }
705 
706   // _S_masked_assign {{{2
707   _GLIBCXX_SIMD_INTRINSIC static void _S_masked_assign(bool __k, bool& __lhs,
708 						       bool __rhs)
709   {
710     if (__k)
711       __lhs = __rhs;
712   }
713 
714   // }}}2
715   // _S_all_of {{{
716   template <typename _Tp, typename _Abi>
717     _GLIBCXX_SIMD_INTRINSIC constexpr static bool
718     _S_all_of(simd_mask<_Tp, _Abi> __k)
719     { return __k._M_data; }
720 
721   // }}}
722   // _S_any_of {{{
723   template <typename _Tp, typename _Abi>
724     _GLIBCXX_SIMD_INTRINSIC constexpr static bool
725     _S_any_of(simd_mask<_Tp, _Abi> __k)
726     { return __k._M_data; }
727 
728   // }}}
729   // _S_none_of {{{
730   template <typename _Tp, typename _Abi>
731     _GLIBCXX_SIMD_INTRINSIC constexpr static bool
732     _S_none_of(simd_mask<_Tp, _Abi> __k)
733     { return !__k._M_data; }
734 
735   // }}}
736   // _S_some_of {{{
737   template <typename _Tp, typename _Abi>
738     _GLIBCXX_SIMD_INTRINSIC constexpr static bool
739     _S_some_of(simd_mask<_Tp, _Abi>)
740     { return false; }
741 
742   // }}}
743   // _S_popcount {{{
744   template <typename _Tp, typename _Abi>
745     _GLIBCXX_SIMD_INTRINSIC constexpr static int
746     _S_popcount(simd_mask<_Tp, _Abi> __k)
747     { return __k._M_data; }
748 
749   // }}}
750   // _S_find_first_set {{{
751   template <typename _Tp, typename _Abi>
752     _GLIBCXX_SIMD_INTRINSIC constexpr static int
753     _S_find_first_set(simd_mask<_Tp, _Abi>)
754     { return 0; }
755 
756   // }}}
757   // _S_find_last_set {{{
758   template <typename _Tp, typename _Abi>
759     _GLIBCXX_SIMD_INTRINSIC constexpr static int
760     _S_find_last_set(simd_mask<_Tp, _Abi>)
761     { return 0; }
762 
763   // }}}
764 };
765 
766 // }}}
767 
768 _GLIBCXX_SIMD_END_NAMESPACE
769 #endif // __cplusplus >= 201703L
770 #endif // _GLIBCXX_EXPERIMENTAL_SIMD_SCALAR_H_
771 
772 // vim: foldmethod=marker sw=2 noet ts=8 sts=2 tw=80
773