Module Name:    src
Committed By:   rillig
Date:           Thu Jan  2 03:46:27 UTC 2025

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

Log Message:
lint: compute integer constraints for xor and minus

These operators allow lint to correctly track the possible values in
expressions that sign-extend an integer.


To generate a diff of this commit:
cvs rdiff -u -r1.48 -r1.49 src/tests/usr.bin/xlint/lint1/msg_132.c
cvs rdiff -u -r1.665 -r1.666 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.48 src/tests/usr.bin/xlint/lint1/msg_132.c:1.49
--- src/tests/usr.bin/xlint/lint1/msg_132.c:1.48	Wed Jan  1 14:09:28 2025
+++ src/tests/usr.bin/xlint/lint1/msg_132.c	Thu Jan  2 03:46:27 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_132.c,v 1.48 2025/01/01 14:09:28 rillig Exp $	*/
+/*	$NetBSD: msg_132.c,v 1.49 2025/01/02 03:46:27 rillig Exp $	*/
 # 3 "msg_132.c"
 
 // Test for message: conversion from '%s' to '%s' may lose accuracy [132]
@@ -327,10 +327,59 @@ test_ic_plus(void)
 	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
 	u8 = 256 + u64 % 1;
 
+	// XXX: No warnings since portable_rank_cmp is the same for both sides.
+	bits.u11 = bits.u10 + bits.u10 + 1;
+	bits.u11 = bits.u10 + bits.u10 + 2;
+	bits.u11 = bits.u10 + 1024;
+	bits.u11 = bits.u10 + 1025;
+
+	u8 = bits.u7 + bits.u7 + 1;
+	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
+	u8 = bits.u7 + bits.u7 + 2;
+	u8 = bits.u7 + 128;
+	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
+	u8 = bits.u7 + 129;
+
+	// The result of the second '+' wraps around, thus the warning,
+	// even though the final result fits in a u16.
+	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
+	u16 = u32 % 0x00010000 + 0x80000000 + 0x80000000;
+
+	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
+	u16 = u32 % 0x00010000 + 0xffff8000;
+	/* expect+1: warning: conversion from 'unsigned int' to 'short' may lose accuracy [132] */
+	s16 = u32 % 0x00010000 + 0xffff8000;
+
+	/* expect+1: warning: conversion from 'long long' to 'unsigned short' may lose accuracy [132] */
+	u16 = s64 % 0x00010000 + 0xffffffffLL + -0xffffffffLL;
+	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
+	u16 = s32 % 0x00010000 + 0x7fff0000 + -0x7fff0000;
+	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
+	u16 = u32 % 0x00010000 + 0xffff0000 + 0x00010000;
+
 	s8 = '0' + s64 % 10;
 }
 
 void
+test_ic_minus(void)
+{
+	// Shift the range [0x00 to 0xff] to [-0x80 to 0x7f].
+	s8 = (s64 & 0xff) - 0x80;
+
+	// Sign-extend the lowest bits.
+	s8 = ((s64 & 0xff) ^ 0x80) - 0x80;
+	s16 = ((s64 & 0xffff) ^ 0x8000) - 0x8000;
+	/* expect+1: warning: extra bits set to 0 in conversion of 'unsigned int' to 'long long', op '&' [309] */
+	s32 = ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
+
+	// Trying to sign-extend, but with off-by-one errors.
+	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
+	s8 = ((s64 & 0xff) ^ 0x80) - 0x7f;
+	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
+	s8 = ((s64 & 0xff) ^ 0x80) - 0x81;
+}
+
+void
 test_ic_shl(void)
 {
 	u64 = u64 << u64;
@@ -392,6 +441,20 @@ test_ic_bitand(void)
 }
 
 void
