Module Name:    src
Committed By:   rillig
Date:           Thu Jan  2 20:02:59 UTC 2025

Modified Files:
        src/tests/usr.bin/xlint/lint1: msg_132.c
        src/usr.bin/xlint/lint1: tree.c

Log Message:
lint: fix possible loss of accuracy in multiplication and division


To generate a diff of this commit:
cvs rdiff -u -r1.51 -r1.52 src/tests/usr.bin/xlint/lint1/msg_132.c
cvs rdiff -u -r1.668 -r1.669 src/usr.bin/xlint/lint1/tree.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/usr.bin/xlint/lint1/msg_132.c
diff -u src/tests/usr.bin/xlint/lint1/msg_132.c:1.51 src/tests/usr.bin/xlint/lint1/msg_132.c:1.52
--- src/tests/usr.bin/xlint/lint1/msg_132.c:1.51	Thu Jan  2 18:36:52 2025
+++ src/tests/usr.bin/xlint/lint1/msg_132.c	Thu Jan  2 20:02:59 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_132.c,v 1.51 2025/01/02 18:36:52 rillig Exp $	*/
+/*	$NetBSD: msg_132.c,v 1.52 2025/01/02 20:02:59 rillig Exp $	*/
 # 3 "msg_132.c"
 
 // Test for message: conversion from '%s' to '%s' may lose accuracy [132]
@@ -251,14 +251,28 @@ test_ic_mult(void)
 	// from __BITS, __SHIFTIN, __SHIFTOUT
 	u32 = (u16 & 1023ULL) / 1ULL * 1024ULL | (u16 & 1023ULL) / 1ULL * 1ULL;
 
-	// FIXME
-	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
 	s8 = 1 * s8;
-	// FIXME
-	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
 	s16 = 1 * s16;
 	s32 = 1 * s32;
 	s64 = 1 * s64;
+
+	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
+	s8 = 2 * s8;
+	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
+	s16 = 2 * s16;
+	// No warning, as there is no narrowing conversion.
+	s32 = 2 * s32;
+	// No warning, as there is no narrowing conversion.
+	s64 = 2 * s64;
+
+	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
+	s8 = -1 * s8;
+	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
+	s16 = -1 * s16;
+	// No warning, as there is no narrowing conversion.
+	s32 = -1 * s32;
+	// No warning, as there is no narrowing conversion.
+	s64 = -1 * s64;
 }
 
 void
@@ -272,14 +286,19 @@ test_ic_div(void)
 	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
 	u16 = u32 / 65535;
 
-	// FIXME
-	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
 	s8 = s8 / 1;
-	// FIXME
-	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
 	s16 = s16 / 1;
 	s32 = s32 / 1;
 	s64 = s64 / 1;
+
+	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
+	s8 = s8 / -1;
+	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
+	s16 = s16 / -1;
+	// No warning, as there is no narrowing conversion.
+	s32 = s32 / -1;
+	// No warning, as there is no narrowing conversion.
+	s64 = s64 / -1;
 }
 
 void

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.668 src/usr.bin/xlint/lint1/tree.c:1.669
--- src/usr.bin/xlint/lint1/tree.c:1.668	Thu Jan  2 18:36:51 2025
+++ src/usr.bin/xlint/lint1/tree.c	Thu Jan  2 20:02:59 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.668 2025/01/02 18:36:51 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.669 2025/01/02 20:02:59 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: tree.c,v 1.668 2025/01/02 18:36:51 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.669 2025/01/02 20:02:59 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -147,6 +147,22 @@ si_min_value(const type_t *tp)
 }
 
 static int64_t
+si_mult_sat(const type_t *tp, int64_t l, int64_t r)
+{
+	uint64_t al = s64_abs(l);
+	uint64_t ar = s64_abs(r);
+	bool neg = (l >= 0) != (r >= 0);
+	uint64_t max = ui_max_value(tp);
+	uint64_t max_prod = (uint64_t)max + (neg ? 1 : 0);
+	if (al == 0 || ar <= max_prod / al)
+		return l * r;
+	else if (neg)
+		return -1 - (int64_t)(max >> 1);
+	else
+		return (int64_t)(max >> 1);
+}
+
+static int64_t
 si_plus_sat(const type_t *tp, int64_t a, int64_t b)
 {
 	if (b >= 0) {
@@ -211,8 +227,21 @@ ic_mult(const type_t *tp, integer_constr
 {
 	integer_constraints c;
 
-	if (ic_maybe_signed_binary(tp, a, b)
-	    || (a.umax > 0 && b.umax > ic_any(tp).umax / a.umax))
+	if (ic_maybe_signed_binary(tp, a, b)) {
+		int64_t ll = si_mult_sat(tp, a.smin, b.smin);
+		int64_t lu = si_mult_sat(tp, a.smin, b.smax);
+		int64_t ul = si_mult_sat(tp, a.smax, b.smin);
+		int64_t uu = si_mult_sat(tp, a.smax, b.smax);
+
+		c.smin = s64_min(ll, s64_min(lu, s64_min(ul, uu)));
+		c.smax = s64_max(ll, s64_max(lu, s64_max(ul, uu)));
+		c.umin = c.smin >= 0 ? (uint64_t)c.smin : 0;
+		c.umax = c.smin >= 0 ? (uint64_t)c.smax : UINT64_MAX;
+		c.bclr = ~u64_fill_right(c.umax);
+		return c;
+	}
+
+	if (a.umax > 0 && b.umax > ic_any(tp).umax / a.umax)
 		return ic_any(tp);
 
 	c.smin = INT64_MIN;
@@ -226,8 +255,11 @@ ic_mult(const type_t *tp, integer_constr
 static integer_constraints
 ic_div(const type_t *tp, integer_constraints a, integer_constraints b)
 {
-	if (ic_maybe_signed_binary(tp, a, b))
+	if (ic_maybe_signed_binary(tp, a, b)) {
+		if (b.smin >= 0)
+			return a;
 		return ic_any(tp);
+	}
 
 	integer_constraints c;
 	c.smin = INT64_MIN;
@@ -1102,8 +1134,8 @@ fold_signed_integer(op_t op, int64_t l, 
 		*overflow = l == min_value;
 		return *overflow ? l : -l;
 	case MULT:;
-		uint64_t al = l >= 0 ? (uint64_t)l : -(uint64_t)l;
-		uint64_t ar = r >= 0 ? (uint64_t)r : -(uint64_t)r;
+		uint64_t al = s64_abs(l);
+		uint64_t ar = s64_abs(r);
 		bool neg = (l >= 0) != (r >= 0);
 		uint64_t max_prod = (uint64_t)max_value + (neg ? 1 : 0);
 		if (al > 0 && ar > max_prod / al) {

Reply via email to