Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: base/numerics/safe_math_impl.h

Issue 2509233003: Use GCC/Clang builtins for Checked(Add|Sub|Mul) (Closed)
Patch Set: clang compile fix (last time) Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 struct IsIntegerArithmeticSafe { 249 struct IsIntegerArithmeticSafe {
250 static const bool value = !std::numeric_limits<T>::is_iec559 && 250 static const bool value = !std::numeric_limits<T>::is_iec559 &&
251 StaticDstRangeRelationToSrcRange<T, Lhs>::value == 251 StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
252 NUMERIC_RANGE_CONTAINED && 252 NUMERIC_RANGE_CONTAINED &&
253 sizeof(T) >= (2 * sizeof(Lhs)) && 253 sizeof(T) >= (2 * sizeof(Lhs)) &&
254 StaticDstRangeRelationToSrcRange<T, Rhs>::value != 254 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
255 NUMERIC_RANGE_CONTAINED && 255 NUMERIC_RANGE_CONTAINED &&
256 sizeof(T) >= (2 * sizeof(Rhs)); 256 sizeof(T) >= (2 * sizeof(Rhs));
257 }; 257 };
258 258
259 // Probe for builtin math overflow support on Clang and version check on GCC.
260 #if defined(__has_builtin)
261 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
262 #elif defined(__GNUC__)
263 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5)
264 #else
265 #define USE_OVERFLOW_BUILTINS (0)
266 #endif
267
259 // Here are the actual portable checked integer math implementations. 268 // Here are the actual portable checked integer math implementations.
260 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean 269 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
261 // way to coalesce things into the CheckedNumericState specializations below. 270 // way to coalesce things into the CheckedNumericState specializations below.
262 271
263 template <typename T> 272 template <typename T>
264 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 273 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
265 CheckedAddImpl(T x, T y, T* result) { 274 CheckedAddImpl(T x, T y, T* result) {
266 // Since the value of x+y is undefined if we have a signed type, we compute 275 // Since the value of x+y is undefined if we have a signed type, we compute
267 // it using the unsigned type of the same size. 276 // it using the unsigned type of the same size.
268 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 277 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
269 UnsignedDst ux = static_cast<UnsignedDst>(x); 278 UnsignedDst ux = static_cast<UnsignedDst>(x);
270 UnsignedDst uy = static_cast<UnsignedDst>(y); 279 UnsignedDst uy = static_cast<UnsignedDst>(y);
271 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); 280 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
272 *result = static_cast<T>(uresult); 281 *result = static_cast<T>(uresult);
273 // Addition is valid if the sign of (x + y) is equal to either that of x or 282 // Addition is valid if the sign of (x + y) is equal to either that of x or
274 // that of y. 283 // that of y.
275 return (std::numeric_limits<T>::is_signed) 284 return (std::numeric_limits<T>::is_signed)
276 ? HasSignBit(BinaryComplement( 285 ? HasSignBit(BinaryComplement(
277 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) 286 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
278 : (BinaryComplement(x) >= 287 : (BinaryComplement(x) >=
279 y); // Unsigned is either valid or underflow. 288 y); // Unsigned is either valid or underflow.
280 } 289 }
281 290
282 template <typename T, typename U, typename V> 291 template <typename T, typename U, typename V>
283 typename std::enable_if<std::numeric_limits<T>::is_integer && 292 typename std::enable_if<std::numeric_limits<T>::is_integer &&
284 std::numeric_limits<U>::is_integer && 293 std::numeric_limits<U>::is_integer &&
285 std::numeric_limits<V>::is_integer, 294 std::numeric_limits<V>::is_integer,
286 bool>::type 295 bool>::type
287 CheckedAdd(T x, U y, V* result) { 296 CheckedAdd(T x, U y, V* result) {
297 #if USE_OVERFLOW_BUILTINS
298 return !__builtin_add_overflow(x, y, result);
299 #else
288 using Promotion = 300 using Promotion =
289 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 301 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
290 Promotion presult; 302 Promotion presult;
291 // Fail if either operand is out of range for the promoted type. 303 // Fail if either operand is out of range for the promoted type.
292 // TODO(jschuh): This could be made to work for a broader range of values. 304 // TODO(jschuh): This could be made to work for a broader range of values.
293 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 305 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
294 IsValueInRangeForNumericType<Promotion>(y); 306 IsValueInRangeForNumericType<Promotion>(y);
295 307
296 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 308 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) {
297 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); 309 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
298 } else { 310 } else {
299 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), 311 is_valid &= CheckedAddImpl(static_cast<Promotion>(x),
300 static_cast<Promotion>(y), &presult); 312 static_cast<Promotion>(y), &presult);
301 } 313 }
302 *result = static_cast<V>(presult); 314 *result = static_cast<V>(presult);
303 return is_valid && IsValueInRangeForNumericType<V>(presult); 315 return is_valid && IsValueInRangeForNumericType<V>(presult);
316 #endif
304 } 317 }
305 318
306 template <typename T> 319 template <typename T>
307 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 320 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
308 CheckedSubImpl(T x, T y, T* result) { 321 CheckedSubImpl(T x, T y, T* result) {
309 // Since the value of x+y is undefined if we have a signed type, we compute 322 // Since the value of x+y is undefined if we have a signed type, we compute
310 // it using the unsigned type of the same size. 323 // it using the unsigned type of the same size.
311 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; 324 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
312 UnsignedDst ux = static_cast<UnsignedDst>(x); 325 UnsignedDst ux = static_cast<UnsignedDst>(x);
313 UnsignedDst uy = static_cast<UnsignedDst>(y); 326 UnsignedDst uy = static_cast<UnsignedDst>(y);
314 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); 327 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
315 *result = static_cast<T>(uresult); 328 *result = static_cast<T>(uresult);
316 // Subtraction is valid if either x and y have same sign, or (x-y) and x have 329 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
317 // the same sign. 330 // the same sign.
318 return (std::numeric_limits<T>::is_signed) 331 return (std::numeric_limits<T>::is_signed)
319 ? HasSignBit(BinaryComplement( 332 ? HasSignBit(BinaryComplement(
320 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) 333 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
321 : (x >= y); 334 : (x >= y);
322 } 335 }
323 336
324 template <typename T, typename U, typename V> 337 template <typename T, typename U, typename V>
325 typename std::enable_if<std::numeric_limits<T>::is_integer && 338 typename std::enable_if<std::numeric_limits<T>::is_integer &&
326 std::numeric_limits<U>::is_integer && 339 std::numeric_limits<U>::is_integer &&
327 std::numeric_limits<V>::is_integer, 340 std::numeric_limits<V>::is_integer,
328 bool>::type 341 bool>::type
329 CheckedSub(T x, U y, V* result) { 342 CheckedSub(T x, U y, V* result) {
343 #if USE_OVERFLOW_BUILTINS
344 return !__builtin_sub_overflow(x, y, result);
345 #else
330 using Promotion = 346 using Promotion =
331 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 347 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
332 Promotion presult; 348 Promotion presult;
333 // Fail if either operand is out of range for the promoted type. 349 // Fail if either operand is out of range for the promoted type.
334 // TODO(jschuh): This could be made to work for a broader range of values. 350 // TODO(jschuh): This could be made to work for a broader range of values.
335 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 351 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
336 IsValueInRangeForNumericType<Promotion>(y); 352 IsValueInRangeForNumericType<Promotion>(y);
337 353
338 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 354 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) {
339 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); 355 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
340 } else { 356 } else {
341 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), 357 is_valid &= CheckedSubImpl(static_cast<Promotion>(x),
342 static_cast<Promotion>(y), &presult); 358 static_cast<Promotion>(y), &presult);
343 } 359 }
344 *result = static_cast<V>(presult); 360 *result = static_cast<V>(presult);
345 return is_valid && IsValueInRangeForNumericType<V>(presult); 361 return is_valid && IsValueInRangeForNumericType<V>(presult);
362 #endif
346 } 363 }
347 364
348 // Integer multiplication is a bit complicated. In the fast case we just 365 // Integer multiplication is a bit complicated. In the fast case we just
349 // we just promote to a twice wider type, and range check the result. In the 366 // we just promote to a twice wider type, and range check the result. In the
350 // slow case we need to manually check that the result won't be truncated by 367 // slow case we need to manually check that the result won't be truncated by
351 // checking with division against the appropriate bound. 368 // checking with division against the appropriate bound.
352 template <typename T> 369 template <typename T>
353 typename std::enable_if<std::numeric_limits<T>::is_integer && 370 typename std::enable_if<std::numeric_limits<T>::is_integer &&
354 sizeof(T) * 2 <= sizeof(uintmax_t), 371 sizeof(T) * 2 <= sizeof(uintmax_t),
355 bool>::type 372 bool>::type
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 *result = x * y; 416 *result = x * y;
400 return (y == 0 || x <= std::numeric_limits<T>::max() / y); 417 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
401 } 418 }
402 419
403 template <typename T, typename U, typename V> 420 template <typename T, typename U, typename V>
404 typename std::enable_if<std::numeric_limits<T>::is_integer && 421 typename std::enable_if<std::numeric_limits<T>::is_integer &&
405 std::numeric_limits<U>::is_integer && 422 std::numeric_limits<U>::is_integer &&
406 std::numeric_limits<V>::is_integer, 423 std::numeric_limits<V>::is_integer,
407 bool>::type 424 bool>::type
408 CheckedMul(T x, U y, V* result) { 425 CheckedMul(T x, U y, V* result) {
426 #if USE_OVERFLOW_BUILTINS
427 #if defined(__clang__)
428 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
429 // support full-width, mixed-sign multiply builtins. https://crbug.com/613003
430 static const bool kUseMaxInt =
431 sizeof(__typeof__(x * y)) < sizeof(intptr_t) ||
432 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) &&
433 std::is_signed<T>::value == std::is_signed<U>::value);
434 #else
435 static const bool kUseMaxInt = true;
436 #endif
437 if (kUseMaxInt)
438 return !__builtin_mul_overflow(x, y, result);
439 #endif
409 using Promotion = 440 using Promotion =
410 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 441 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
411 Promotion presult; 442 Promotion presult;
412 // Fail if either operand is out of range for the promoted type. 443 // Fail if either operand is out of range for the promoted type.
413 // TODO(jschuh): This could be made to work for a broader range of values. 444 // TODO(jschuh): This could be made to work for a broader range of values.
414 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 445 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
415 IsValueInRangeForNumericType<Promotion>(y); 446 IsValueInRangeForNumericType<Promotion>(y);
416 447
417 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { 448 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) {
418 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); 449 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
419 } else { 450 } else {
420 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), 451 is_valid &= CheckedMulImpl(static_cast<Promotion>(x),
421 static_cast<Promotion>(y), &presult); 452 static_cast<Promotion>(y), &presult);
422 } 453 }
423 *result = static_cast<V>(presult); 454 *result = static_cast<V>(presult);
424 return is_valid && IsValueInRangeForNumericType<V>(presult); 455 return is_valid && IsValueInRangeForNumericType<V>(presult);
425 } 456 }
426 457
458 // Avoid poluting the namespace once we're done with the macro.
459 #undef USE_OVERFLOW_BUILTINS
460
427 // Division just requires a check for a zero denominator or an invalid negation 461 // Division just requires a check for a zero denominator or an invalid negation
428 // on signed min/-1. 462 // on signed min/-1.
429 template <typename T> 463 template <typename T>
430 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 464 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
431 CheckedDivImpl(T x, T y, T* result) { 465 CheckedDivImpl(T x, T y, T* result) {
432 if (y && (!std::numeric_limits<T>::is_signed || 466 if (y && (!std::numeric_limits<T>::is_signed ||
433 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { 467 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
434 *result = x / y; 468 *result = x / y;
435 return true; 469 return true;
436 } 470 }
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after
750 : value_(static_cast<T>(rhs.value())) {} 784 : value_(static_cast<T>(rhs.value())) {}
751 785
752 bool is_valid() const { return std::isfinite(value_); } 786 bool is_valid() const { return std::isfinite(value_); }
753 T value() const { return value_; } 787 T value() const { return value_; }
754 }; 788 };
755 789
756 } // namespace internal 790 } // namespace internal
757 } // namespace base 791 } // namespace base
758 792
759 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 793 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698