aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/int.c
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2017-12-12 16:32:31 -0800
committerAndres Freund <andres@anarazel.de>2017-12-12 16:55:37 -0800
commit101c7ee3ee847bac970c74b73b4f2858484383e5 (patch)
tree0e2c14000aa86975fbb36fb36470f54251b83b54 /src/backend/utils/adt/int.c
parent4d6ad31257adaf8a51e1c4377d96afa656d9165f (diff)
downloadpostgresql-101c7ee3ee847bac970c74b73b4f2858484383e5.tar.gz
postgresql-101c7ee3ee847bac970c74b73b4f2858484383e5.zip
Use new overflow aware integer operations.
A previous commit added inline functions that provide fast(er) and correct overflow checks for signed integer math. Use them in a significant portion of backend code. There's more to touch in both backend and frontend code, but these were the easily identifiable cases. The old overflow checks are noticeable in integer heavy workloads. A secondary benefit is that getting rid of overflow checks that rely on signed integer overflow wrapping around, will allow us to get rid of -fwrapv in the future. Which in turn slows down other code. Author: Andres Freund Discussion: https://postgr.es/m/20171024103954.ztmatprlglz3rwke@alap3.anarazel.de
Diffstat (limited to 'src/backend/utils/adt/int.c')
-rw-r--r--src/backend/utils/adt/int.c200
1 files changed, 40 insertions, 160 deletions
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 4cd8960b3fc..36ba86ca734 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -32,14 +32,12 @@
#include <limits.h>
#include "catalog/pg_type.h"
+#include "common/int.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
-
-#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
-
#define Int2VectorSize(n) (offsetof(int2vector, values) + (n) * sizeof(int16))
typedef struct
@@ -328,7 +326,7 @@ i4toi2(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
- if (arg1 < SHRT_MIN || arg1 > SHRT_MAX)
+ if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
@@ -598,15 +596,12 @@ Datum
int4um(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
- int32 result;
- result = -arg;
- /* overflow check (needed for INT_MIN) */
- if (arg != 0 && SAMESIGN(result, arg))
+ if (unlikely(arg == PG_INT32_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
- PG_RETURN_INT32(result);
+ PG_RETURN_INT32(-arg);
}
Datum
@@ -624,14 +619,7 @@ int4pl(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 + arg2;
-
- /*
- * Overflow check. If the inputs are of different signs then their sum
- * cannot overflow. If the inputs are of the same sign, their sum had
- * better be that sign too.
- */
- if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_add_s32_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -645,14 +633,7 @@ int4mi(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 - arg2;
-
- /*
- * Overflow check. If the inputs are of the same sign then their
- * difference cannot overflow. If they are of different signs then the
- * result should be of the same sign as the first input.
- */
- if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_sub_s32_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -666,24 +647,7 @@ int4mul(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 * arg2;
-
- /*
- * Overflow check. We basically check to see if result / arg2 gives arg1
- * again. There are two cases where this fails: arg2 = 0 (which cannot
- * overflow) and arg1 = INT_MIN, arg2 = -1 (where the division itself will
- * overflow and thus incorrectly match).
- *
- * Since the division is likely much more expensive than the actual
- * multiplication, we'd like to skip it where possible. The best bang for
- * the buck seems to be to check whether both inputs are in the int16
- * range; if so, no overflow is possible.
- */
- if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX &&
- arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
- arg2 != 0 &&
- ((arg2 == -1 && arg1 < 0 && result < 0) ||
- result / arg2 != arg1))
+ if (unlikely(pg_mul_s32_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -714,12 +678,11 @@ int4div(PG_FUNCTION_ARGS)
*/
if (arg2 == -1)
{
- result = -arg1;
- /* overflow check (needed for INT_MIN) */
- if (arg1 != 0 && SAMESIGN(result, arg1))
+ if (unlikely(arg1 == PG_INT32_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ result = -arg1;
PG_RETURN_INT32(result);
}
@@ -736,9 +699,7 @@ int4inc(PG_FUNCTION_ARGS)
int32 arg = PG_GETARG_INT32(0);
int32 result;
- result = arg + 1;
- /* Overflow check */
- if (arg > 0 && result < 0)
+ if (unlikely(pg_add_s32_overflow(arg, 1, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -750,15 +711,12 @@ Datum
int2um(PG_FUNCTION_ARGS)
{
int16 arg = PG_GETARG_INT16(0);
- int16 result;
- result = -arg;
- /* overflow check (needed for SHRT_MIN) */
- if (arg != 0 && SAMESIGN(result, arg))
+ if (unlikely(arg == PG_INT16_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
- PG_RETURN_INT16(result);
+ PG_RETURN_INT16(-arg);
}
Datum
@@ -776,14 +734,7 @@ int2pl(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int16 result;
- result = arg1 + arg2;
-
- /*
- * Overflow check. If the inputs are of different signs then their sum
- * cannot overflow. If the inputs are of the same sign, their sum had
- * better be that sign too.
- */
- if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_add_s16_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
@@ -797,14 +748,7 @@ int2mi(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int16 result;
- result = arg1 - arg2;
-
- /*
- * Overflow check. If the inputs are of the same sign then their
- * difference cannot overflow. If they are of different signs then the
- * result should be of the same sign as the first input.
- */
- if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_sub_s16_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
@@ -816,20 +760,14 @@ int2mul(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
- int32 result32;
-
- /*
- * The most practical way to detect overflow is to do the arithmetic in
- * int32 (so that the result can't overflow) and then do a range check.
- */
- result32 = (int32) arg1 * (int32) arg2;
+ int16 result;
- if (result32 < SHRT_MIN || result32 > SHRT_MAX)
+ if (unlikely(pg_mul_s16_overflow(arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
- PG_RETURN_INT16((int16) result32);
+ PG_RETURN_INT16(result);
}
Datum
@@ -856,12 +794,11 @@ int2div(PG_FUNCTION_ARGS)
*/
if (arg2 == -1)
{
- result = -arg1;
- /* overflow check (needed for SHRT_MIN) */
- if (arg1 != 0 && SAMESIGN(result, arg1))
+ if (unlikely(arg1 == INT16_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ result = -arg1;
PG_RETURN_INT16(result);
}
@@ -879,14 +816,7 @@ int24pl(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 + arg2;
-
- /*
- * Overflow check. If the inputs are of different signs then their sum
- * cannot overflow. If the inputs are of the same sign, their sum had
- * better be that sign too.
- */
- if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_add_s32_overflow((int32) arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -900,14 +830,7 @@ int24mi(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 - arg2;
-
- /*
- * Overflow check. If the inputs are of the same sign then their
- * difference cannot overflow. If they are of different signs then the
- * result should be of the same sign as the first input.
- */
- if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_sub_s32_overflow((int32) arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -921,20 +844,7 @@ int24mul(PG_FUNCTION_ARGS)
int32 arg2 = PG_GETARG_INT32(1);
int32 result;
- result = arg1 * arg2;
-
- /*
- * Overflow check. We basically check to see if result / arg2 gives arg1
- * again. There is one case where this fails: arg2 = 0 (which cannot
- * overflow).
- *
- * Since the division is likely much more expensive than the actual
- * multiplication, we'd like to skip it where possible. The best bang for
- * the buck seems to be to check whether both inputs are in the int16
- * range; if so, no overflow is possible.
- */
- if (!(arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
- result / arg2 != arg1)
+ if (unlikely(pg_mul_s32_overflow((int32) arg1, arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -947,7 +857,7 @@ int24div(PG_FUNCTION_ARGS)
int16 arg1 = PG_GETARG_INT16(0);
int32 arg2 = PG_GETARG_INT32(1);
- if (arg2 == 0)
+ if (unlikely(arg2 == 0))
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -967,14 +877,7 @@ int42pl(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int32 result;
- result = arg1 + arg2;
-
- /*
- * Overflow check. If the inputs are of different signs then their sum
- * cannot overflow. If the inputs are of the same sign, their sum had
- * better be that sign too.
- */
- if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_add_s32_overflow(arg1, (int32) arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -988,14 +891,7 @@ int42mi(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int32 result;
- result = arg1 - arg2;
-
- /*
- * Overflow check. If the inputs are of the same sign then their
- * difference cannot overflow. If they are of different signs then the
- * result should be of the same sign as the first input.
- */
- if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ if (unlikely(pg_sub_s32_overflow(arg1, (int32) arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1009,20 +905,7 @@ int42mul(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int32 result;
- result = arg1 * arg2;
-
- /*
- * Overflow check. We basically check to see if result / arg1 gives arg2
- * again. There is one case where this fails: arg1 = 0 (which cannot
- * overflow).
- *
- * Since the division is likely much more expensive than the actual
- * multiplication, we'd like to skip it where possible. The best bang for
- * the buck seems to be to check whether both inputs are in the int16
- * range; if so, no overflow is possible.
- */
- if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX) &&
- result / arg1 != arg2)
+ if (unlikely(pg_mul_s32_overflow(arg1, (int32) arg2, &result)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1036,7 +919,7 @@ int42div(PG_FUNCTION_ARGS)
int16 arg2 = PG_GETARG_INT16(1);
int32 result;
- if (arg2 == 0)
+ if (unlikely(arg2 == 0))
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1053,12 +936,11 @@ int42div(PG_FUNCTION_ARGS)
*/
if (arg2 == -1)
{
- result = -arg1;
- /* overflow check (needed for INT_MIN) */
- if (arg1 != 0 && SAMESIGN(result, arg1))
+ if (unlikely(arg1 == PG_INT32_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ result = -arg1;
PG_RETURN_INT32(result);
}
@@ -1075,7 +957,7 @@ int4mod(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
- if (arg2 == 0)
+ if (unlikely(arg2 == 0))
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1103,7 +985,7 @@ int2mod(PG_FUNCTION_ARGS)
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
- if (arg2 == 0)
+ if (unlikely(arg2 == 0))
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1136,12 +1018,11 @@ int4abs(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
int32 result;
- result = (arg1 < 0) ? -arg1 : arg1;
- /* overflow check (needed for INT_MIN) */
- if (result < 0)
+ if (unlikely(arg1 == INT32_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ result = (arg1 < 0) ? -arg1 : arg1;
PG_RETURN_INT32(result);
}
@@ -1151,12 +1032,11 @@ int2abs(PG_FUNCTION_ARGS)
int16 arg1 = PG_GETARG_INT16(0);
int16 result;
- result = (arg1 < 0) ? -arg1 : arg1;
- /* overflow check (needed for SHRT_MIN) */
- if (result < 0)
+ if (unlikely(arg1 == INT16_MIN))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ result = (arg1 < 0) ? -arg1 : arg1;
PG_RETURN_INT16(result);
}
@@ -1381,11 +1261,11 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
if ((fctx->step > 0 && fctx->current <= fctx->finish) ||
(fctx->step < 0 && fctx->current >= fctx->finish))
{
- /* increment current in preparation for next iteration */
- fctx->current += fctx->step;
-
- /* if next-value computation overflows, this is the final result */
- if (SAMESIGN(result, fctx->step) && !SAMESIGN(result, fctx->current))
+ /*
+ * Increment current in preparation for next iteration. If next-value
+ * computation overflows, this is the final result.
+ */
+ if (pg_add_s32_overflow(fctx->current, fctx->step, &fctx->current))
fctx->step = 0;
/* do when there is more left to send */