+test_ic_bitxor(void)
+{
+	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
+	u8 = u8 ^ u16;
+	u16 = u8 ^ u16;
+
+	// Sign-extend.
+	s8 = (u8 ^ 0x80) - 0x80;
+	s16 = (u16 ^ 0x8000) - 0x8000;
+	s32 = (u32 ^ 0x80000000) - 0x80000000;
+	s64 = (u64 ^ 0x8000000000000000) - 0x8000000000000000;
+}
+
+void
 test_ic_bitor(void)
 {
 	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.665 src/usr.bin/xlint/lint1/tree.c:1.666
--- src/usr.bin/xlint/lint1/tree.c:1.665	Wed Jan  1 14:09:28 2025
+++ src/usr.bin/xlint/lint1/tree.c	Thu Jan  2 03:46:27 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.665 2025/01/01 14:09:28 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.666 2025/01/02 03:46:27 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.665 2025/01/01 14:09:28 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.666 2025/01/02 03:46:27 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -83,43 +83,46 @@ u64_max(uint64_t a, uint64_t b)
 	return a > b ? a : b;
 }
 
-static int64_t
-si_min_value(const type_t *tp)
+static uint64_t
+ui_max_value(const type_t *tp)
 {
-	uint64_t mask = value_bits(size_in_bits(tp->t_tspec));
-	return -(int64_t)(mask >> 1) - 1;
+	return value_bits(size_in_bits(tp->t_tspec));
 }
 
 static int64_t
 si_max_value(const type_t *tp)
 {
-	uint64_t mask = value_bits(size_in_bits(tp->t_tspec));
-	return (int64_t)(mask >> 1);
+	return (int64_t)(ui_max_value(tp) >> 1);
 }
 
