Linux SPARSE checker discussions
 help / color / mirror / Atom feed
From: Bart Van Assche <bvanassche@acm.org>
To: linux-sparse@vger.kernel.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
	Bart Van Assche <bvanassche@acm.org>
Subject: [PATCH 17/73] simplify: Prevent overflow on LLONG_MIN negation and fix simplifications
Date: Mon,  9 Nov 2026 12:32:03 -0800	[thread overview]
Message-ID: <20261109203259.362845-18-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>

Avoid undefined integer overflow and invalid IR transformations in
linearize.h and simplify.c:
- Check that operand and instruction sizes do not exceed 64 bits before
  evaluating 64-bit constant simplifications or calling eval_op() and
  eval_unop().
- Use bits_mask(), sign_extend(), and zero_extend() instead of open-coded
  64-bit shifts to avoid undefined shift overflow on 64-bit types.
- Check for null pseudo pointers in DEF_OPCODE(), canonical_order(), and
  simplification helpers before dereferencing operands.
- Update CFG edges and phi sources accurately in simplify_cgoto().
- Add regression test validation/optim/simplify-memops-ssa-fixes.c.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 linearize.h                                  |   2 +-
 simplify.c                                   | 225 ++++++++++++++-----
 validation/optim/simplify-memops-ssa-fixes.c | 103 +++++++++
 3 files changed, 278 insertions(+), 52 deletions(-)
 create mode 100644 validation/optim/simplify-memops-ssa-fixes.c

