[flang] Template specializations and more automatic configuration

Original-commit: flang-compiler/f18@a230a53907
Reviewed-on: https://github.com/flang-compiler/f18/pull/101
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-06-01 11:48:31 -07:00
parent d0c120b371
commit ce91eae382
7 changed files with 195 additions and 78 deletions

View File

@ -14,4 +14,5 @@
add_library(FortranEvaluate
constant.cc
integer.cc
)

View File

@ -0,0 +1,72 @@
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FORTRAN_EVALUATE_COMMON_H_
#define FORTRAN_EVALUATE_COMMON_H_
#include <cinttypes>
namespace Fortran::evaluate {
// Integers are always ordered; reals may not be.
enum class Ordering { Less, Equal, Greater };
enum class Relation { Less, Equal, Greater, Unordered };
static constexpr Ordering Reverse(Ordering ordering) {
if (ordering == Ordering::Less) {
return Ordering::Greater;
} else if (ordering == Ordering::Greater) {
return Ordering::Less;
} else {
return Ordering::Equal;
}
}
static constexpr Relation Reverse(Relation relation) {
if (relation == Relation::Less) {
return Relation::Greater;
} else if (relation == Relation::Greater) {
return Relation::Less;
} else {
return relation;
}
}
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
constexpr bool IsHostLittleEndian{false};
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
constexpr bool IsHostLittleEndian{true};
#else
#error host endianness is not known
#endif
template<bool LE8, bool LE16, bool LE32, bool LE64> struct SmallestUInt {};
template<> struct SmallestUInt<true, true, true, true> {
using type = std::uint8_t;
};
template<> struct SmallestUInt<false, true, true, true> {
using type = std::uint16_t;
};
template<> struct SmallestUInt<false, false, true, true> {
using type = std::uint32_t;
};
template<> struct SmallestUInt<false, false, false, true> {
using type = std::uint64_t;
};
template<int BITS>
using HostUnsignedInt =
typename SmallestUInt<BITS <= 8, BITS <= 16, BITS <= 32, BITS <= 64>::type;
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_COMMON_H_

View File

@ -0,0 +1,37 @@
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "integer.h"
namespace Fortran::evaluate {
template class Integer<8>;
template class Integer<16>;
template class Integer<32>;
template class Integer<64>;
template class Integer<128>;
// Sanity checks intended to catch misconfiguration bugs
static_assert(Integer<8>::partBits == 8);
static_assert(std::is_same_v<typename Integer<8>::Part, std::uint8_t>);
static_assert(Integer<16>::partBits == 16);
static_assert(std::is_same_v<typename Integer<16>::Part, std::uint16_t>);
static_assert(Integer<32>::partBits == 32);
static_assert(std::is_same_v<typename Integer<32>::Part, std::uint32_t>);
static_assert(Integer<64>::partBits == 32);
static_assert(std::is_same_v<typename Integer<64>::Part, std::uint32_t>);
static_assert(Integer<128>::partBits == 32);
static_assert(std::is_same_v<typename Integer<128>::Part, std::uint32_t>);
} // namespace Fortran::evaluate

View File

@ -24,47 +24,42 @@
// and signed-magnitude encodings appear to be extinct in 2018.)
#include "bit-population-count.h"
#include "common.h"
#include "leading-zero-bit-count.h"
#include <cinttypes>
#include <climits>
#include <cstddef>
#include <type_traits>
namespace Fortran::evaluate {
// Integers are always ordered.
enum class Ordering { Less, Equal, Greater };
static constexpr Ordering Reverse(Ordering ordering) {
if (ordering == Ordering::Less) {
return Ordering::Greater;
} else if (ordering == Ordering::Greater) {
return Ordering::Less;
} else {
return Ordering::Equal;
}
}
// Implements an integer as an assembly of smaller integer parts.
// For best performance, the part should be half of the size of the
// largest efficient integer supported by the host processor.
// Implements an integer as an assembly of smaller host integer parts
// that constitute the digits of a large-radix fixed-point number.
// For best performance, the type of these parts should be half of the
// size of the largest efficient integer supported by the host processor.
// These parts are stored in either little- or big-endian order, which can
// match that of the host's endianness or not; but if the ordering matches
// that of the host, raw host data can be overlaid with a properly configured
// instance of this class and used in situ.
// To facilitate exhaustive testing of what would otherwise be more rare
// edge cases, this class template may be configured to use other part
// types &/or partial fields in the parts.
// types &/or partial fields in the parts. The radix (i.e., the number
// of possible values in a part), however, must be a power of two; this
// template class is not generalized to enable, say, decimal arithmetic.
// Member functions that correspond to Fortran intrinsic functions are
// named accordingly.
template<int BITS, int PARTBITS = 32, typename PART = std::uint32_t,
typename BIGPART = std::uint64_t, bool LITTLE_ENDIAN = true>
class Integer {
// named accordingly so that they can be referenced easily in the
// language standard.
template<int BITS,
int PARTBITS =
BITS<32 ? BITS : 32, typename PART = HostUnsignedInt<PARTBITS>,
typename BIGPART = HostUnsignedInt<PARTBITS * 2>,
bool LITTLE_ENDIAN = IsHostLittleEndian> class Integer {
public:
static constexpr int bits{BITS};
static constexpr int partBits{PARTBITS};
using Part = PART;
using BigPart = BIGPART;
static_assert(sizeof(BigPart) >= 2 * sizeof(Part));
static_assert(CHAR_BIT * sizeof(BigPart) >= 2 * partBits);
static constexpr bool littleEndian{LITTLE_ENDIAN};
private:
@ -152,9 +147,9 @@ public:
if (bits < 4) {
return 0;
}
Integer x{HUGE}, ten{std::uint64_t{10}};
Integer x{HUGE()}, ten{std::uint64_t{10}};
int digits{0};
while (x.Compare(ten) != Ordering::Less) {
while (x.CompareUnsigned(ten) != Ordering::Less) {
++digits;
x = x.DivideUnsigned(ten).quotient;
}
@ -515,8 +510,7 @@ public:
return result;
}
constexpr Integer MERGE_BITS(
const Integer &y, const Integer &mask) const {
constexpr Integer MERGE_BITS(const Integer &y, const Integer &mask) const {
return IAND(mask).IOR(y.IAND(mask.NOT()));
}
@ -585,7 +579,7 @@ public:
constexpr ValueWithOverflow SIGN(const Integer &sign) const {
bool goNegative{sign.IsNegative()};
if (goNegative == IsNegative()) {
return *this;
return {*this, false};
} else if (goNegative) {
return Negate();
} else {
@ -655,8 +649,7 @@ public:
Integer quotient, remainder;
bool divisionByZero, overflow;
};
constexpr QuotientWithRemainder DivideUnsigned(
const Integer &divisor) const {
constexpr QuotientWithRemainder DivideUnsigned(const Integer &divisor) const {
if (divisor.IsZero()) {
return {MASKR(bits), Integer{}, true, false}; // overflow to max value
}
@ -734,7 +727,6 @@ public:
// Result has the sign of the divisor argument.
// 8 mod 5 = 3; -8 mod 5 = 2; 8 mod -5 = -2; -8 mod -5 = -3
constexpr ValueWithOverflow MODULO(const Integer &divisor) const {
Integer quotient{*this};
bool negativeDivisor{divisor.IsNegative()};
bool distinctSigns{IsNegative() != negativeDivisor};
QuotientWithRemainder divided{DivideSigned(divisor)};
@ -781,5 +773,14 @@ private:
Part part_[parts];
};
extern template class Integer<8>;
extern template class Integer<16>;
extern template class Integer<32>;
extern template class Integer<64>;
extern template class Integer<128>;
template<int KIND> using IntrinsicInteger = Integer<KIND * CHAR_BIT>;
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_INTEGER_H_

View File

@ -24,9 +24,9 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
std::int64_t maxPositiveSignedValue{(std::int64_t{1} << (BITS - 1)) - 1};
std::int64_t mostNegativeSignedValue{-(std::int64_t{1} << (BITS - 1))};
char desc[64];
std::snprintf(desc, sizeof desc, "BITS=%d, PARTBITS=%d, sizeof(Part)=%d, LE=%d",
BITS, INT::partBits, static_cast<int>(sizeof(typename INT::Part)),
INT::littleEndian);
std::snprintf(desc, sizeof desc,
"BITS=%d, PARTBITS=%d, sizeof(Part)=%d, LE=%d", BITS, INT::partBits,
static_cast<int>(sizeof(typename INT::Part)), INT::littleEndian);
MATCH(BITS, INT::bits)(desc);
MATCH(maxPositiveSignedValue, INT::HUGE().ToUInt64())(desc);
@ -47,14 +47,14 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
MATCH(x ^ maxUnsignedValue, t.ToUInt64())("%s, x=0x%llx", desc, x);
auto negated{a.Negate()};
MATCH(x == std::uint64_t{1} << (BITS - 1), negated.overflow)
("%s, x=0x%llx", desc, x);
("%s, x=0x%llx", desc, x);
MATCH(-x & maxUnsignedValue, negated.value.ToUInt64())
("%s, x=0x%llx", desc, x);
("%s, x=0x%llx", desc, x);
auto abs{a.ABS()};
MATCH(x == std::uint64_t{1} << (BITS - 1), abs.overflow)
("%s, x=0x%llx", desc, x);
MATCH(x >> (BITS-1) ? -x & maxUnsignedValue : x, abs.value.ToUInt64())
("%s, x=0x%llx", desc, x);
("%s, x=0x%llx", desc, x);
MATCH(x >> (BITS - 1) ? -x & maxUnsignedValue : x, abs.value.ToUInt64())
("%s, x=0x%llx", desc, x);
int lzbc{a.LEADZ()};
COMPARE(lzbc, >=, 0)("%s, x=0x%llx", desc, x);
COMPARE(lzbc, <=, BITS)("%s, x=0x%llx", desc, x);
@ -77,7 +77,7 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
MATCH(trailcheck, a.TRAILZ())("%s, x=0x%llx", desc, x);
for (int j{0}; j < BITS; ++j) {
MATCH((x >> j) & 1, a.BTEST(j))
("%s, x=0x%llx, bit %d", desc, x, j);
("%s, x=0x%llx, bit %d", desc, x, j);
}
// TODO test DIM, MODULO, ISHFTC, DSHIFTL/R
// TODO test IBCLR, IBSET, IBITS, MAX, MIN, MERGE_BITS, RANGE, SIGN
@ -104,20 +104,21 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
for (int count{0}; count <= BITS + 1; ++count) {
t = a.SHIFTL(count);
MATCH((x << count) & maxUnsignedValue, t.ToUInt64())
("%s, x=0x%llx, count=%d", desc, x, count);
("%s, x=0x%llx, count=%d", desc, x, count);
t = a.ISHFT(count);
MATCH((x << count) & maxUnsignedValue, t.ToUInt64())
("%s, x=0x%llx, count=%d", desc, x, count);
("%s, x=0x%llx, count=%d", desc, x, count);
t = a.SHIFTR(count);
MATCH(x >> count, t.ToUInt64())
("%s, x=0x%llx, count=%d", desc, x, count);
("%s, x=0x%llx, count=%d", desc, x, count);
t = a.ISHFT(-count);
MATCH(x >> count, t.ToUInt64())("%s, x=0x%llx, count=%d", desc, x, count);
t = a.SHIFTA(count);
std::uint64_t fill{-(x >> (BITS-1))};
std::uint64_t sra{count >= BITS ? fill : (x >> count) | (fill << (BITS-count))};
std::uint64_t fill{-(x >> (BITS - 1))};
std::uint64_t sra{
count >= BITS ? fill : (x >> count) | (fill << (BITS - count))};
MATCH(sra, t.ToInt64())
("%s, x=0x%llx, count=%d", desc, x, count);
("%s, x=0x%llx, count=%d", desc, x, count);
}
for (std::uint64_t y{0}; y <= maxUnsignedValue; ++y) {
@ -146,8 +147,8 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
ord = Ordering::Equal;
}
TEST(a.CompareSigned(b) == ord)
("%s, x=0x%llx %lld %d, y=0x%llx %lld %d", desc, x, sx,
a.IsNegative(), y, sy, b.IsNegative());
("%s, x=0x%llx %lld %d, y=0x%llx %lld %d", desc, x, sx, a.IsNegative(), y,
sy, b.IsNegative());
t = a.IAND(b);
MATCH(x & y, t.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
@ -156,41 +157,45 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
t = a.IEOR(b);
MATCH(x ^ y, t.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
auto sum{a.AddUnsigned(b)};
COMPARE(x + y, ==, sum.value.ToUInt64() + (std::uint64_t{sum.carry} << BITS))
("%s, x=0x%llx, y=0x%llx, carry=%d", desc, x, y, sum.carry);
COMPARE(
x + y, ==, sum.value.ToUInt64() + (std::uint64_t{sum.carry} << BITS))
("%s, x=0x%llx, y=0x%llx, carry=%d", desc, x, y, sum.carry);
auto ssum{a.AddSigned(b)};
MATCH((sx + sy) & maxUnsignedValue, ssum.value.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(sx + sy < mostNegativeSignedValue ||
sx + sy > maxPositiveSignedValue, ssum.overflow)
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(
sx + sy < mostNegativeSignedValue || sx + sy > maxPositiveSignedValue,
ssum.overflow)
("%s, x=0x%llx, y=0x%llx", desc, x, y);
auto diff{a.SubtractSigned(b)};
MATCH((sx - sy) & maxUnsignedValue, diff.value.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(sx - sy < mostNegativeSignedValue ||
sx - sy > maxPositiveSignedValue, diff.overflow)
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(
sx - sy < mostNegativeSignedValue || sx - sy > maxPositiveSignedValue,
diff.overflow)
("%s, x=0x%llx, y=0x%llx", desc, x, y);
auto product{a.MultiplyUnsigned(b)};
MATCH(x * y, (product.upper.ToUInt64() << BITS) ^ product.lower.ToUInt64())
("%s, x=0x%llx, y=0x%llx, lower=0x%llx, upper=0x%llx", desc, x, y,
MATCH(
x * y, (product.upper.ToUInt64() << BITS) ^ product.lower.ToUInt64())
("%s, x=0x%llx, y=0x%llx, lower=0x%llx, upper=0x%llx", desc, x, y,
product.lower.ToUInt64(), product.upper.ToUInt64());
product = a.MultiplySigned(b);
MATCH((sx * sy) & maxUnsignedValue, product.lower.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(((sx * sy) >> BITS) & maxUnsignedValue, product.upper.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
auto quot{a.DivideUnsigned(b)};
MATCH(y == 0, quot.divisionByZero)("%s, x=0x%llx, y=0x%llx", desc, x, y);
if (y == 0) {
MATCH(maxUnsignedValue, quot.quotient.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(0, quot.remainder.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else {
MATCH(x / y, quot.quotient.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(x % y, quot.remainder.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
quot = a.DivideSigned(b);
bool badCase{sx == mostNegativeSignedValue &&
@ -206,16 +211,18 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
MATCH(0, quot.remainder.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else if (badCase) {
MATCH(x, quot.quotient.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(0, quot.remainder.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(x, quot.quotient.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(0, quot.remainder.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else {
MATCH(sx / sy, quot.quotient.ToInt64())
("%s, x=0x%llx %lld, y=0x%llx %lld; unsigned 0x%llx", desc, x, sx, y,
("%s, x=0x%llx %lld, y=0x%llx %lld; unsigned 0x%llx", desc, x, sx, y,
sy, quot.quotient.ToUInt64());
MATCH(sx - sy * (sx / sy), quot.remainder.ToInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
}
}

View File

@ -48,8 +48,8 @@ FailureDetailPrinter Test(
}
}
FailureDetailPrinter Match(const char *file, int line,
unsigned long long want, const char *gots, unsigned long long got) {
FailureDetailPrinter Match(const char *file, int line, unsigned long long want,
const char *gots, unsigned long long got) {
if (want == got) {
++passes;
return BitBucket;
@ -90,8 +90,8 @@ FailureDetailPrinter Compare(const char *file, int line, const char *xs,
return BitBucket;
} else {
++failures;
fprintf(stderr, "%s:%d: FAIL %s[0x%llx] %s %s[0x%llx]\n", file, line, xs,
x, rel, ys, y);
fprintf(stderr, "%s:%d: FAIL %s[0x%llx] %s %s[0x%llx]\n", file, line, xs, x,
rel, ys, y);
return PrintFailureDetails;
}
}

View File

@ -27,8 +27,7 @@ int Complete();
// will also print z after the usual failure message if x != y.
#define TEST(predicate) \
testing::Test(__FILE__, __LINE__, #predicate, (predicate))
#define MATCH(want, got) \
testing::Match(__FILE__, __LINE__, (want), #got, (got))
#define MATCH(want, got) testing::Match(__FILE__, __LINE__, (want), #got, (got))
#define COMPARE(x, rel, y) \
testing::Compare(__FILE__, __LINE__, #x, #rel, #y, (x), (y))
@ -36,8 +35,8 @@ int Complete();
using FailureDetailPrinter = void (*)(const char *, ...);
FailureDetailPrinter Test(
const char *file, int line, const char *predicate, bool pass);
FailureDetailPrinter Match(const char *file, int line,
unsigned long long want, const char *gots, unsigned long long got);
FailureDetailPrinter Match(const char *file, int line, unsigned long long want,
const char *gots, unsigned long long got);
FailureDetailPrinter Compare(const char *file, int line, const char *xs,
const char *rel, const char *ys, unsigned long long x,
unsigned long long y);