Fix for GitHub issue #199: handle signed division overflow.

When performing a signed division or modulus operation using native
arithmetic, trap the special case that the numerator is the minimum
integer value and the denominator is -1, as this gives an undefined
result in C++.
This commit is contained in:
Martin Whitaker 2018-06-12 21:59:58 +01:00
parent 6e49ab10ec
commit 7ad5b59a6f
2 changed files with 17 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2018 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -22,6 +22,7 @@
# include "verinum.h" # include "verinum.h"
# include <iostream> # include <iostream>
# include <cassert> # include <cassert>
# include <climits>
# include <cmath> // Needed to get pow for as_double(). # include <cmath> // Needed to get pow for as_double().
# include <cstdio> // Needed to get snprintf for as_string(). # include <cstdio> // Needed to get snprintf for as_string().
# include <algorithm> # include <algorithm>
@ -1444,7 +1445,8 @@ verinum operator / (const verinum&left, const verinum&right)
if (use_len <= (8*sizeof(long) - 1)) { if (use_len <= (8*sizeof(long) - 1)) {
long l = left.as_long(); long l = left.as_long();
long r = right.as_long(); long r = right.as_long();
long v = l / r; bool overflow = (l == LONG_MIN) && (r == -1);
long v = overflow ? LONG_MIN : l / r;
for (unsigned idx = 0 ; idx < use_len ; idx += 1) { for (unsigned idx = 0 ; idx < use_len ; idx += 1) {
result.set(idx, (v & 1)? verinum::V1 : verinum::V0); result.set(idx, (v & 1)? verinum::V1 : verinum::V0);
v >>= 1; v >>= 1;
@ -1518,7 +1520,8 @@ verinum operator % (const verinum&left, const verinum&right)
/* Use native signed modulus to do the work. */ /* Use native signed modulus to do the work. */
long l = left.as_long(); long l = left.as_long();
long r = right.as_long(); long r = right.as_long();
long v = l % r; bool overflow = (l == LONG_MIN) && (r == -1);
long v = overflow ? 0 : l % r;
for (unsigned idx = 0 ; idx < use_len ; idx += 1) { for (unsigned idx = 0 ; idx < use_len ; idx += 1) {
result.set(idx, (v & 1)? verinum::V1 : verinum::V0); result.set(idx, (v & 1)? verinum::V1 : verinum::V0);
v >>= 1; v >>= 1;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001-2017 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2018 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -2707,6 +2707,10 @@ bool of_DIV_S(vthread_t thr, vvp_code_t)
if (bp[0] == 0) { if (bp[0] == 0) {
vvp_vector4_t tmp(wid, BIT4_X); vvp_vector4_t tmp(wid, BIT4_X);
vala = tmp; vala = tmp;
} else if (((long)ap[0] == LONG_MIN) && ((long)bp[0] == -1)) {
vvp_vector4_t tmp(wid, BIT4_0);
tmp.set_bit(wid-1, BIT4_1);
vala = tmp;
} else { } else {
long tmpa = (long) ap[0]; long tmpa = (long) ap[0];
long tmpb = (long) bp[0]; long tmpb = (long) bp[0];
@ -3996,6 +4000,9 @@ bool of_MOD_S(vthread_t thr, vvp_code_t)
if (rv == 0) if (rv == 0)
goto x_out; goto x_out;
if ((lv == LONG_LONG_MIN) && (rv == -1))
goto zero_out;
/* Sign extend the signed operands when needed. */ /* Sign extend the signed operands when needed. */
if (wid < 8*sizeof(long long)) { if (wid < 8*sizeof(long long)) {
if (lv & (1LL << (wid-1))) if (lv & (1LL << (wid-1)))
@ -4027,6 +4034,9 @@ bool of_MOD_S(vthread_t thr, vvp_code_t)
x_out: x_out:
vala = vvp_vector4_t(wid, BIT4_X); vala = vvp_vector4_t(wid, BIT4_X);
return true; return true;
zero_out:
vala = vvp_vector4_t(wid, BIT4_0);
return true;
} }
/* /*