-static uint64_t
-ui_max_value(const type_t *tp)
+static int64_t
+si_min_value(const type_t *tp)
 {
-	return value_bits(size_in_bits(tp->t_tspec));
+	return -si_max_value(tp) - 1;
 }
 
 static int64_t
 si_plus_sat(const type_t *tp, int64_t a, int64_t b)
 {
-	if (a >= 0) {
+	if (b >= 0) {
 		int64_t max = si_max_value(tp);
-		return b <= max - a ? a + b : max;
+		return a <= max - b ? a + b : max;
 	} else {
 		int64_t min = si_min_value(tp);
-		return b >= min - a ? a + b : min;
+		return a >= min - b ? a + b : min;
 	}
 }
 
-static uint64_t
-ui_plus_sat(const type_t *tp, uint64_t a, uint64_t b)
+static int64_t
+si_minus_sat(const type_t *tp, int64_t a, int64_t b)
 {
-	uint64_t max = ui_max_value(tp);
-	return b <= max - a ? a + b : max;
+	if (b >= 0) {
+		int64_t min = si_min_value(tp);
+		return a >= min + b ? a - b : min;
+	} else {
+		int64_t max = si_max_value(tp);
+		return a <= max + b ? a - b : max;
+	}
 }
 
 static uint64_t
@@ -175,10 +178,11 @@ ic_any(const type_t *tp)
 {
 	integer_constraints c;
 
-	uint64_t vbits = value_bits(width_in_bits(tp));
+	unsigned width = width_in_bits(tp);
+	uint64_t vbits = value_bits(width);
 	if (is_uinteger(tp->t_tspec)) {
-		c.smin = INT64_MIN;
-		c.smax = INT64_MAX;
+		c.smin = width < 64 ? 0 : INT64_MIN;
+		c.smax = width < 64 ? (int64_t)vbits : INT64_MAX;
 		c.umin = 0;
 		c.umax = vbits;
 		c.bclr = ~c.umax;
@@ -260,13 +264,55 @@ ic_mod(const type_t *tp, integer_constra
 static integer_constraints
 ic_plus(const type_t *tp, integer_constraints a, integer_constraints b)
 {
-	bool maybe_signed = ic_maybe_signed(tp, &a) || ic_maybe_signed(tp, &b);
+	if (ic_maybe_signed(tp, &a) || ic_maybe_signed(tp, &b)) {
+		integer_constraints c;
+		c.smin = si_plus_sat(tp, a.smin, b.smin);
+		c.smax = si_plus_sat(tp, a.smax, b.smax);
+		c.umin = 0;
+		c.umax = UINT64_MAX;
+		c.bclr = 0;
+		return c;
+	}
 
+	uint64_t max = ui_max_value(tp);
 	integer_constraints c;
-	c.smin = maybe_signed ? si_plus_sat(tp, a.smin, b.smin) : INT64_MIN;
-	c.smax = maybe_signed ? si_plus_sat(tp, a.smax, b.smax) : INT64_MAX;
-	c.umin = maybe_signed ? 0 : ui_plus_sat(tp, a.umin, b.umin);
-	c.umax = maybe_signed ? UINT64_MAX : ui_plus_sat(tp, a.umax, b.umax);
+	c.smin = INT64_MIN;
+	c.smax = INT64_MAX;
+	if (b.umax <= max - a.umax) {
+		c.umin = a.umin + b.umin;
+		c.umax = a.umax + b.umax;
+	} else {
+		c.umin = 0;
+		c.umax = max;
+	}
+	c.bclr = ~u64_fill_right(c.umax);
+	return c;
+}
+
+static integer_constraints
+ic_minus(const type_t *tp, integer_constraints a, integer_constraints b)
+{
+	if (ic_maybe_signed(tp, &a) || ic_maybe_signed(tp, &b)) {
+		integer_constraints c;
+		c.smin = si_minus_sat(tp, a.smin, b.smax);
+		c.smax = si_minus_sat(tp, a.smax, b.smin);
+		c.umin = 0;
+		c.umax = UINT64_MAX;
+		c.bclr = 0;
+		return c;
+	}
+
+	integer_constraints c;
+	c.smin = si_minus_sat(tp, a.smin, b.smax);
+	c.smax = si_minus_sat(tp, a.smax, b.smin);
+	if (a.umin >= b.umax) {
+		c.umin = a.umin - b.umax;
+		c.umax = a.umax - b.umin;
+	} else {
+		c.umin = 0;
+		c.umax = is_uinteger(tp->t_tspec) ? ui_max_value(tp)
+		    : UINT64_MAX;
+	}
 	c.bclr = ~u64_fill_right(c.umax);
 	return c;
 }
@@ -337,6 +383,21 @@ ic_bitand(integer_constraints a, integer
 }
 
 static integer_constraints
+ic_bitxor(const type_t *tp, integer_constraints a, integer_constraints b)
+{
+	integer_constraints c = ic_any(tp);
+	if (ic_maybe_signed(tp, &a) || ic_maybe_signed(tp, &b))
+		return c;
+	c.umax = a.umax | b.umax;
+	if (c.umax >> 63 == 0) {
+		c.smin = 0;
+		c.smax = (int64_t)c.umax;
+	}
+	c.bclr = a.bclr & b.bclr;
+	return c;
+}
+
+static integer_constraints
 ic_bitor(integer_constraints a, integer_constraints b)
 {
 	integer_constraints c;
@@ -417,6 +478,10 @@ ic_expr(const tnode_t *tn)
 		lc = ic_expr(before_conversion(tn->u.ops.left));
 		rc = ic_expr(before_conversion(tn->u.ops.right));
 		return ic_plus(tn->tn_type, lc, rc);
+	case MINUS:
+		lc = ic_expr(before_conversion(tn->u.ops.left));
+		rc = ic_expr(before_conversion(tn->u.ops.right));
+		return ic_minus(tn->tn_type, lc, rc);
 	case SHL:
 		lc = ic_expr(tn->u.ops.left);
 		rc = ic_expr(tn->u.ops.right);
@@ -429,6 +494,10 @@ ic_expr(const tnode_t *tn)
 		lc = ic_expr(tn->u.ops.left);
 		rc = ic_expr(tn->u.ops.right);
 		return ic_bitand(lc, rc);
+	case BITXOR:
+		lc = ic_expr(tn->u.ops.left);
+		rc = ic_expr(tn->u.ops.right);
+		return ic_bitxor(tn->tn_type, lc, rc);
 	case BITOR:
 		lc = ic_expr(tn->u.ops.left);
 		rc = ic_expr(tn->u.ops.right);

Reply via email to