diff --git a/linearize.h b/linearize.h
index cac6082fc886..3412494edbef 100644
--- a/linearize.h
+++ b/linearize.h
@@ -187,7 +187,7 @@ struct basic_block {
 // and OP_BADOP if not. It also assigns the defining instruction
 // to ``DEF``.
 #define DEF_OPCODE(DEF, SRC)	\
-	(((SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP)
+	(((SRC) && (SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP)
 
 
 static inline void add_bb(struct basic_block_list **list, struct basic_block *bb)
diff --git a/simplify.c b/simplify.c
index f20b6f1a9881..2ddaf1c619a8 100644
--- a/simplify.c
+++ b/simplify.c
@@ -57,7 +57,7 @@
 // check if a pseudo is a power of 2
 static inline bool is_pow2(pseudo_t src)
 {
-	if (src->type != PSEUDO_VAL)
+	if (!src || src->type != PSEUDO_VAL)
 		return false;
 	return is_power_of_2(src->value);
 }
@@ -315,6 +315,9 @@ static void kill_asm(struct instruction *insn)
 	FOR_EACH_PTR(insn->asm_rules->inputs, con) {
 		kill_use(&con->pseudo);
 	} END_FOR_EACH_PTR(con);
+	FOR_EACH_PTR(insn->asm_rules->outputs, con) {
+		kill_use(&con->pseudo);
+	} END_FOR_EACH_PTR(con);
 }
 
 ///
@@ -428,7 +431,7 @@ void remove_dead_insns(struct entrypoint *ep)
 
 static inline int constant(pseudo_t pseudo)
 {
-	return pseudo->type == PSEUDO_VAL;
+	return pseudo && pseudo->type == PSEUDO_VAL;
 }
 
 ///
@@ -444,7 +447,7 @@ static inline pseudo_t is_same_op(pseudo_t src, int op, unsigned osize)
 {
 	struct instruction *def;
 
-	if (src->type != PSEUDO_REG)
+	if (!src || src->type != PSEUDO_REG)
 		return NULL;
 	def = src->def;
 	if (def->opcode != op)
@@ -582,7 +585,7 @@ static int swap_select(struct instruction *out, struct instruction *in, pseudo_t
 
 static inline int def_opcode(pseudo_t p)
 {
-	if (p->type != PSEUDO_REG)
+	if (!p || p->type != PSEUDO_REG)
 		return OP_BADOP;
 	return p->def->opcode;
 }
@@ -629,12 +632,18 @@ static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
 static pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2)
 {
 	/* FIXME! Verify signs and sizes!! */
-	long long left = src1->value;
-	long long right = src2->value;
+	long long left;
+	long long right;
 	unsigned long long ul, ur;
 	long long res;
 	unsigned long long mask, bits;
 
+	if (!size || size > 64)
+		return NULL;
+
+	left = src1->value;
+	right = src2->value;
+
 	mask = sign_bit(size);
 	bits = bits_mask(size);
 
@@ -670,7 +679,7 @@ static pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2)
 	case OP_DIVS:
 		if (!right)
 			goto undef;
-		if (left == mask && right == -1)
+		if (ul == mask && right == -1)
 			goto undef;
 		res = left / right;
 		break;
@@ -682,7 +691,7 @@ static pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2)
 	case OP_MODS:
 		if (!right)
 			goto undef;
-		if (left == mask && right == -1)
+		if (ul == mask && right == -1)
 			goto undef;
 		res = left % right;
 		break;
@@ -1136,10 +1145,14 @@ static int simplify_seteq_setne(struct instruction *insn, long long value)
 		return REPEAT_CSE;
 
 	case OP_SEXT:
+		if (!def->orig_type)
+			break;
 		if (value && (def->orig_type->bit_size == 1))
 			break;
 		/* Fall through */
 	case OP_ZEXT:
+		if (!def->orig_type)
+			break;
 		// Convert:
 		//	*ext.m	%s <- (1) %a
 		//	setne.1 %r <- %s, $0
@@ -1149,7 +1162,8 @@ static int simplify_seteq_setne(struct instruction *insn, long long value)
 		insn->itype = def->orig_type;
 		return replace_pseudo(insn, &insn->src1, def->src);
 	case OP_TRUNC:
-		if (!one_use(old))
+		if (!one_use(old) || !def->orig_type ||
+		    def->orig_type->bit_size > 64 || def->size >= 64)
 			break;
 		// convert
 		//	trunc.n	%s <- (o) %a
@@ -1159,6 +1173,7 @@ static int simplify_seteq_setne(struct instruction *insn, long long value)
 		//	setne.m %r <- %s, $0
 		// and same for setne/eq ... 0/1
 		osize = def->size;
+		insn->itype = def->orig_type;
 		def->opcode = OP_AND;
 		def->type = def->orig_type;
 		def->size = def->type->bit_size;
@@ -1208,12 +1223,16 @@ static int simplify_unsigned_zero_compare(struct instruction *insn, int result)
 static int simplify_compare_constant(struct instruction *insn, long long value)
 {
 	unsigned size = insn->itype->bit_size;
-	unsigned long long bits = bits_mask(size);
+	unsigned long long bits;
 	struct instruction *def;
 	pseudo_t src1, src2;
 	unsigned int osize;
 	int changed = 0;
 
+	if (!size || size > 64)
+		return 0;
+	bits = bits_mask(size);
+
 	switch (insn->opcode) {
 	case OP_SET_LT:
 		if (!value)
@@ -1457,6 +1476,8 @@ static int simplify_compare_constant(struct instruction *insn, long long value)
 		}
 		break;
 	case OP_TRUNC:
+		if (!def->orig_type || def->orig_type->bit_size > 64 || size >= 64)
+			break;
 		osize = def->orig_type->bit_size;
 		switch (insn->opcode) {
 		case OP_SET_EQ: case OP_SET_NE:
@@ -1547,7 +1568,12 @@ static int simplify_const_rightadd(struct instruction *def, struct instruction *
 	switch (def->opcode) {
 	case OP_SUB:
 		if (constant(def->src1)) { // (C - y) + D --> eval(C+D) - y
-			pseudo_t val = eval_op(OP_ADD, size, def->src1, src2);
+			pseudo_t val;
+			if (size > 64)
+				break;
+			val = eval_op(OP_ADD, size, def->src1, src2);
+			if (!val)
+				break;
 			insn->opcode = OP_SUB;
 			use_pseudo(insn, def->src2, &insn->src2);
 			return replace_pseudo(insn, &insn->src1, val);
@@ -1560,9 +1586,13 @@ static int simplify_const_rightadd(struct instruction *def, struct instruction *
 static int simplify_constant_rightside(struct instruction *insn)
 {
 	long long value = insn->src2->value;
-	unsigned long long bits = bits_mask(insn->size);
+	unsigned long long bits;
 	int changed = 0;
 
+	if (insn->size > 64)
+		return 0;
+	bits = bits_mask(insn->size);
+
 	switch (insn->opcode) {
 	case OP_OR:
 		if ((value & bits) == bits)
@@ -1632,13 +1662,24 @@ static int simplify_const_leftsub(struct instruction *insn, struct instruction *
 	switch (def->opcode) {
 	case OP_ADD:
 		if (constant(def->src2)) { // C - (y + D) --> eval(C-D) - y
-			insn->src1 = eval_op(OP_SUB, size, src1, def->src2);
+			pseudo_t val;
+			if (size > 64)
+				break;
+			val = eval_op(OP_SUB, size, src1, def->src2);
+			if (!val)
+				break;
+			insn->src1 = val;
 			return replace_pseudo(insn, &insn->src2, def->src1);
 		}
 		break;
 	case OP_SUB:
 		if (constant(def->src1)) { // C - (D - z) --> z + eval(C-D)
-			pseudo_t val = eval_op(OP_SUB, size, src1, def->src1);
+			pseudo_t val;
+			if (size > 64)
+				break;
+			val = eval_op(OP_SUB, size, src1, def->src1);
+			if (!val)
+				break;
 			insn->opcode = OP_ADD;
 			use_pseudo(insn, def->src2, &insn->src1);
 			return replace_pseudo(insn, &insn->src2, val);
@@ -1650,7 +1691,11 @@ static int simplify_const_leftsub(struct instruction *insn, struct instruction *
 
 static int simplify_constant_leftside(struct instruction *insn)
 {
-	long long value = insn->src1->value;
+	long long value;
+
+	if (!insn->src1 || !insn->src2 || insn->size > 64)
+		return 0;
+	value = insn->src1->value;
 
 	switch (insn->opcode) {
 	case OP_ADD: case OP_OR: case OP_XOR:
@@ -1717,6 +1762,8 @@ static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg)
 
 static int simplify_binop(struct instruction *insn)
 {
+	if (!insn->src1 || !insn->src2)
+		return 0;
 	if (constant(insn->src1)) {
 		if (constant(insn->src2))
 			return simplify_constant_binop(insn);
@@ -1753,8 +1800,12 @@ static int switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instru
 //	* VOID, UNDEF and PHI are uninteresting (but VOID should have type 0)
 static int canonical_order(pseudo_t p1, pseudo_t p2)
 {
-	int t1 = p1->type;
-	int t2 = p2->type;
+	int t1, t2;
+
+	if (!p1 || !p2)
+		return 1;
+	t1 = p1->type;
+	t2 = p2->type;
 
 	/* symbol/constants on the right */
 	if (t1 < t2)
@@ -1793,7 +1844,7 @@ static int canonicalize_compare(struct instruction *insn)
 
 static inline int simple_pseudo(pseudo_t pseudo)
 {
-	return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM;
+	return pseudo && (pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM);
 }
 
 ///
@@ -1820,7 +1871,7 @@ static inline bool can_move_to(pseudo_t src, struct instruction *dst)
 
 	if (!one_use(dst->target))
 		return false;
-	if (src->type != PSEUDO_REG)
+	if (!src || src->type != PSEUDO_REG)
 		return true;
 
 	def = src->def;
@@ -1851,8 +1902,14 @@ static int simplify_associative_binop(struct instruction *insn)
 	if (!simple_pseudo(def->src2))
 		return 0;
 	if (constant(def->src2) && constant(insn->src2)) {
+		pseudo_t val;
+		if (insn->size > 64)
+			return 0;
+		val = eval_op(insn->opcode, insn->size, insn->src2, def->src2);
+		if (!val)
+			return 0;
 		// (x # C) # K --> x # eval(C # K)
-		insn->src2 = eval_op(insn->opcode, insn->size, insn->src2, def->src2);
+		insn->src2 = val;
 		return replace_pseudo(insn, &insn->src1, def->src1);
 	}
 
@@ -1873,12 +1930,12 @@ static int simplify_add_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 	switch (DEF_OPCODE(def, src1)) {
 	case OP_MUL:
 		if (DEF_OPCODE(defr, *p2) == OP_MUL) {
-			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
+			if (defr->src2 == def->src2 && can_move_to(def->src1, defr)) {
 				// ((x * z) + (y * z)) into ((x + y) * z)
 				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
 				return REPEAT_CSE;
 			}
-			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
+			if (defr->src1 == def->src1 && can_move_to(def->src2, defr)) {
 				// ((z * x) + (z * y)) into ((x + y) * z)
 				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
 				return REPEAT_CSE;
@@ -1991,12 +2048,12 @@ static int simplify_and_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 		break;
 	case OP_OR:
 		if (DEF_OPCODE(defr, *p2) == OP_OR) {
-			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
+			if (defr->src2 == def->src2 && can_move_to(def->src1, defr)) {
 				// ((x | z) & (y | z)) into ((x & y) | z)
 				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
 				return REPEAT_CSE;
 			}
-			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
+			if (defr->src1 == def->src1 && can_move_to(def->src2, defr)) {
 				// ((z | x) & (z | y)) into ((x & y) | z)
 				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
 				return REPEAT_CSE;
@@ -2035,12 +2092,12 @@ static int simplify_ior_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 	switch (DEF_OPCODE(def, src1)) {
 	case OP_AND:
 		if (DEF_OPCODE(defr, *p2) == OP_AND) {
-			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
+			if (defr->src2 == def->src2 && can_move_to(def->src1, defr)) {
 				// ((x & z) | (y & z)) into ((x | y) & z)
 				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
 				return REPEAT_CSE;
 			}
-			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
+			if (defr->src1 == def->src1 && can_move_to(def->src2, defr)) {
 				// ((z & x) | (z & y)) into ((x | y) & z)
 				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
 				return REPEAT_CSE;
@@ -2053,6 +2110,8 @@ static int simplify_ior_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 		}
 		break;
 	case OP_NOT:
+		if (insn->size > 64)
+			break;
 		if (def->src == *p2)
 			return replace_with_value(insn, bits_mask(insn->size));
 		break;
@@ -2089,12 +2148,12 @@ static int simplify_xor_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 	switch (DEF_OPCODE(def, src1)) {
 	case OP_AND:
 		if (DEF_OPCODE(defr, *p2) == OP_AND) {
-			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
+			if (defr->src2 == def->src2 && can_move_to(def->src1, defr)) {
 				// ((x & z) ^ (y & z)) into ((x ^ y) & z)
 				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
 				return REPEAT_CSE;
 			}
-			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
+			if (defr->src1 == def->src1 && can_move_to(def->src2, defr)) {
 				// ((z & x) ^ (z & y)) into ((x ^ y) & z)
 				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
 				return REPEAT_CSE;
@@ -2107,6 +2166,8 @@ static int simplify_xor_one_side(struct instruction *insn, pseudo_t *p1, pseudo_
 		}
 		break;
 	case OP_NOT:
+		if (insn->size > 64)
+			break;
 		if (def->src == *p2)
 			return replace_with_value(insn, bits_mask(insn->size));
 		break;
@@ -2137,8 +2198,11 @@ static int simplify_xor(struct instruction *insn)
 
 static int simplify_constant_unop(struct instruction *insn)
 {
-	long long val = insn->src1->value;
-	unsigned long long res, mask;
+	unsigned long long val = insn->src1->value;
+	unsigned long long res;
+
+	if (!insn->size || insn->size > 64)
+		return 0;
 
 	switch (insn->opcode) {
 	case OP_NOT:
@@ -2148,18 +2212,24 @@ static int simplify_constant_unop(struct instruction *insn)
 		res = -(unsigned long long)val;
 		break;
 	case OP_SEXT:
-		mask = sign_bit(insn->orig_type->bit_size);
-		if (val & mask)
-			val |= ~bits_mask(insn->orig_type->bit_size);
-		/* fall through */
+		if (!insn->orig_type || insn->orig_type->bit_size > 64)
+			return 0;
+		res = sign_extend(val, insn->orig_type->bit_size);
+		break;
 	case OP_ZEXT:
+		if (!insn->orig_type || insn->orig_type->bit_size > 64)
+			return 0;
+		val = zero_extend(val, insn->orig_type->bit_size);
+		/* fall through */
 	case OP_TRUNC:
+		if (!insn->orig_type || insn->orig_type->bit_size > 64)
+			return 0;
 		res = val;
 		break;
 	default:
 		return 0;
 	}
-	res &= bits_mask(insn->size);
+	res = zero_extend(res, insn->size);
 	
 	return replace_with_value(insn, res);
 }
@@ -2176,7 +2246,7 @@ static int simplify_unop(struct instruction *insn)
 	case OP_NOT:
 		switch (DEF_OPCODE(def, src)) {
 		case OP_ADD:
-			if (!constant(def->src2))
+			if (insn->size > 64 || !constant(def->src2))
 				break;
 			insn->opcode = OP_SUB;	// ~(x + C) --> ~C - x
 			src = eval_unop(OP_NOT, insn->size, def->src2);
@@ -2189,13 +2259,13 @@ static int simplify_unop(struct instruction *insn)
 		case OP_NOT:			// ~(~x) --> x
 			return replace_with_pseudo(insn, def->src);
 		case OP_SUB:
-			if (!constant(def->src1))
+			if (insn->size > 64 || !constant(def->src1))
 				break;
 			insn->opcode = OP_ADD;	// ~(C - x) --> x + ~C
 			insn->src2 = eval_unop(OP_NOT, insn->size, def->src1);
 			return replace_pseudo(insn, &insn->src1, def->src2);
 		case OP_XOR:
-			if (!constant(def->src2))
+			if (insn->size > 64 || !constant(def->src2))
 				break;
 			insn->opcode = OP_XOR;	// ~(x ^ C) --> x ^ ~C
 			insn->src2 = eval_unop(OP_NOT, insn->size, def->src2);
@@ -2205,7 +2275,7 @@ static int simplify_unop(struct instruction *insn)
 	case OP_NEG:
 		switch (DEF_OPCODE(def, src)) {
 		case OP_ADD:
-			if (!constant(def->src2))
+			if (insn->size > 64 || !constant(def->src2))
 				break;
 			insn->opcode = OP_SUB;	// -(x + C) --> (-C - x)
 			src = eval_unop(OP_NEG, insn->size, def->src2);
@@ -2314,7 +2384,7 @@ static int simplify_cast(struct instruction *insn)
 	switch (def_opcode(src)) {
 	case OP_AND:
 		val = def->src2;
-		if (val->type != PSEUDO_VAL)
+		if (!constant(val) || size > 64 || def->size > 64)
 			break;
 		/* A cast of a AND might be a no-op.. */
 		switch (insn->opcode) {
@@ -2395,13 +2465,15 @@ static int simplify_cast(struct instruction *insn)
 	case OP_OR:
 		switch (insn->opcode) {
 		case OP_TRUNC:
+			if (size > 64 || def->size > 64)
+				break;
 			mask = bits_mask(insn->size);
 			return simplify_mask_or(insn, mask, def);
 		}
 		break;
 	case OP_LSR:
 	case OP_SHL:
-		if (insn->opcode != OP_TRUNC)
+		if (insn->opcode != OP_TRUNC || size > 64 || def->size > 64)
 			break;
 		mask = bits_mask(insn->size);
 		return simplify_mask_shift(def, mask);
@@ -2411,7 +2483,7 @@ static int simplify_cast(struct instruction *insn)
 			insn->orig_type = def->orig_type;
 			return replace_pseudo(insn, &insn->src1, def->src);
 		case OP_SEXT:
-			if (size != def->orig_type->bit_size)
+			if (!def->orig_type || size > 64 || size != def->orig_type->bit_size)
 				break;
 			if (DEF_OPCODE(def2, def->src) != OP_LSR)
 				break;
@@ -2422,7 +2494,8 @@ static int simplify_cast(struct instruction *insn)
 			insn->src2 = def2->src2;
 			return replace_pseudo(insn, &insn->src1, def2->src1);
 		case OP_ZEXT:
-			if (size != def->orig_type->bit_size)
+			if (!def->orig_type || size > 64 || def->size >= 64 ||
+			    size != def->orig_type->bit_size)
 				break;
 			insn->opcode = OP_AND;
 			insn->src2 = value_pseudo(bits_mask(def->size));
@@ -2451,6 +2524,10 @@ static int simplify_cast(struct instruction *insn)
 			return replace_pseudo(insn, &insn->src, def->src);
 		}
 		switch (insn->opcode) {
+		case OP_ZEXT:
+			if (insn->orig_type != def->orig_type)
+				break;
+			/* fall through */
 		case OP_SEXT:
 			insn->orig_type = def->orig_type;
 			return replace_pseudo(insn, &insn->src, def->src);
@@ -2691,6 +2768,10 @@ static int simplify_switch(struct instruction *insn)
 	if (!constant(cond))
 		return 0;
 	val = insn->cond->value;
+	if (insn->type && insn->size > 0) {
+		bool is_signed = !(insn->type->ctype.modifiers & MOD_UNSIGNED);
+		val = bits_extend(val, insn->size, is_signed);
+	}
 
 	FOR_EACH_PTR(insn->multijmp_list, jmp) {
 		/* Default case */
@@ -2717,34 +2798,76 @@ static struct basic_block *is_label(pseudo_t pseudo)
 
 static int simplify_cgoto(struct instruction *insn)
 {
-	struct basic_block *target, *bb = insn->bb;
-	struct basic_block *bbt, *bbf;
+	struct basic_block *target, *orig_target, *bb = insn->bb;
+	struct basic_block *bbt, *bbf, *orig_bbt, *orig_bbf;
 	struct instruction *def;
 	struct multijmp *jmp;
 
 	switch (DEF_OPCODE(def, insn->src)) {
 	case OP_SEL:	// CGOTO(SEL(x, L1, L2)) --> CBR x, L1, L2
 		if ((bbt = is_label(def->src2)) && (bbf = is_label(def->src3))) {
+			if (!bbt->ep || !bbf->ep)
+				return 0;
+			if (bbt == bbf) {
+				target = bbt;
+				goto convert_to_br;
+			}
+			orig_bbt = bbt;
+			orig_bbf = bbf;
+			FOR_EACH_PTR(insn->multijmp_list, jmp) {
+				if (jmp->target == bbt) {
+					bbt = NULL;
+					continue;
+				}
+				if (jmp->target == bbf) {
+					bbf = NULL;
+					continue;
+				}
+				remove_bb_from_list(&jmp->target->parents, bb, 1);
+				remove_bb_from_list(&bb->children, jmp->target, 1);
+				remove_phisources(bb, jmp->target);
+			} END_FOR_EACH_PTR(jmp);
+			free_ptr_list(&insn->multijmp_list);
 			insn->opcode = OP_CBR;
-			insn->bb_true = bbt;
-			insn->bb_false = bbf;
-			return replace_pseudo(insn, &insn->src1, def->cond);
+			insn->bb_true = orig_bbt;
+			insn->bb_false = orig_bbf;
+			if (bbt) {
+				add_bb(&orig_bbt->parents, bb);
+				add_bb(&bb->children, orig_bbt);
+			}
+			if (bbf) {
+				add_bb(&orig_bbf->parents, bb);
+				add_bb(&bb->children, orig_bbf);
+			}
+			replace_pseudo(insn, &insn->src1, def->cond);
+			return REPEAT_CSE|REPEAT_CFG_CLEANUP;
 		}
 		break;
 	case OP_LABEL:
 		target = def->bb_true;
 		if (!target->ep)
 			return 0;
+	convert_to_br:
+		orig_target = target;
 		FOR_EACH_PTR(insn->multijmp_list, jmp) {
-			if (jmp->target == target)
+			if (jmp->target == target) {
+				target = NULL;
 				continue;
+			}
 			remove_bb_from_list(&jmp->target->parents, bb, 1);
 			remove_bb_from_list(&bb->children, jmp->target, 1);
-			DELETE_CURRENT_PTR(jmp);
+			remove_phisources(bb, jmp->target);
 		} END_FOR_EACH_PTR(jmp);
+		free_ptr_list(&insn->multijmp_list);
+		if (target) {
+			add_bb(&orig_target->parents, bb);
+			add_bb(&bb->children, orig_target);
+		}
 		kill_use(&insn->src);
+		insn->cond = NULL;
 		insn->opcode = OP_BR;
-		insn->bb_true = target;
+		insn->bb_true = orig_target;
+		insn->bb_false = NULL;
 		return REPEAT_CSE|REPEAT_CFG_CLEANUP;
 	}
 	return 0;
diff --git a/validation/optim/simplify-memops-ssa-fixes.c b/validation/optim/simplify-memops-ssa-fixes.c
new file mode 100644
index 000000000000..b94e0182902e
--- /dev/null
+++ b/validation/optim/simplify-memops-ssa-fixes.c
@@ -0,0 +1,103 @@
+/* 1. Negation of LLONG_MIN */
+static long long test_neg_llong_min(void)
+{
+	return -(-9223372036854775807LL - 1LL);
+}
+
+/* 2. Signed bitfield post-increment assigned to another bitfield */
+struct sbf {
+	signed int a:3;
+	signed int b:3;
+	signed int c:3;
+};
+
+int bf_postinc_const(void)
+{
+	struct sbf s = { -1, 0, 0 };
+	s.b = s.a++;
+	return s.c;
+}
+
+/* 3. ((x & mask) | bits) == C with bits outside mask */
+int and_or_equ(unsigned x)
+{
+	return ((x & 0xf0) | 0x01) == 0x11;
+}
+
+/* 4. Commuted (x + 1U) - x */
+unsigned add_sub_cancel(unsigned x)
+{
+	return (x + 1U) - x;
+}
+
+/* 5. 0 / non-constant */
+int zero_div(int x)
+{
+	return 0 / (x ? 1 : 2);
+}
+
+/* 6. Computed goto OP_SEL with identical targets and live PHI */
+int cgoto_same_target_phi(int c, int d)
+{
+	void *p = c ? &&L1 : &&L1;
+	int r = 1;
+	if (d) {
+		r = 2;
+		goto L2;
+	}
+	goto *p;
+L2:
+	r = 3;
+L1:
+	return r;
+}
+
+/* 7. Computed goto OP_LABEL deduplication with live PHI */
+int cgoto_label_dedup(int c)
+{
+	void *p = &&L1;
+	int r = 1;
+	if (c) {
+		r = 2;
+		goto L2;
+	}
+	goto *p;
+L2:
+	r = 3;
+L1:
+	return r;
+}
+
+/* 8. 128-bit zero-extension and addition above 64-bit width */
+__int128 zext_u64_to_s128(void)
+{
+	return (__int128)0x8000000000000000ULL;
+}
+
+unsigned __int128 add_u128_carry(void)
+{
+	return (unsigned __int128)0x8000000000000000ULL + 0x8000000000000000ULL;
+}
+
+/* 9. ZEXT of SEXT with distinct source widths */
+unsigned long long zext_sext_char(signed char c)
+{
+	return (unsigned long long)(unsigned int)(int)c;
+}
+
+/*
+ * check-name: simplify-memops-ssa-fixes
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: VOID
+ * check-output-contains: ret\\.64.*\\$0x8000000000000000
+ * check-output-contains: zext\\.128
+ * check-output-contains: add\\.128
+ * check-output-contains: sext\\.32
+ * check-output-contains: zext\\.64
+ *
+ * check-error-start
+optim/simplify-memops-ssa-fixes.c:4:16: warning: constant integer operation overflow
+ * check-error-end
+ */

  parent reply	other threads:[~2026-11-09 20:33 UTC|newest]

Thread overview: 74+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09 20:31 Bart Van Assche
2026-11-09 20:31 ` [PATCH 01/73] Makefile: Pass '--' to rm in clean target to handle dash-prefixed files Bart Van Assche
2026-11-09 20:31 ` [PATCH 02/73] sparse-llvm: Use LLVMCreateBuilderForModule() with LLVM 24 and later Bart Van Assche
2026-11-09 20:31 ` [PATCH 03/73] typeof_unqual: Strip qualifiers and address space from types and arrays Bart Van Assche
2026-11-09 20:31 ` [PATCH 04/73] bitfield: Pack consecutive _Bool bitfields into byte containers Bart Van Assche
2026-11-09 20:31 ` [PATCH 05/73] allocate: Harden slab allocation bounds, zeroing, and mmap checks Bart Van Assche
2026-11-09 20:31 ` [PATCH 06/73] tokenize: Support 4096-byte identifiers and fix escape bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 07/73] pre-process: Fix macro expansion and include directive handling Bart Van Assche
2026-11-09 20:31 ` [PATCH 08/73] options, lib: Harden warning buffers and command-line option parsing Bart Van Assche
2026-11-09 20:31 ` [PATCH 09/73] builtin: Fix atomic builtin type checking and object_size bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 10/73] parse, show-parse: Fix attribute propagation, enum overflow, and bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 11/73] target-openrisc: Set target_openrisc.mach to MACH_OPENRISC Bart Van Assche
2026-11-09 20:31 ` [PATCH 12/73] evaluate: Fix pointer arithmetic, incomplete types, and qualifiers Bart Van Assche
2026-11-09 20:31 ` [PATCH 13/73] expand: Fix static initializer folding and floating-point casts Bart Van Assche
2026-11-09 20:32 ` [PATCH 14/73] inline: Copy union cast types and VLA bounds when inlining Bart Van Assche
2026-11-09 20:32 ` [PATCH 15/73] linearize, flow: Fix asm operands, switch case bounds, and DSE Bart Van Assche
2026-11-09 20:32 ` [PATCH 16/73] memops, ssa: Walk parent insns in reverse and skip mixed-size SSA Bart Van Assche
2026-11-09 20:32 ` Bart Van Assche [this message]
2026-11-09 20:32 ` [PATCH 18/73] sparse: Gate context failure warning on Wcontext and catch underflow Bart Van Assche
2026-11-09 20:32 ` [PATCH 19/73] ast-model: Return AST_N_COLUMNS and inspect child nodes lazily Bart Van Assche
2026-11-09 20:32 ` [PATCH 20/73] c2xml: Guard against recursive symbols and free xmlGetProp() string Bart Van Assche
2026-11-09 20:32 ` [PATCH 21/73] compile-i386: Fix buffer bounds, unsigned '<=' codegen, and null checks Bart Van Assche
2026-11-09 20:32 ` [PATCH 22/73] ctags: Fix line comparison order and handle SYM_RESTRICT and SYM_LABEL Bart Van Assche
2026-11-09 20:32 ` [PATCH 23/73] dissect: Unwrap array element type via base_type() Bart Van Assche
2026-11-09 20:32 ` [PATCH 24/73] example: Fix show_op() rotating buffer dimensions Bart Van Assche
2026-11-09 20:32 ` [PATCH 25/73] graph: Check PSEUDO_SYM before dereferencing insn->func->sym Bart Van Assche
2026-11-09 20:32 ` [PATCH 26/73] scheck: Fix slice() upper bit index calculation Bart Van Assche
2026-11-09 20:32 ` [PATCH 27/73] sparse-llvm: Support arbitrary struct member counts and fix operand types Bart Van Assche
2026-11-09 20:32 ` [PATCH 28/73] inline: Duplicate generic expressions instead of evaluating them Bart Van Assche
2026-11-09 20:32 ` [PATCH 29/73] lib: Avoid mutating fmt before va_start() in warning() Bart Van Assche
2026-11-09 20:32 ` [PATCH 30/73] cse, canon: Cast pointers to uintptr_t before relational comparison Bart Van Assche
2026-11-09 20:32 ` [PATCH 31/73] context: Add "conditional context increment" model Bart Van Assche
2026-11-09 20:32 ` [PATCH 32/73] expand: Simplify signed constants early Bart Van Assche
2026-11-09 20:32 ` [PATCH 33/73] bitwise: Allow all-bits-set as a restricted value Bart Van Assche
2026-11-09 20:32 ` [PATCH 34/73] bitwise: Allow restricted ordered compares with unrestricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 35/73] bitwise: Keep unrestricted values unrestricted after a cast Bart Van Assche
2026-11-09 20:32 ` [PATCH 36/73] bitwise: Don't remove MOD_SPECIFIER from 'bitwise' types Bart Van Assche
2026-11-09 20:32 ` [PATCH 37/73] bitwise: Allow both regular casts and forced casts for restricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 38/73] clang-format: Add .clang-format configuration file Bart Van Assche
2026-11-09 20:32 ` [PATCH 39/73] options: Add support for -std=c23, -std=c2x and -std=gnu23 Bart Van Assche
2026-11-09 20:32 ` [PATCH 40/73] preprocessor: Add support for #elifdef and #elifndef Bart Van Assche
2026-11-09 20:32 ` [PATCH 41/73] preprocessor: Add support for __has_include and __has_include_next Bart Van Assche
2026-11-09 20:32 ` [PATCH 42/73] c23: Add support for static_assert, alignas, alignof, thread_local Bart Van Assche
2026-11-09 20:32 ` [PATCH 43/73] c23: Add support for bool, true, and false keywords Bart Van Assche
2026-11-09 20:32 ` [PATCH 44/73] c23: Add support for nullptr and nullptr_t Bart Van Assche
2026-11-09 20:32 ` [PATCH 45/73] c23: Add support for digit separators in numeric literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 46/73] c23: Support unnamed parameters in function definitions Bart Van Assche
2026-11-09 20:32 ` [PATCH 47/73] c23: Support variadic functions without named parameters Bart Van Assche
2026-11-09 20:32 ` [PATCH 48/73] c23: Support labels before declarations and at block ends Bart Van Assche
2026-11-09 20:32 ` [PATCH 49/73] c23: Support auto type inference Bart Van Assche
2026-11-09 20:32 ` [PATCH 50/73] c23: Support constexpr object specifier Bart Van Assche
2026-11-09 20:32 ` [PATCH 51/73] c23: Support enumerations with fixed underlying types Bart Van Assche
2026-11-09 20:32 ` [PATCH 52/73] c23: Support standard [[]] attribute syntax Bart Van Assche
2026-11-09 20:32 ` [PATCH 53/73] preprocessor: Add support for __has_c_attribute Bart Van Assche
2026-11-09 20:32 ` [PATCH 54/73] c23: Support empty initializer braces {} Bart Van Assche
2026-11-09 20:32 ` [PATCH 55/73] preprocessor: Add support for __has_embed Bart Van Assche
2026-11-09 20:32 ` [PATCH 56/73] preprocessor: Add support for #embed directive Bart Van Assche
2026-11-09 20:32 ` [PATCH 57/73] c23: Add support for u8 character constants and string literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 58/73] preprocessor: Add support for __VA_OPT__ Bart Van Assche
2026-11-09 20:32 ` [PATCH 59/73] c23: Treat empty parameter list () as (void) and reject K&R functions Bart Van Assche
2026-11-09 20:32 ` [PATCH 60/73] c23: Support storage-class specifiers in compound literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 61/73] c23: Add support for _BitInt(N) Bart Van Assche
2026-11-09 20:32 ` [PATCH 62/73] enum: Support machine mode attributes on enumeration types Bart Van Assche
2026-11-09 20:32 ` [PATCH 63/73] enum: Support __attribute__((packed)) " Bart Van Assche
2026-11-09 20:32 ` [PATCH 64/73] expand: Ignore top-level non-array address space in compare_types() Bart Van Assche
2026-11-09 20:32 ` [PATCH 65/73] builtin: Support __builtin_infer_alloc_token() as a size_t ICE Bart Van Assche
2026-11-09 20:32 ` [PATCH 66/73] builtin: Add support for Linux kernel compiler builtins Bart Van Assche
2026-11-09 20:32 ` [PATCH 67/73] builtin: Add support for __builtin_has_attribute() Bart Van Assche
2026-11-09 20:32 ` [PATCH 68/73] cleanup: Support __attribute__((cleanup(...))) in linearize and -Wcontext Bart Van Assche
2026-11-09 20:32 ` [PATCH 69/73] builtin: Do not evaluate __builtin_clz(0) and __builtin_ctz(0) Bart Van Assche
2026-11-09 20:32 ` [PATCH 70/73] pre-process: Support long token and string expansions dynamically Bart Van Assche
2026-11-09 20:32 ` [PATCH 71/73] x86: Undefine conflicting bitness macros when parsing -m flags Bart Van Assche
2026-11-09 20:32 ` [PATCH 72/73] evaluate: Accept conditional expressions with only one void side Bart Van Assche
2026-11-09 20:32 ` [PATCH 73/73] ci: Add Linux kernel GitHub Actions workflow and build script Bart Van Assche

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261109203259.362845-18-bvanassche@acm.org \
    --to=bvanassche@acm.org \
    --cc=linux-sparse@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox