blob: bcd565bf2c3f967fc4613799d5db3ee06e6e2155 [file] [log] [blame]
/*
* Copyright 2015 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* Test the overflow builtins.
*
* This test is run on multiple compilers and makes sure they all output the
* same golden output values for overflow builtins. This is important to test
* that PNaCl (which doesn't support these builtins as-is) still generates
* correct code.
*
* The test performs addition/subtraction/multiplication of the minimum/maximum
* numbers that can be represented for each datatype supported by the
* overflow builtins.
*
* Documentation:
* clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins
*/
#include <iostream>
#include <iomanip>
#include <limits>
using namespace std;
// The compiler can do further optimizations when it sees values coming in to
// the overflow builtins. Run this test with and without inlining to make sure
// there's test coverage for overflow with constant, and overflow with variable.
#if SHOULD_INLINE
# define MAYBE_INLINE __attribute__((always_inline))
#else
# define MAYBE_INLINE __attribute__((noinline))
#endif
// Datatypes supported by the overflow builtins.
// DO(TYPE, SIGN, LENGTH)
#define FOR_EACH_TYPE(DO) \
DO(unsigned, u, ) \
DO(unsigned long long, u, ll) \
DO(int, s, ) \
DO(long long, s, ll)
static_assert(((sizeof(int) != sizeof(long)) ||
(sizeof(long) != sizeof(long long))),
"This test avoids testing long because it'll produce the same "
"output as either int or long long (depending on the platform). "
"If this weren't the case for some compilers then the golden "
"output would be different.");
// The builtins have C-style names, create C++ overloads for them.
template <typename T> MAYBE_INLINE bool add(T, T, T*);
template <typename T> MAYBE_INLINE bool sub(T, T, T*);
template <typename T> MAYBE_INLINE bool mul(T, T, T*);
#define DECLARE_OVERLOADS(TYPE, SIGN, LENGTH) \
template <> \
MAYBE_INLINE bool add<TYPE>(TYPE lhs, TYPE rhs, TYPE * res) { \
return __builtin_##SIGN##add##LENGTH##_overflow(lhs, rhs, res); \
} \
template <> \
MAYBE_INLINE bool sub<TYPE>(TYPE lhs, TYPE rhs, TYPE * res) { \
return __builtin_##SIGN##sub##LENGTH##_overflow(lhs, rhs, res); \
} \
template <> \
MAYBE_INLINE bool mul<TYPE>(TYPE lhs, TYPE rhs, TYPE * res) { \
return __builtin_##SIGN##mul##LENGTH##_overflow(lhs, rhs, res); \
}
FOR_EACH_TYPE(DECLARE_OVERLOADS)
#define TEST_SINGLE(OP, REP, LHS, RHS) \
do { \
const auto w = numeric_limits<T>::digits10 + 2; \
T res; \
cout << setw(w) << LHS << ' ' << REP << ' ' << setw(w) << RHS << " = " \
<< setw(w); \
if (OP(LHS, RHS, &res)) \
cout << "overflow"; \
else \
cout << res; \
cout << '\n'; \
} while (0)
// Test the combinations of OP(LHS, RHS). We start at the *_START values and
// iterate for `iterations` repetitions using the *_OP operator on values at
// each repetition.
#define ITERATE(OP, REP, LHS_START, RHS_START, LHS_OP, RHS_OP, ITERS) \
do { \
T lhs = LHS_START; \
for (int lhs_values = 0; lhs_values != ITERS; ++lhs_values) { \
T rhs = RHS_START; \
for (int rhs_values = 0; rhs_values != ITERS; ++rhs_values) { \
TEST_SINGLE(OP<T>, REP, lhs, rhs); \
RHS_OP rhs; \
} \
LHS_OP lhs; \
} \
} while (0)
template <typename T>
void test_type() {
int iterations = 3;
bool is_signed = numeric_limits<T>::is_signed;
T min = numeric_limits<T>::min();
T max = numeric_limits<T>::max();
cout << "\nTesting " << sizeof(T) << " byte " << (is_signed ? "" : "un")
<< "signed integer:\n";
ITERATE(add, '+', min, min, ++, ++, iterations);
ITERATE(add, '+', min, max, ++, --, iterations);
ITERATE(add, '+', max, max, --, --, iterations);
ITERATE(add, '+', max, min, --, ++, iterations);
ITERATE(sub, '-', min, min, ++, ++, iterations);
ITERATE(sub, '-', min, max, ++, --, iterations);
ITERATE(sub, '-', max, max, --, --, iterations);
ITERATE(sub, '-', max, min, --, ++, iterations);
ITERATE(mul, '*', min, min, ++, ++, iterations);
ITERATE(mul, '*', min, max, ++, --, iterations);
ITERATE(mul, '*', max, max, --, --, iterations);
ITERATE(mul, '*', max, min, --, ++, iterations);
if (is_signed) {
ITERATE(add, '+', 0, min, (void), ++, iterations);
ITERATE(add, '+', 0, max, (void), --, iterations);
ITERATE(add, '+', min, 0, ++, (void), iterations);
ITERATE(add, '+', max, 0, --, (void), iterations);
ITERATE(sub, '-', 0, min, (void), ++, iterations);
ITERATE(sub, '-', 0, max, (void), --, iterations);
ITERATE(sub, '-', min, 0, ++, (void), iterations);
ITERATE(sub, '-', max, 0, --, (void), iterations);
ITERATE(mul, '*', 0, min, (void), ++, iterations);
ITERATE(mul, '*', 0, max, (void), --, iterations);
ITERATE(mul, '*', min, 0, ++, (void), iterations);
ITERATE(mul, '*', max, 0, --, (void), iterations);
}
}
int main() {
#define TEST_TYPE(TYPE, SIGN, LENGTH) test_type<TYPE>();
FOR_EACH_TYPE(TEST_TYPE)
return 0;
}