[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH for-4.0 2/7] Remove support for compilers that can n
From: |
Thomas Huth |
Subject: |
[Qemu-devel] [PATCH for-4.0 2/7] Remove support for compilers that can not do 128-bit arithmetics |
Date: |
Mon, 3 Dec 2018 15:05:32 +0100 |
Now that we require at least GCC v4.8 and Clang v3.4, we can be sure
that we always have a compiler that has support for 128-bit arithmetics.
Thus we can remove the !CONFIG_INT128 code now.
Signed-off-by: Thomas Huth <address@hidden>
---
Note: We could even remove int128.h and the helper functions now ...
but that's also topic for a separate patch, I think.
configure | 37 ++---------
include/qemu/host-utils.h | 30 ---------
include/qemu/int128.h | 160 ----------------------------------------------
target/ppc/cpu.h | 2 -
target/ppc/int_helper.c | 159 ---------------------------------------------
target/s390x/int_helper.c | 7 --
util/host-utils.c | 135 --------------------------------------
7 files changed, 4 insertions(+), 526 deletions(-)
diff --git a/configure b/configure
index f1e305e..14e9722 100755
--- a/configure
+++ b/configure
@@ -5137,35 +5137,11 @@ EOF
fi
fi
-########################################
-# check if __[u]int128_t is usable.
-
-int128=no
-cat > $TMPC << EOF
-#if defined(__clang_major__) && defined(__clang_minor__)
-# if ((__clang_major__ < 3) || (__clang_major__ == 3) && (__clang_minor__ < 2))
-# error __int128_t does not work in CLANG before 3.2
-# endif
-#endif
-__int128_t a;
-__uint128_t b;
-int main (void) {
- a = a + b;
- b = a * b;
- a = a * a;
- return 0;
-}
-EOF
-if compile_prog "" "" ; then
- int128=yes
-fi
-
#########################################
# See if 128-bit atomic operations are supported.
atomic128=no
-if test "$int128" = "yes"; then
- cat > $TMPC << EOF
+cat > $TMPC << EOF
int main(void)
{
unsigned __int128 x = 0, y = 0;
@@ -5175,13 +5151,12 @@ int main(void)
return 0;
}
EOF
- if compile_prog "" "" ; then
- atomic128=yes
- fi
+if compile_prog "" "" ; then
+ atomic128=yes
fi
cmpxchg128=no
-if test "$int128" = yes -a "$atomic128" = no; then
+if test "$atomic128" = no; then
cat > $TMPC << EOF
int main(void)
{
@@ -6708,10 +6683,6 @@ if test "$cpuid_h" = "yes" ; then
echo "CONFIG_CPUID_H=y" >> $config_host_mak
fi
-if test "$int128" = "yes" ; then
- echo "CONFIG_INT128=y" >> $config_host_mak
-fi
-
if test "$atomic128" = "yes" ; then
echo "CONFIG_ATOMIC128=y" >> $config_host_mak
fi
diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h
index 38da849..25ba3e8 100644
--- a/include/qemu/host-utils.h
+++ b/include/qemu/host-utils.h
@@ -28,7 +28,6 @@
#include "qemu/bswap.h"
-#ifdef CONFIG_INT128
static inline void mulu64(uint64_t *plow, uint64_t *phigh,
uint64_t a, uint64_t b)
{
@@ -76,35 +75,6 @@ static inline int divs128(int64_t *plow, int64_t *phigh,
int64_t divisor)
return result != *plow;
}
}
-#else
-void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b);
-void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b);
-int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor);
-int divs128(int64_t *plow, int64_t *phigh, int64_t divisor);
-
-static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
-{
- union {
- uint64_t ll;
- struct {
-#ifdef HOST_WORDS_BIGENDIAN
- uint32_t high, low;
-#else
- uint32_t low, high;
-#endif
- } l;
- } u, res;
- uint64_t rl, rh;
-
- u.ll = a;
- rl = (uint64_t)u.l.low * (uint64_t)b;
- rh = (uint64_t)u.l.high * (uint64_t)b;
- rh += (rl >> 32);
- res.l.high = rh / c;
- res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
- return res.ll;
-}
-#endif
/**
* clz32 - count leading zeros in a 32-bit value.
diff --git a/include/qemu/int128.h b/include/qemu/int128.h
index 5c9890d..8eac3f8 100644
--- a/include/qemu/int128.h
+++ b/include/qemu/int128.h
@@ -1,7 +1,6 @@
#ifndef INT128_H
#define INT128_H
-#ifdef CONFIG_INT128
#include "qemu/bswap.h"
typedef __int128_t Int128;
@@ -143,163 +142,4 @@ static inline Int128 bswap128(Int128 a)
return int128_make128(bswap64(int128_gethi(a)), bswap64(int128_getlo(a)));
}
-#else /* !CONFIG_INT128 */
-
-typedef struct Int128 Int128;
-
-struct Int128 {
- uint64_t lo;
- int64_t hi;
-};
-
-static inline Int128 int128_make64(uint64_t a)
-{
- return (Int128) { a, 0 };
-}
-
-static inline Int128 int128_make128(uint64_t lo, uint64_t hi)
-{
- return (Int128) { lo, hi };
-}
-
-static inline uint64_t int128_get64(Int128 a)
-{
- assert(!a.hi);
- return a.lo;
-}
-
-static inline uint64_t int128_getlo(Int128 a)
-{
- return a.lo;
-}
-
-static inline int64_t int128_gethi(Int128 a)
-{
- return a.hi;
-}
-
-static inline Int128 int128_zero(void)
-{
- return int128_make64(0);
-}
-
-static inline Int128 int128_one(void)
-{
- return int128_make64(1);
-}
-
-static inline Int128 int128_2_64(void)
-{
- return (Int128) { 0, 1 };
-}
-
-static inline Int128 int128_exts64(int64_t a)
-{
- return (Int128) { .lo = a, .hi = (a < 0) ? -1 : 0 };
-}
-
-static inline Int128 int128_and(Int128 a, Int128 b)
-{
- return (Int128) { a.lo & b.lo, a.hi & b.hi };
-}
-
-static inline Int128 int128_rshift(Int128 a, int n)
-{
- int64_t h;
- if (!n) {
- return a;
- }
- h = a.hi >> (n & 63);
- if (n >= 64) {
- return int128_make128(h, h >> 63);
- } else {
- return int128_make128((a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h);
- }
-}
-
-static inline Int128 int128_add(Int128 a, Int128 b)
-{
- uint64_t lo = a.lo + b.lo;
-
- /* a.lo <= a.lo + b.lo < a.lo + k (k is the base, 2^64). Hence,
- * a.lo + b.lo >= k implies 0 <= lo = a.lo + b.lo - k < a.lo.
- * Similarly, a.lo + b.lo < k implies a.lo <= lo = a.lo + b.lo < k.
- *
- * So the carry is lo < a.lo.
- */
- return int128_make128(lo, (uint64_t)a.hi + b.hi + (lo < a.lo));
-}
-
-static inline Int128 int128_neg(Int128 a)
-{
- uint64_t lo = -a.lo;
- return int128_make128(lo, ~(uint64_t)a.hi + !lo);
-}
-
-static inline Int128 int128_sub(Int128 a, Int128 b)
-{
- return int128_make128(a.lo - b.lo, (uint64_t)a.hi - b.hi - (a.lo < b.lo));
-}
-
-static inline bool int128_nonneg(Int128 a)
-{
- return a.hi >= 0;
-}
-
-static inline bool int128_eq(Int128 a, Int128 b)
-{
- return a.lo == b.lo && a.hi == b.hi;
-}
-
-static inline bool int128_ne(Int128 a, Int128 b)
-{
- return !int128_eq(a, b);
-}
-
-static inline bool int128_ge(Int128 a, Int128 b)
-{
- return a.hi > b.hi || (a.hi == b.hi && a.lo >= b.lo);
-}
-
-static inline bool int128_lt(Int128 a, Int128 b)
-{
- return !int128_ge(a, b);
-}
-
-static inline bool int128_le(Int128 a, Int128 b)
-{
- return int128_ge(b, a);
-}
-
-static inline bool int128_gt(Int128 a, Int128 b)
-{
- return !int128_le(a, b);
-}
-
-static inline bool int128_nz(Int128 a)
-{
- return a.lo || a.hi;
-}
-
-static inline Int128 int128_min(Int128 a, Int128 b)
-{
- return int128_le(a, b) ? a : b;
-}
-
-static inline Int128 int128_max(Int128 a, Int128 b)
-{
- return int128_ge(a, b) ? a : b;
-}
-
-static inline void int128_addto(Int128 *a, Int128 b)
-{
- *a = int128_add(*a, b);
-}
-
-static inline void int128_subfrom(Int128 *a, Int128 b)
-{
- *a = int128_sub(*a, b);
-}
-
-#endif /* CONFIG_INT128 */
#endif /* INT128_H */
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index ab68abe..858f121 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -265,9 +265,7 @@ union ppc_avr_t {
int32_t s32[4];
uint64_t u64[2];
int64_t s64[2];
-#ifdef CONFIG_INT128
__uint128_t u128;
-#endif
Int128 s128;
};
diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c
index fcac90a..00c65bd 100644
--- a/target/ppc/int_helper.c
+++ b/target/ppc/int_helper.c
@@ -1550,8 +1550,6 @@ PMSUM(vpmsumw, u32, u64, uint64_t)
void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
-
-#ifdef CONFIG_INT128
int i, j;
__uint128_t prod[2];
@@ -1565,32 +1563,6 @@ void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a,
ppc_avr_t *b)
}
r->u128 = prod[0] ^ prod[1];
-
-#else
- int i, j;
- ppc_avr_t prod[2];
-
- VECTOR_FOR_INORDER_I(i, u64) {
- prod[i].u64[LO_IDX] = prod[i].u64[HI_IDX] = 0;
- for (j = 0; j < 64; j++) {
- if (a->u64[i] & (1ull<<j)) {
- ppc_avr_t bshift;
- if (j == 0) {
- bshift.u64[HI_IDX] = 0;
- bshift.u64[LO_IDX] = b->u64[i];
- } else {
- bshift.u64[HI_IDX] = b->u64[i] >> (64-j);
- bshift.u64[LO_IDX] = b->u64[i] << j;
- }
- prod[i].u64[LO_IDX] ^= bshift.u64[LO_IDX];
- prod[i].u64[HI_IDX] ^= bshift.u64[HI_IDX];
- }
- }
- }
-
- r->u64[LO_IDX] = prod[0].u64[LO_IDX] ^ prod[1].u64[LO_IDX];
- r->u64[HI_IDX] = prod[0].u64[HI_IDX] ^ prod[1].u64[HI_IDX];
-#endif
}
@@ -2354,183 +2326,52 @@ VGENERIC_DO(popcntd, u64)
#define QW_ONE { .u64 = { 1, 0 } }
#endif
-#ifndef CONFIG_INT128
-
-static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a)
-{
- t->u64[0] = ~a.u64[0];
- t->u64[1] = ~a.u64[1];
-}
-
-static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b)
-{
- if (a.u64[HI_IDX] < b.u64[HI_IDX]) {
- return -1;
- } else if (a.u64[HI_IDX] > b.u64[HI_IDX]) {
- return 1;
- } else if (a.u64[LO_IDX] < b.u64[LO_IDX]) {
- return -1;
- } else if (a.u64[LO_IDX] > b.u64[LO_IDX]) {
- return 1;
- } else {
- return 0;
- }
-}
-
-static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b)
-{
- t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX];
- t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] +
- (~a.u64[LO_IDX] < b.u64[LO_IDX]);
-}
-
-static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b)
-{
- ppc_avr_t not_a;
- t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX];
- t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] +
- (~a.u64[LO_IDX] < b.u64[LO_IDX]);
- avr_qw_not(¬_a, a);
- return avr_qw_cmpu(not_a, b) < 0;
-}
-
-#endif
-
void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
-#ifdef CONFIG_INT128
r->u128 = a->u128 + b->u128;
-#else
- avr_qw_add(r, *a, *b);
-#endif
}
void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
{
-#ifdef CONFIG_INT128
r->u128 = a->u128 + b->u128 + (c->u128 & 1);
-#else
-
- if (c->u64[LO_IDX] & 1) {
- ppc_avr_t tmp;
-
- tmp.u64[HI_IDX] = 0;
- tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1;
- avr_qw_add(&tmp, *a, tmp);
- avr_qw_add(r, tmp, *b);
- } else {
- avr_qw_add(r, *a, *b);
- }
-#endif
}
void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
-#ifdef CONFIG_INT128
r->u128 = (~a->u128 < b->u128);
-#else
- ppc_avr_t not_a;
-
- avr_qw_not(¬_a, *a);
-
- r->u64[HI_IDX] = 0;
- r->u64[LO_IDX] = (avr_qw_cmpu(not_a, *b) < 0);
-#endif
}
void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
{
-#ifdef CONFIG_INT128
int carry_out = (~a->u128 < b->u128);
if (!carry_out && (c->u128 & 1)) {
carry_out = ((a->u128 + b->u128 + 1) == 0) &&
((a->u128 != 0) || (b->u128 != 0));
}
r->u128 = carry_out;
-#else
-
- int carry_in = c->u64[LO_IDX] & 1;
- int carry_out = 0;
- ppc_avr_t tmp;
-
- carry_out = avr_qw_addc(&tmp, *a, *b);
-
- if (!carry_out && carry_in) {
- ppc_avr_t one = QW_ONE;
- carry_out = avr_qw_addc(&tmp, tmp, one);
- }
- r->u64[HI_IDX] = 0;
- r->u64[LO_IDX] = carry_out;
-#endif
}
void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
-#ifdef CONFIG_INT128
r->u128 = a->u128 - b->u128;
-#else
- ppc_avr_t tmp;
- ppc_avr_t one = QW_ONE;
-
- avr_qw_not(&tmp, *b);
- avr_qw_add(&tmp, *a, tmp);
- avr_qw_add(r, tmp, one);
-#endif
}
void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
{
-#ifdef CONFIG_INT128
r->u128 = a->u128 + ~b->u128 + (c->u128 & 1);
-#else
- ppc_avr_t tmp, sum;
-
- avr_qw_not(&tmp, *b);
- avr_qw_add(&sum, *a, tmp);
-
- tmp.u64[HI_IDX] = 0;
- tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1;
- avr_qw_add(r, sum, tmp);
-#endif
}
void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
-#ifdef CONFIG_INT128
r->u128 = (~a->u128 < ~b->u128) ||
(a->u128 + ~b->u128 == (__uint128_t)-1);
-#else
- int carry = (avr_qw_cmpu(*a, *b) > 0);
- if (!carry) {
- ppc_avr_t tmp;
- avr_qw_not(&tmp, *b);
- avr_qw_add(&tmp, *a, tmp);
- carry = ((tmp.s64[HI_IDX] == -1ull) && (tmp.s64[LO_IDX] == -1ull));
- }
- r->u64[HI_IDX] = 0;
- r->u64[LO_IDX] = carry;
-#endif
}
void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
{
-#ifdef CONFIG_INT128
r->u128 =
(~a->u128 < ~b->u128) ||
((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1));
-#else
- int carry_in = c->u64[LO_IDX] & 1;
- int carry_out = (avr_qw_cmpu(*a, *b) > 0);
- if (!carry_out && carry_in) {
- ppc_avr_t tmp;
- avr_qw_not(&tmp, *b);
- avr_qw_add(&tmp, *a, tmp);
- carry_out = ((tmp.u64[HI_IDX] == -1ull) && (tmp.u64[LO_IDX] == -1ull));
- }
-
- r->u64[HI_IDX] = 0;
- r->u64[LO_IDX] = carry_out;
-#endif
}
#define BCD_PLUS_PREF_1 0xC
diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c
index abf77a9..c8cfb93 100644
--- a/target/s390x/int_helper.c
+++ b/target/s390x/int_helper.c
@@ -100,7 +100,6 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah,
uint64_t al,
ret = al / b;
} else {
/* ??? Move i386 idivq helper to host-utils. */
-#ifdef CONFIG_INT128
__uint128_t a = ((__uint128_t)ah << 64) | al;
__uint128_t q = a / b;
env->retxl = a % b;
@@ -108,12 +107,6 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah,
uint64_t al,
if (ret != q) {
s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
}
-#else
- S390CPU *cpu = s390_env_get_cpu(env);
- /* 32-bit hosts would need special wrapper functionality - just abort
if
- we encounter such a case; it's very unlikely anyways. */
- cpu_abort(CPU(cpu), "128 -> 64/64 division not implemented\n");
-#endif
}
return ret;
}
diff --git a/util/host-utils.c b/util/host-utils.c
index 7b93220..18fa1d0 100644
--- a/util/host-utils.c
+++ b/util/host-utils.c
@@ -26,141 +26,6 @@
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
-#ifndef CONFIG_INT128
-/* Long integer helpers */
-static inline void mul64(uint64_t *plow, uint64_t *phigh,
- uint64_t a, uint64_t b)
-{
- typedef union {
- uint64_t ll;
- struct {
-#ifdef HOST_WORDS_BIGENDIAN
- uint32_t high, low;
-#else
- uint32_t low, high;
-#endif
- } l;
- } LL;
- LL rl, rm, rn, rh, a0, b0;
- uint64_t c;
-
- a0.ll = a;
- b0.ll = b;
-
- rl.ll = (uint64_t)a0.l.low * b0.l.low;
- rm.ll = (uint64_t)a0.l.low * b0.l.high;
- rn.ll = (uint64_t)a0.l.high * b0.l.low;
- rh.ll = (uint64_t)a0.l.high * b0.l.high;
-
- c = (uint64_t)rl.l.high + rm.l.low + rn.l.low;
- rl.l.high = c;
- c >>= 32;
- c = c + rm.l.high + rn.l.high + rh.l.low;
- rh.l.low = c;
- rh.l.high += (uint32_t)(c >> 32);
-
- *plow = rl.ll;
- *phigh = rh.ll;
-}
-
-/* Unsigned 64x64 -> 128 multiplication */
-void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
-{
- mul64(plow, phigh, a, b);
-}
-
-/* Signed 64x64 -> 128 multiplication */
-void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
-{
- uint64_t rh;
-
- mul64(plow, &rh, a, b);
-
- /* Adjust for signs. */
- if (b < 0) {
- rh -= a;
- }
- if (a < 0) {
- rh -= b;
- }
- *phigh = rh;
-}
-
-/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */
-/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */
-/* remainder via phigh. */
-int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
-{
- uint64_t dhi = *phigh;
- uint64_t dlo = *plow;
- unsigned i;
- uint64_t carry = 0;
-
- if (divisor == 0) {
- return 1;
- } else if (dhi == 0) {
- *plow = dlo / divisor;
- *phigh = dlo % divisor;
- return 0;
- } else if (dhi > divisor) {
- return 1;
- } else {
-
- for (i = 0; i < 64; i++) {
- carry = dhi >> 63;
- dhi = (dhi << 1) | (dlo >> 63);
- if (carry || (dhi >= divisor)) {
- dhi -= divisor;
- carry = 1;
- } else {
- carry = 0;
- }
- dlo = (dlo << 1) | carry;
- }
-
- *plow = dlo;
- *phigh = dhi;
- return 0;
- }
-}
-
-int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
-{
- int sgn_dvdnd = *phigh < 0;
- int sgn_divsr = divisor < 0;
- int overflow = 0;
-
- if (sgn_dvdnd) {
- *plow = ~(*plow);
- *phigh = ~(*phigh);
- if (*plow == (int64_t)-1) {
- *plow = 0;
- (*phigh)++;
- } else {
- (*plow)++;
- }
- }
-
- if (sgn_divsr) {
- divisor = 0 - divisor;
- }
-
- overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor);
-
- if (sgn_dvdnd ^ sgn_divsr) {
- *plow = 0 - *plow;
- }
-
- if (!overflow) {
- if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) {
- overflow = 1;
- }
- }
-
- return overflow;
-}
-#endif
-
/**
* urshift - 128-bit Unsigned Right Shift.
* @plow: in/out - lower 64-bit integer.
--
1.8.3.1
- Re: [Qemu-devel] [PATCH for-4.0 4/7] Remove QEMU_ARTIFICIAL macro, (continued)
[Qemu-devel] [PATCH for-4.0 6/7] audio/alsaaudio: Remove compiler check around pragma, Thomas Huth, 2018/12/03
[Qemu-devel] [PATCH for-4.0 2/7] Remove support for compilers that can not do 128-bit arithmetics,
Thomas Huth <=
[Qemu-devel] [PATCH for-4.0 5/7] tcg/tcg.h: Remove GCC check for tcg_debug_assert() macro, Thomas Huth, 2018/12/03
[Qemu-devel] [PATCH for-4.0 3/7] configure: Remove old -fno-gcse workaround for GCC 4.6.x and 4.7.[012], Thomas Huth, 2018/12/03
Re: [Qemu-devel] [PATCH for-4.0 0/7] Assume GCC v4.8 and Clang v3.4 as minimum compiler versions, no-reply, 2018/12/03
Re: [Qemu-devel] [PATCH for-4.0 0/7] Assume GCC v4.8 and Clang v3.4 as minimum compiler versions, no-reply, 2018/12/03