blob: 68d6839f2c5ef7f34797dff08f7d3989f57e0ee6 [file] [log] [blame]
Ben Clayton0ce9ab02022-05-05 20:23:40 +00001// Copyright 2021 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
dan sinclairb14a7782023-07-20 09:21:10 +000015#ifndef SRC_TINT_LANG_BASE_BUILTIN_NUMBER_H_
16#define SRC_TINT_LANG_BASE_BUILTIN_NUMBER_H_
Ben Clayton0ce9ab02022-05-05 20:23:40 +000017
18#include <stdint.h>
Ben Claytoncf52af72022-06-29 17:09:11 +000019#include <cmath>
Ben Clayton41285aa2022-05-10 14:55:34 +000020#include <functional>
Ben Claytonc2eccfc2022-05-25 15:04:24 +000021#include <limits>
dan sinclair0a4e2a12022-06-18 14:34:00 +000022#include <optional>
Ben Claytonc2eccfc2022-05-25 15:04:24 +000023
dan sinclair22b4dd22023-07-21 00:40:07 +000024#include "src/tint/utils/macros/compiler.h"
25#include "src/tint/utils/result/result.h"
26#include "src/tint/utils/text/string_stream.h"
27#include "src/tint/utils/traits/traits.h"
Ben Claytonc2eccfc2022-05-25 15:04:24 +000028
29// Forward declaration
30namespace tint {
31/// Number wraps a integer or floating point number, enforcing explicit casting.
32template <typename T>
33struct Number;
34} // namespace tint
Ben Clayton0ce9ab02022-05-05 20:23:40 +000035
Zhaoming Jiang62bfd312022-05-13 12:01:11 +000036namespace tint::detail {
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000037/// Base template for IsNumber
38template <typename T>
39struct IsNumber : std::false_type {};
40
41/// Specialization for IsNumber
42template <typename T>
43struct IsNumber<Number<T>> : std::true_type {};
44
Zhaoming Jiang62bfd312022-05-13 12:01:11 +000045/// An empty structure used as a unique template type for Number when
46/// specializing for the f16 type.
47struct NumberKindF16 {};
Ben Claytonc2eccfc2022-05-25 15:04:24 +000048
49/// Helper for obtaining the underlying type for a Number.
50template <typename T>
51struct NumberUnwrapper {
52 /// When T is not a Number, then type defined to be T.
53 using type = T;
54};
55
56/// NumberUnwrapper specialization for Number<T>.
57template <typename T>
58struct NumberUnwrapper<Number<T>> {
59 /// The Number's underlying type.
60 using type = typename Number<T>::type;
61};
62
Zhaoming Jiang62bfd312022-05-13 12:01:11 +000063} // namespace tint::detail
64
Ben Clayton0ce9ab02022-05-05 20:23:40 +000065namespace tint {
66
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000067/// Evaluates to true iff T is a Number
68template <typename T>
Ben Clayton66805b02023-06-14 22:00:01 +000069constexpr bool IsNumber = tint::detail::IsNumber<T>::value;
Antonio Maioranoc20c5df2022-09-01 14:57:39 +000070
Antonio Maioranod060f362022-07-29 17:12:01 +000071/// Resolves to the underlying type for a Number.
72template <typename T>
Ben Clayton66805b02023-06-14 22:00:01 +000073using UnwrapNumber = typename tint::detail::NumberUnwrapper<T>::type;
Antonio Maioranod060f362022-07-29 17:12:01 +000074
Antonio Maiorano3740ac62022-09-03 22:42:51 +000075/// Evaluates to true iff T or Number<T> is a floating-point type or is NumberKindF16.
Antonio Maiorano6b3f4aa2022-10-04 22:40:32 +000076template <typename T>
77constexpr bool IsFloatingPoint = std::is_floating_point_v<UnwrapNumber<T>> ||
Ben Clayton66805b02023-06-14 22:00:01 +000078 std::is_same_v<UnwrapNumber<T>, tint::detail::NumberKindF16>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000079
80/// Evaluates to true iff T or Number<T> is an integral type.
Antonio Maiorano11e25712022-09-06 18:40:33 +000081template <typename T>
82constexpr bool IsIntegral = std::is_integral_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000083
84/// Evaluates to true iff T or Number<T> is a signed integer type.
Antonio Maiorano11e25712022-09-06 18:40:33 +000085template <typename T>
86constexpr bool IsSignedIntegral =
87 std::is_integral_v<UnwrapNumber<T>> && std::is_signed_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000088
89/// Evaluates to true iff T or Number<T> is an unsigned integer type.
Antonio Maiorano11e25712022-09-06 18:40:33 +000090template <typename T>
91constexpr bool IsUnsignedIntegral =
92 std::is_integral_v<UnwrapNumber<T>> && std::is_unsigned_v<UnwrapNumber<T>>;
Antonio Maiorano3740ac62022-09-03 22:42:51 +000093
94/// Evaluates to true iff T is an integer type, floating-point type or is NumberKindF16.
95template <typename T>
96constexpr bool IsNumeric = IsIntegral<T> || IsFloatingPoint<T>;
97
Antonio Maiorano5f33fac2022-09-23 21:58:29 +000098/// Returns the bit width of T
99template <typename T>
100constexpr size_t BitWidth = sizeof(UnwrapNumber<T>) * 8;
101
Antonio Maioranod060f362022-07-29 17:12:01 +0000102/// NumberBase is a CRTP base class for Number<T>
103template <typename NumberT>
104struct NumberBase {
105 /// @returns value of type `Number<T>` with the highest value for that type.
106 static NumberT Highest() { return NumberT(NumberT::kHighestValue); }
107 /// @returns value of type `Number<T>` with the lowest value for that type.
108 static NumberT Lowest() { return NumberT(NumberT::kLowestValue); }
109 /// @returns value of type `Number<T>` with the smallest value for that type.
110 static NumberT Smallest() { return NumberT(NumberT::kSmallestValue); }
111 /// @returns value of type `Number<T>` that represents NaN for that type.
112 static NumberT NaN() {
113 return NumberT(std::numeric_limits<UnwrapNumber<NumberT>>::quiet_NaN());
114 }
115 /// @returns value of type `Number<T>` that represents infinity for that type.
116 static NumberT Inf() { return NumberT(std::numeric_limits<UnwrapNumber<NumberT>>::infinity()); }
117};
118
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000119/// Number wraps a integer or floating point number, enforcing explicit casting.
120template <typename T>
Antonio Maioranod060f362022-07-29 17:12:01 +0000121struct Number : NumberBase<Number<T>> {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000122 static_assert(IsNumeric<T>, "Number<T> constructed with non-numeric type");
123
124 /// type is the underlying type of the Number
125 using type = T;
126
Ben Clayton46ee6392022-11-09 22:04:11 +0000127 /// Number of bits in the number.
128 static constexpr size_t kNumBits = sizeof(T) * 8;
129
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000130 /// Highest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000131 static constexpr type kHighestValue = std::numeric_limits<type>::max();
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000132
133 /// Lowest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000134 static constexpr type kLowestValue = std::numeric_limits<type>::lowest();
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000135
136 /// Smallest positive normal value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000137 static constexpr type kSmallestValue =
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000138 std::is_integral_v<type> ? 0 : std::numeric_limits<type>::min();
139
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000140 /// Smallest positive subnormal value of this type, 0 for integral type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000141 static constexpr type kSmallestSubnormalValue =
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000142 std::is_integral_v<type> ? 0 : std::numeric_limits<type>::denorm_min();
143
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000144 /// Constructor. The value is zero-initialized.
145 Number() = default;
146
147 /// Constructor.
148 /// @param v the value to initialize this Number to
Ben Clayton41285aa2022-05-10 14:55:34 +0000149 template <typename U>
150 explicit Number(U v) : value(static_cast<T>(v)) {}
151
152 /// Constructor.
153 /// @param v the value to initialize this Number to
154 template <typename U>
155 explicit Number(Number<U> v) : value(static_cast<T>(v.value)) {}
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000156
157 /// Conversion operator
158 /// @returns the value as T
159 operator T() const { return value; }
160
Ben Clayton636e3d02022-05-10 16:02:06 +0000161 /// Negation operator
162 /// @returns the negative value of the number
163 Number operator-() const { return Number(-value); }
164
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000165 /// Assignment operator
166 /// @param v the new value
167 /// @returns this Number so calls can be chained
168 Number& operator=(T v) {
169 value = v;
170 return *this;
171 }
172
173 /// The number value
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000174 type value = {};
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000175};
176
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000177/// Writes the number to the ostream.
dan sinclair6cc183c2023-03-02 21:28:45 +0000178/// @param out the stream to write to
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000179/// @param num the Number
dan sinclair6cc183c2023-03-02 21:28:45 +0000180/// @return the stream so calls can be chained
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000181template <typename T>
dan sinclair6cc183c2023-03-02 21:28:45 +0000182inline utils::StringStream& operator<<(utils::StringStream& out, Number<T> num) {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000183 return out << num.value;
184}
185
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000186/// The partial specification of Number for f16 type, storing the f16 value as float,
187/// and enforcing proper explicit casting.
188template <>
Ben Clayton66805b02023-06-14 22:00:01 +0000189struct Number<tint::detail::NumberKindF16> : NumberBase<Number<tint::detail::NumberKindF16>> {
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000190 /// C++ does not have a native float16 type, so we use a 32-bit float instead.
191 using type = float;
192
Ben Clayton46ee6392022-11-09 22:04:11 +0000193 /// Number of bits in the number.
194 static constexpr size_t kNumBits = 16;
195
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000196 /// Highest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000197 static constexpr type kHighestValue = 65504.0f; // 2¹⁵ × (1 + 1023/1024)
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000198
199 /// Lowest finite representable value of this type.
Antonio Maioranod060f362022-07-29 17:12:01 +0000200 static constexpr type kLowestValue = -65504.0f;
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000201
202 /// Smallest positive normal value of this type.
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000203 /// binary16 0_00001_0000000000, value is 2⁻¹⁴.
Antonio Maioranod060f362022-07-29 17:12:01 +0000204 static constexpr type kSmallestValue = 0x1p-14f;
Zhaoming Jiang0fb4e2c2022-06-10 18:18:35 +0000205
206 /// Smallest positive subnormal value of this type.
207 /// binary16 0_00000_0000000001, value is 2⁻¹⁴ * 2⁻¹⁰ = 2⁻²⁴.
Antonio Maioranod060f362022-07-29 17:12:01 +0000208 static constexpr type kSmallestSubnormalValue = 0x1p-24f;
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000209
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000210 /// Constructor. The value is zero-initialized.
211 Number() = default;
212
213 /// Constructor.
214 /// @param v the value to initialize this Number to
215 template <typename U>
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000216 explicit Number(U v) : value(Quantize(static_cast<type>(v))) {}
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000217
218 /// Constructor.
219 /// @param v the value to initialize this Number to
220 template <typename U>
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000221 explicit Number(Number<U> v) : value(Quantize(static_cast<type>(v.value))) {}
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000222
223 /// Conversion operator
224 /// @returns the value as the internal representation type of F16
225 operator float() const { return value; }
226
227 /// Negation operator
228 /// @returns the negative value of the number
Ben Clayton66805b02023-06-14 22:00:01 +0000229 Number operator-() const { return Number<tint::detail::NumberKindF16>(-value); }
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000230
231 /// Assignment operator with parameter as native floating point type
232 /// @param v the new value
233 /// @returns this Number so calls can be chained
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000234 Number& operator=(type v) {
235 value = Quantize(v);
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000236 return *this;
237 }
238
Zhaoming Jiang2c7440a2022-07-07 03:29:11 +0000239 /// Get the binary16 bit pattern in type uint16_t of this value.
240 /// @returns the binary16 bit pattern, in type uint16_t, of the stored quantized f16 value. If
241 /// the value is NaN, the returned value will be 0x7e00u. If the value is positive infinity, the
242 /// returned value will be 0x7c00u. If the input value is negative infinity, the returned value
243 /// will be 0xfc00u.
244 uint16_t BitsRepresentation() const;
245
dan sinclair00d0fd52022-11-09 20:03:09 +0000246 /// Creates an f16 value from the uint16_t bit representation.
247 /// @param bits the bits to convert from
248 /// @returns the binary16 value based off the provided bit pattern.
Ben Clayton66805b02023-06-14 22:00:01 +0000249 static Number<tint::detail::NumberKindF16> FromBits(uint16_t bits);
dan sinclair00d0fd52022-11-09 20:03:09 +0000250
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000251 /// @param value the input float32 value
252 /// @returns the float32 value quantized to the smaller float16 value, through truncation of the
253 /// mantissa bits (no rounding). If the float32 value is too large (positive or negative) to be
254 /// represented by a float16 value, then the returned value will be positive or negative
255 /// infinity.
256 static type Quantize(type value);
257
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000258 /// The number value, stored as float
Ben Claytonc2eccfc2022-05-25 15:04:24 +0000259 type value = {};
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000260};
261
Ben Clayton508e4a52022-05-10 22:08:04 +0000262/// `AInt` is a type alias to `Number<int64_t>`.
263using AInt = Number<int64_t>;
264/// `AFloat` is a type alias to `Number<double>`.
265using AFloat = Number<double>;
266
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000267/// `i32` is a type alias to `Number<int32_t>`.
268using i32 = Number<int32_t>;
269/// `u32` is a type alias to `Number<uint32_t>`.
270using u32 = Number<uint32_t>;
Ben Clayton41285aa2022-05-10 14:55:34 +0000271/// `f32` is a type alias to `Number<float>`
272using f32 = Number<float>;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000273/// `f16` is a type alias to `Number<detail::NumberKindF16>`, which should be IEEE 754 binary16.
274/// However since C++ don't have native binary16 type, the value is stored as float.
Ben Clayton66805b02023-06-14 22:00:01 +0000275using f16 = Number<tint::detail::NumberKindF16>;
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000276
David Neto27c6d982023-06-19 16:49:10 +0000277/// The algorithms in this module require support for infinity and quiet NaNs on
278/// floating point types.
279static_assert(std::numeric_limits<float>::has_infinity);
280static_assert(std::numeric_limits<float>::has_quiet_NaN);
281static_assert(std::numeric_limits<double>::has_infinity);
282static_assert(std::numeric_limits<double>::has_quiet_NaN);
283
dan sinclair05590052023-04-19 16:52:46 +0000284template <typename T, utils::traits::EnableIf<IsFloatingPoint<T>>* = nullptr>
dan sinclairefe9c492022-11-21 18:00:01 +0000285inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
286
Antonio Maiorano5f33fac2022-09-23 21:58:29 +0000287/// True iff T is an abstract number type
288template <typename T>
289constexpr bool IsAbstract = std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>;
290
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000291/// @returns the friendly name of Number type T
dan sinclair05590052023-04-19 16:52:46 +0000292template <typename T, utils::traits::EnableIf<IsNumber<T>>* = nullptr>
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000293const char* FriendlyName() {
294 if constexpr (std::is_same_v<T, AInt>) {
295 return "abstract-int";
296 } else if constexpr (std::is_same_v<T, AFloat>) {
297 return "abstract-float";
298 } else if constexpr (std::is_same_v<T, i32>) {
299 return "i32";
300 } else if constexpr (std::is_same_v<T, u32>) {
301 return "u32";
302 } else if constexpr (std::is_same_v<T, f32>) {
303 return "f32";
304 } else if constexpr (std::is_same_v<T, f16>) {
305 return "f16";
306 } else {
307 static_assert(!sizeof(T), "Unhandled type");
308 }
309}
310
Antonio Maiorano29aa6132022-09-07 19:34:44 +0000311/// @returns the friendly name of T when T is bool
dan sinclair05590052023-04-19 16:52:46 +0000312template <typename T, utils::traits::EnableIf<std::is_same_v<T, bool>>* = nullptr>
Antonio Maiorano29aa6132022-09-07 19:34:44 +0000313const char* FriendlyName() {
314 return "bool";
315}
316
Ben Clayton61537d32022-05-31 13:14:29 +0000317/// Enumerator of failure reasons when converting from one number to another.
318enum class ConversionFailure {
319 kExceedsPositiveLimit, // The value was too big (+'ve) to fit in the target type
320 kExceedsNegativeLimit, // The value was too big (-'ve) to fit in the target type
321};
322
323/// Writes the conversion failure message to the ostream.
dan sinclair6cc183c2023-03-02 21:28:45 +0000324/// @param out the stream to write to
Ben Clayton61537d32022-05-31 13:14:29 +0000325/// @param failure the ConversionFailure
dan sinclair6cc183c2023-03-02 21:28:45 +0000326/// @return the stream so calls can be chained
327utils::StringStream& operator<<(utils::StringStream& out, ConversionFailure failure);
Ben Clayton61537d32022-05-31 13:14:29 +0000328
329/// Converts a number from one type to another, checking that the value fits in the target type.
330/// @returns the resulting value of the conversion, or a failure reason.
331template <typename TO, typename FROM>
332utils::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
Ben Clayton2b4df782022-06-29 11:31:41 +0000333 // Use the highest-precision integer or floating-point type to perform the comparisons.
334 using T = std::conditional_t<IsFloatingPoint<UnwrapNumber<TO>> || IsFloatingPoint<FROM>,
335 AFloat::type, AInt::type>;
Ben Clayton61537d32022-05-31 13:14:29 +0000336 const auto value = static_cast<T>(num.value);
Antonio Maioranod060f362022-07-29 17:12:01 +0000337 if (value > static_cast<T>(TO::kHighestValue)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000338 return ConversionFailure::kExceedsPositiveLimit;
339 }
Antonio Maioranod060f362022-07-29 17:12:01 +0000340 if (value < static_cast<T>(TO::kLowestValue)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000341 return ConversionFailure::kExceedsNegativeLimit;
342 }
343 return TO(value); // Success
344}
345
Ben Claytoncf52af72022-06-29 17:09:11 +0000346/// Equality operator.
347/// @param a the LHS number
348/// @param b the RHS number
349/// @returns true if the numbers `a` and `b` are exactly equal. Also considers sign bit.
350template <typename A, typename B>
351bool operator==(Number<A> a, Number<B> b) {
352 // Use the highest-precision integer or floating-point type to perform the comparisons.
353 using T =
354 std::conditional_t<IsFloatingPoint<A> || IsFloatingPoint<B>, AFloat::type, AInt::type>;
355 auto va = static_cast<T>(a.value);
356 auto vb = static_cast<T>(b.value);
357 if constexpr (IsFloatingPoint<T>) {
358 if (std::signbit(va) != std::signbit(vb)) {
359 return false;
360 }
361 }
362 return std::equal_to<T>()(va, vb);
363}
364
365/// Inequality operator.
366/// @param a the LHS number
367/// @param b the RHS number
368/// @returns true if the numbers `a` and `b` are exactly unequal. Also considers sign bit.
369template <typename A, typename B>
370bool operator!=(Number<A> a, Number<B> b) {
371 return !(a == b);
372}
373
374/// Equality operator.
375/// @param a the LHS number
376/// @param b the RHS number
377/// @returns true if the numbers `a` and `b` are exactly equal.
378template <typename A, typename B>
379std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
380 return a == Number<B>(b);
381}
382
383/// Inequality operator.
384/// @param a the LHS number
385/// @param b the RHS number
386/// @returns true if the numbers `a` and `b` are exactly unequal.
387template <typename A, typename B>
388std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
389 return !(a == b);
390}
391
392/// Equality operator.
393/// @param a the LHS number
394/// @param b the RHS number
395/// @returns true if the numbers `a` and `b` are exactly equal.
396template <typename A, typename B>
397std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
398 return Number<A>(a) == b;
399}
400
401/// Inequality operator.
402/// @param a the LHS number
403/// @param b the RHS number
404/// @returns true if the numbers `a` and `b` are exactly unequal.
405template <typename A, typename B>
406std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
407 return !(a == b);
408}
409
Ben Clayton61537d32022-05-31 13:14:29 +0000410/// Define 'TINT_HAS_OVERFLOW_BUILTINS' if the compiler provide overflow checking builtins.
411/// If the compiler does not support these builtins, then these are emulated with algorithms
412/// described in:
413/// https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
414#if defined(__GNUC__) && __GNUC__ >= 5
415#define TINT_HAS_OVERFLOW_BUILTINS
416#elif defined(__clang__)
417#if __has_builtin(__builtin_add_overflow) && __has_builtin(__builtin_mul_overflow)
418#define TINT_HAS_OVERFLOW_BUILTINS
419#endif
420#endif
421
Antonio Maiorano7f5b9d02022-12-13 16:29:06 +0000422// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635
423TINT_BEGIN_DISABLE_WARNING(MAYBE_UNINITIALIZED);
424
Ben Clayton61537d32022-05-31 13:14:29 +0000425/// @returns a + b, or an empty optional if the resulting value overflowed the AInt
426inline std::optional<AInt> CheckedAdd(AInt a, AInt b) {
427 int64_t result;
428#ifdef TINT_HAS_OVERFLOW_BUILTINS
429 if (__builtin_add_overflow(a.value, b.value, &result)) {
430 return {};
431 }
432#else // TINT_HAS_OVERFLOW_BUILTINS
433 if (a.value >= 0) {
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000434 if (b.value > AInt::kHighestValue - a.value) {
Ben Clayton61537d32022-05-31 13:14:29 +0000435 return {};
436 }
437 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000438 if (b.value < AInt::kLowestValue - a.value) {
Ben Clayton61537d32022-05-31 13:14:29 +0000439 return {};
440 }
441 }
442 result = a.value + b.value;
443#endif // TINT_HAS_OVERFLOW_BUILTINS
444 return AInt(result);
445}
446
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000447/// @returns a + b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000448template <typename FloatingPointT,
449 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000450inline std::optional<FloatingPointT> CheckedAdd(FloatingPointT a, FloatingPointT b) {
451 auto result = FloatingPointT{a.value + b.value};
452 if (!std::isfinite(result.value)) {
Antonio Maiorano50940ae2022-08-03 21:20:26 +0000453 return {};
454 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000455 return result;
Antonio Maiorano50940ae2022-08-03 21:20:26 +0000456}
457
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000458/// @returns a - b, or an empty optional if the resulting value overflowed the AInt
459inline std::optional<AInt> CheckedSub(AInt a, AInt b) {
460 int64_t result;
461#ifdef TINT_HAS_OVERFLOW_BUILTINS
462 if (__builtin_sub_overflow(a.value, b.value, &result)) {
463 return {};
464 }
465#else // TINT_HAS_OVERFLOW_BUILTINS
466 if (b.value >= 0) {
467 if (a.value < AInt::kLowestValue + b.value) {
468 return {};
469 }
470 } else {
471 if (a.value > AInt::kHighestValue + b.value) {
472 return {};
473 }
474 }
475 result = a.value - b.value;
476#endif // TINT_HAS_OVERFLOW_BUILTINS
477 return AInt(result);
478}
479
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000480/// @returns a + b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000481template <typename FloatingPointT,
482 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000483inline std::optional<FloatingPointT> CheckedSub(FloatingPointT a, FloatingPointT b) {
484 auto result = FloatingPointT{a.value - b.value};
485 if (!std::isfinite(result.value)) {
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000486 return {};
487 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000488 return result;
Antonio Maioranof8a34d02022-08-17 12:46:39 +0000489}
490
Ben Clayton61537d32022-05-31 13:14:29 +0000491/// @returns a * b, or an empty optional if the resulting value overflowed the AInt
492inline std::optional<AInt> CheckedMul(AInt a, AInt b) {
493 int64_t result;
494#ifdef TINT_HAS_OVERFLOW_BUILTINS
495 if (__builtin_mul_overflow(a.value, b.value, &result)) {
496 return {};
497 }
498#else // TINT_HAS_OVERFLOW_BUILTINS
499 if (a > 0) {
500 if (b > 0) {
Antonio Maioranod060f362022-07-29 17:12:01 +0000501 if (a > (AInt::kHighestValue / b)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000502 return {};
503 }
504 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000505 if (b < (AInt::kLowestValue / a)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000506 return {};
507 }
508 }
509 } else {
510 if (b > 0) {
Antonio Maioranod060f362022-07-29 17:12:01 +0000511 if (a < (AInt::kLowestValue / b)) {
Ben Clayton61537d32022-05-31 13:14:29 +0000512 return {};
513 }
514 } else {
Antonio Maioranod060f362022-07-29 17:12:01 +0000515 if ((a != 0) && (b < (AInt::kHighestValue / a))) {
Ben Clayton61537d32022-05-31 13:14:29 +0000516 return {};
517 }
518 }
519 }
520 result = a.value * b.value;
521#endif // TINT_HAS_OVERFLOW_BUILTINS
522 return AInt(result);
523}
524
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000525/// @returns a * b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000526template <typename FloatingPointT,
527 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000528inline std::optional<FloatingPointT> CheckedMul(FloatingPointT a, FloatingPointT b) {
529 auto result = FloatingPointT{a.value * b.value};
530 if (!std::isfinite(result.value)) {
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000531 return {};
532 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000533 return result;
Antonio Maioranoc20c5df2022-09-01 14:57:39 +0000534}
535
Antonio Maiorano1741f442022-09-03 21:31:23 +0000536/// @returns a / b, or an empty optional if the resulting value overflowed the AInt
537inline std::optional<AInt> CheckedDiv(AInt a, AInt b) {
538 if (b == 0) {
539 return {};
540 }
541
542 if (b == -1 && a == AInt::Lowest()) {
543 return {};
544 }
545
546 return AInt{a.value / b.value};
547}
548
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000549/// @returns a / b, or an empty optional if the resulting value overflowed the float value
dan sinclair05590052023-04-19 16:52:46 +0000550template <typename FloatingPointT,
551 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000552inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT b) {
Antonio Maioranodeb2ec92023-03-24 18:44:02 +0000553 if (b == FloatingPointT{0.0} || b == FloatingPointT{-0.0}) {
554 return {};
555 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000556 auto result = FloatingPointT{a.value / b.value};
557 if (!std::isfinite(result.value)) {
Antonio Maiorano1741f442022-09-03 21:31:23 +0000558 return {};
559 }
Antonio Maiorano4d89ce12022-11-16 21:51:31 +0000560 return result;
Antonio Maiorano1741f442022-09-03 21:31:23 +0000561}
562
Antonio Maioranocfea2202022-11-30 14:18:51 +0000563namespace detail {
564/// @returns the remainder of e1 / e2
565template <typename T>
566inline T Mod(T e1, T e2) {
Antonio Maiorano875d1162022-12-05 17:16:15 +0000567 if constexpr (IsIntegral<T>) {
568 return e1 % e2;
569
570 } else {
571 return e1 - e2 * std::trunc(e1 / e2);
572 }
Antonio Maioranocfea2202022-11-30 14:18:51 +0000573}
574} // namespace detail
575
576/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the AInt
577inline std::optional<AInt> CheckedMod(AInt a, AInt b) {
578 if (b == 0) {
579 return {};
580 }
581
582 if (b == -1 && a == AInt::Lowest()) {
583 return {};
584 }
585
Ben Clayton66805b02023-06-14 22:00:01 +0000586 return AInt{tint::detail::Mod(a.value, b.value)};
Antonio Maioranocfea2202022-11-30 14:18:51 +0000587}
588
589/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the
590/// float value
dan sinclair05590052023-04-19 16:52:46 +0000591template <typename FloatingPointT,
592 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maioranocfea2202022-11-30 14:18:51 +0000593inline std::optional<FloatingPointT> CheckedMod(FloatingPointT a, FloatingPointT b) {
Antonio Maioranodeb2ec92023-03-24 18:44:02 +0000594 if (b == FloatingPointT{0.0} || b == FloatingPointT{-0.0}) {
595 return {};
596 }
Ben Clayton66805b02023-06-14 22:00:01 +0000597 auto result = FloatingPointT{tint::detail::Mod(a.value, b.value)};
Antonio Maioranocfea2202022-11-30 14:18:51 +0000598 if (!std::isfinite(result.value)) {
599 return {};
600 }
601 return result;
602}
603
Ben Clayton61537d32022-05-31 13:14:29 +0000604/// @returns a * b + c, or an empty optional if the value overflowed the AInt
605inline std::optional<AInt> CheckedMadd(AInt a, AInt b, AInt c) {
Ben Clayton61537d32022-05-31 13:14:29 +0000606 if (auto mul = CheckedMul(a, b)) {
607 return CheckedAdd(mul.value(), c);
608 }
609 return {};
Ben Clayton61537d32022-05-31 13:14:29 +0000610}
611
Antonio Maiorano7f5b9d02022-12-13 16:29:06 +0000612/// @returns the value of `base` raised to the power `exp`, or an empty optional if the operation
613/// cannot be performed.
dan sinclair05590052023-04-19 16:52:46 +0000614template <typename FloatingPointT,
615 typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
Antonio Maiorano7f5b9d02022-12-13 16:29:06 +0000616inline std::optional<FloatingPointT> CheckedPow(FloatingPointT base, FloatingPointT exp) {
617 static_assert(IsNumber<FloatingPointT>);
618 if ((base < 0) || (base == 0 && exp <= 0)) {
619 return {};
620 }
621 auto result = FloatingPointT{std::pow(base.value, exp.value)};
622 if (!std::isfinite(result.value)) {
623 return {};
624 }
625 return result;
626}
627
628TINT_END_DISABLE_WARNING(MAYBE_UNINITIALIZED);
629
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000630} // namespace tint
631
632namespace tint::number_suffixes {
633
Ben Clayton508e4a52022-05-10 22:08:04 +0000634/// Literal suffix for abstract integer literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000635inline AInt operator""_a(unsigned long long int value) { // NOLINT
Ben Clayton508e4a52022-05-10 22:08:04 +0000636 return AInt(static_cast<int64_t>(value));
637}
638
639/// Literal suffix for abstract float literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000640inline AFloat operator""_a(long double value) { // NOLINT
Ben Clayton508e4a52022-05-10 22:08:04 +0000641 return AFloat(static_cast<double>(value));
642}
643
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000644/// Literal suffix for i32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000645inline i32 operator""_i(unsigned long long int value) { // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000646 return i32(static_cast<int32_t>(value));
647}
648
649/// Literal suffix for u32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000650inline u32 operator""_u(unsigned long long int value) { // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000651 return u32(static_cast<uint32_t>(value));
652}
653
Ben Clayton41285aa2022-05-10 14:55:34 +0000654/// Literal suffix for f32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000655inline f32 operator""_f(long double value) { // NOLINT
Ben Clayton41285aa2022-05-10 14:55:34 +0000656 return f32(static_cast<double>(value));
657}
658
659/// Literal suffix for f32 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000660inline f32 operator""_f(unsigned long long int value) { // NOLINT
Ben Clayton41285aa2022-05-10 14:55:34 +0000661 return f32(static_cast<double>(value));
662}
663
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000664/// Literal suffix for f16 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000665inline f16 operator""_h(long double value) { // NOLINT
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000666 return f16(static_cast<double>(value));
667}
668
669/// Literal suffix for f16 literals
Antonio Maiorano83fc2472022-05-13 20:14:57 +0000670inline f16 operator""_h(unsigned long long int value) { // NOLINT
Zhaoming Jiang62bfd312022-05-13 12:01:11 +0000671 return f16(static_cast<double>(value));
672}
673
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000674} // namespace tint::number_suffixes
675
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000676namespace std {
677
678/// Custom std::hash specialization for tint::Number<T>
679template <typename T>
680class hash<tint::Number<T>> {
681 public:
682 /// @param n the Number
683 /// @return the hash value
684 inline std::size_t operator()(const tint::Number<T>& n) const {
685 return std::hash<decltype(n.value)>()(n.value);
686 }
687};
688
689} // namespace std
690
dan sinclairb14a7782023-07-20 09:21:10 +0000691#endif // SRC_TINT_LANG_BASE_BUILTIN_NUMBER_H_