Skip to content

Instantly share code, notes, and snippets.

@tstellar
Created July 28, 2020 01:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tstellar/80dae2ab8a18d810b10b8e42777f4fe4 to your computer and use it in GitHub Desktop.
Save tstellar/80dae2ab8a18d810b10b8e42777f4fe4 to your computer and use it in GitHub Desktop.
// Test driver for https://reviews.llvm.org/D84405
//
// based on similar driver for https://reviews.llvm.org/D41149
#include <cstdint>
#include <limits>
#include <iostream>
#include <vector>
// Instructions:
// - Compile this program with a baseline compiler and a patched compiler.
// - Check that the output of the two executables is the same.
// - Flags: -std=c++11 -O3
// Generate interesting test inputs for mul<S, U>(S a, U b).
template<typename S, typename U0, typename U1>
std::vector<std::pair<U0, U1>> gen() {
std::vector<U0> Inputs0;
std::vector<U1> Inputs1;
// Create some interesting signed and unsigned numbers.
for (int i = 0; i < 50; ++i) {
Inputs0.push_back(i);
Inputs1.push_back(i);
}
for (int i = 1; i < 50; ++i) { // {MIN, MAX} / 1..49
Inputs0.push_back(std::numeric_limits<U0>::min() / i);
Inputs1.push_back(std::numeric_limits<U1>::max() / i);
}
// Add an edge case where the product is greater than sign max but less than
// unsigned max.
if (std::numeric_limits<S>::max() < std::numeric_limits<U0>::max()) {
U0 SignedMax = std::numeric_limits<S>::max();
U0 SignedMaxNextEven = SignedMax % 2 == 0 ? SignedMax + 2 : SignedMax + 1;
U0 Divisor = SignedMaxNextEven / 2;
Inputs0.push_back(Divisor);
Inputs1.push_back(2);
}
if (std::numeric_limits<S>::max() < std::numeric_limits<U1>::max()) {
U1 SignedMax = std::numeric_limits<S>::max();
U1 SignedMaxNextEven = SignedMax % 2 == 0 ? SignedMax + 2 : SignedMax + 1;
U1 Divisor = SignedMaxNextEven / 2;
Inputs0.push_back(2);
Inputs1.push_back(Divisor);
}
// Take the cartesian product of the interesting signed and unsigned numbers.
std::vector<std::pair<U0, U1>> Inputs;
for (U0 i : Inputs0)
for (U1 u : Inputs1)
Inputs.emplace_back(i, u);
return Inputs;
}
// Multiply a * b. Print out the result if there's no overflow. Return true
// iff there's an overflow.
template<typename S, typename U0, typename U1>
bool mul(U0 a, U1 b) {
S res;
bool oflow = __builtin_mul_overflow(a, b, &res);
std::cout << a << " * " << b << " = " << res << "\toflow: " << oflow << std::endl;
return oflow;
}
// Compute some interesting checked multiplications given the following types:
//
// S: signed type
// U: unsigned type
// R: result type
//
// Print out the number of overflows detected.
template<typename S, typename U0, typename U1>
void test_mul() {
uint64_t NumOverflows = 0;
for (auto I : gen<S, U0, U1>())
if (mul<S, U0, U1>(I.first, I.second))
++NumOverflows;
std::cout << "# overflows = " << NumOverflows << std::endl;
}
// Test 6 different result types for each pair of signed & unsigned types.
#define R(SignedTy, UnsignedTy) \
test_mul<SignedTy, UnsignedTy, uint16_t>(); \
test_mul<SignedTy, UnsignedTy, uint32_t>(); \
test_mul<SignedTy, UnsignedTy, uint64_t>();
// Test 3 different unsigned types for each signed type.
#define U(SignedTy) \
R(SignedTy, uint16_t) \
R(SignedTy, uint32_t) \
R(SignedTy, uint64_t)
// Test 3 different signed types.
#define S() \
U(int16_t) \
U(int32_t) \
U(int64_t)
int main() {
// S(); // Test 54 different combinations of signed, unsigned, and result types.
//
//
// clang 10 can only compile these combinations. (The rest generate __muloti4 calls
// and cause the test program to fail to link).
test_mul<int32_t, uint32_t, uint32_t>();
test_mul<int16_t, uint16_t, uint16_t>();
// clang 11 with this patch can only test the clang 10 combinations plus these:
test_mul<int64_t, uint64_t, uint64_t>();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment