Skip to contentSkip to conversation

Multiple & vs. && and | vs. || bugs in 2.4.20

PATCH In review Norbert Kiesel 7 participants 9 messages Mar 2, 2003 – Mar 7, 2003

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Multiple & vs. && and | vs. || bugs in 2.4.20

Hi,

here are patches for some | vs. || and & vs. && bugs found with find ${1:-.} -name \*.c | xargs grep -En \
'![a-zA-Z0-9_ ]+(\|[^|]|\&[^&])|([^|]\||[^&]\&) *!'

I also emailed them to the maintainers/authors if I could find them, but failed for some (e.g. gus_xxx.c).

so long
Norbert

drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
@@ -240,7 +240,7 @@
 	if (urb->status)
 		dbg("nonzero read bulk status received: %d", urb->status);
 
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters,
 			 * we drop them. */
drivers/net/aironet4500_core.c renamed from drivers/net/aironet4500_core.c~ +2 -4
@@ -2676,10 +2676,8 @@
 #endif
 	//awc_dump_registers(dev);
 
-	if (adhoc & !max_mtu)
-		max_mtu= 2250;
-	else if (!max_mtu)
-		max_mtu= 1500;
+	if (!max_mtu)
+		max_mtu= adhoc ? 2250 : 1500;
 			
         priv->sleeping_bap = 1;
         	
drivers/video/aty128fb.c renamed from drivers/video/aty128fb.c~ +1 -1
@@ -2531,7 +2531,7 @@
 	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
 	if (on && level > BACKLIGHT_OFF) {
 		reg |= LVDS_DIGION;
-		if (!reg & LVDS_ON) {
+		if ((reg & LVDS_ON) == 0) {
 			reg &= ~LVDS_BLON;
 			aty_st_le32(LVDS_GEN_CNTL, reg);
 			(void)aty_ld_le32(LVDS_GEN_CNTL);
drivers/sound/gus_midi.c renamed from drivers/sound/gus_midi.c~ +1 -1
@@ -183,7 +183,7 @@
 		qhead++;
 	}
 	restore_flags(flags);
-	return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
+	return (qlen > 0) || !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
 }
 
 #define MIDI_SYNTH_NAME	"Gravis Ultrasound Midi"
drivers/sound/gus_wave.c renamed from drivers/sound/gus_wave.c~ +1 -1
@@ -3123,7 +3123,7 @@
 
 	gus_initialize();
 	
-	if ((gus_mem_size > 0) & !gus_no_wave_dma)
+	if ((gus_mem_size > 0) && !gus_no_wave_dma)
 	{
 		hw_config->slots[4] = -1;
 		if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
drivers/i2c/i2c-proc.c renamed from drivers/i2c/i2c-proc.c~ +3 -3
@@ -729,7 +729,7 @@
 			     ||
 			     ((address_data->
 			       ignore_range[i] ==
-			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			       SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr >= address_data->ignore_range[i + 1])
 			    && (addr <= address_data->ignore_range[i + 2])) {
 #ifdef DEBUG
@@ -818,7 +818,7 @@
 		     i += 2) {
 			if (((adapter_id == address_data->probe[i]) ||
 			     ((address_data->
-			       probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa))
+			       probe[i] == SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr == address_data->probe[i + 1])) {
 #ifdef DEBUG
 				printk
@@ -835,7 +835,7 @@
 			    ((adapter_id == address_data->probe_range[i])
 			     ||
 			     ((address_data->probe_range[i] ==
-			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			       SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr >= address_data->probe_range[i + 1])
 			    && (addr <= address_data->probe_range[i + 2])) {
 				found = 1;
drivers/sound/maestro.c renamed from drivers/sound/maestro.c~ +1 -1
@@ -3359,7 +3359,7 @@
 	/* check to see if we have a capabilities list in
 		the config register */
 	pci_read_config_word(pcidev, PCI_STATUS, &w);
-	if(! w & PCI_STATUS_CAP_LIST) return 0;
+	if(!(w & PCI_STATUS_CAP_LIST)) return 0;
 
 	/* walk the list, starting at the head. */
 	pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next);
drivers/video/radeonfb.c renamed from drivers/video/radeonfb.c~ +1 -1
@@ -2778,7 +2778,7 @@
 	lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
 	if (on && (level > BACKLIGHT_OFF)) {
 		lvds_gen_cntl |= LVDS_DIGON;
-		if (!lvds_gen_cntl & LVDS_ON) {
+		if ((lvds_gen_cntl & LVDS_ON) == 0) {
 			lvds_gen_cntl &= ~LVDS_BLON;
 			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
 			(void)INREG(LVDS_GEN_CNTL);
#1 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

drivers/usb/acm.c line 246

Quoted 11 lines · Norbert Kiesel
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
@@ -240,7 +240,7 @@
 	if (urb->status)
 		dbg("nonzero read bulk status received: %d", urb->status);
 
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters,
 			 * we drop them. */

Have you really looked at detail at all these cases? The problem is that you have made the code less efficient on some platforms. The use of && requires shortcut evaluation. I.e., the compiler is forced to not evaluate !acm->throttle if !urb->status is true. The causes, unless the architecture has condition bits, a conditional jump.

The original code didn't need and normally has no jump and thi specific case was certainly fine before since the result of the ! operator is either 0 or 1 and therefore the & operator has no strange side effects.

As an example, here is how the code for x86 could have looked before

   movl   status(%edx), %edx
   testl  %edx, %edx
   movl   throttle(%eax), %eax
   sete   %dl
   testl  %eax, %eax
   sete   %al
   andb   %dl, %al
   jne    ...

[if code]

After the change the code must look something like this:

   movl   status(%edx), %edx
   testl  %edx, %edx
   jne    ...
   movl   throttle(%eax), %eax
   testl  %eax, %eax
   jne    ...

[if code]

Observe the extra 'jne' and the fact that the value of 'throttle'
element cannot be loaded until after the conditional jump. Not even
out of order execution can arrange that.

To summarize, I'd probably not be amused if you would change any of my code which takes advantage of such programming finess. I would probably have added appropriate comments to explain the code but nevertheless, replacing the more efficient code with some which is easier to understand should probably be considered on a case by case basis. Incorrect branch prediction is costly.

- --
- --------------. ,-. 444 Castro Street
Ulrich Drepper \ ,-----------------' \ Mountain View, CA 94041 USA
Red Hat --' drepper at redhat.com ---------------------------
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE+Ykf82ijCOnn/RHQRApJaAKCxM4hwu12mJVbQuD3o+t13YVxrsACgsnVH RZmgjNB5KP3Qu27iqpf5aiU=
=l7xl
-----END PGP SIGNATURE-----

#2 lore raw

Hi,

Quoted 8 lines · Ulrich Drepper

drivers/usb/acm.c line 243

Quoted 2 lines
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {

[...]

Observe the extra 'jne' and the fact that the value of 'throttle'
element cannot be loaded until after the conditional jump. Not even
out of order execution can arrange that.

But speculative execution can arrange that (providing the cpu predicted the jump target correctly).

Quoted 2 lines

To summarize, I'd probably not be amused if you would change any of my code which takes advantage of such programming finess.

If that was really intentional, I would assume to see something like this:

	if (!(urb->status | acm->throttle)) {

bye, Roman

#3 lore raw
In reply to Ulrich Drepper
Quoted 2 lines · Ulrich Drepper
Quoted 2 lines
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {

[...]

Quoted 3 lines

Have you really looked at detail at all these cases? The problem is that you have made the code less efficient on some platforms. The use of && requires shortcut evaluation. I.e., the compiler is forced to not

While I agree with your observation in general, this is actually something the compiler should be able to figure out by itself:

- there's only a side-effect if acm is NULL
- in ACM_READY, we've already tested acm for NULL, and subsequently de-referenced it
- acm is a local variable, and not aliased, so the dbg() can't change it

So, given the negations, || and | are equivalent in this case, and whether a jump, conditional execution, a bit operation, or something else yields better code is compiler, machine, and context specific.

- Werner

Signature · 3 lines
  _________________________________________________________________________
 / Werner Almesberger, Buenos Aires, Argentina         [email protected] /
/_http://www.almesberger.net/____________________________________________/
#4 lore raw
Quoted 4 lines · Werner Almesberger

While I agree with your observation in general, this is actually something the compiler should be able to figure out by itself:

- there's only a side-effect if acm is NULL

In general there's also a side effect if acm is uninitialized. I didn't look at the code in question here.

As for the rest, it's true that we should be able to do this, but we don't currently have a pass that globally propagates "trapiness" of memory references. It would be a useful thing to have though, particularly for Java, which is required to arrange for these traps to be able to be caught with exceptions, and other horrible reordering issues.

r~

#5 lore raw
In reply to Ulrich Drepper

Hi,
yes I agree that the code will be slightly less efficient in this case (though looking a bit around, there are way more places which could be optimized, e.g. the useless "&& !acm->throttle" in the next line). What's IMHO more important is that the original code was producing the correct result, so the patch for acm.c is not really necessary. This is also true for the patches for gus_midi.c, gus-wave.c, and i2c-proc.c.

OTOH, I still think that in most (all?) of this cases the bit-op was not intended.

Anyway, I'll wait another day or two to collect replies from the maintainers and then resend the remaining patches where the code really produces wrong results.

so long
Norbert

Quoted 73 lines · Ulrich Drepper

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

drivers/usb/acm.c line 246

Quoted 11 lines · Norbert Kiesel
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
@@ -240,7 +240,7 @@
 	if (urb->status)
 		dbg("nonzero read bulk status received: %d", urb->status);
 
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters,
 			 * we drop them. */

Have you really looked at detail at all these cases? The problem is that you have made the code less efficient on some platforms. The use of && requires shortcut evaluation. I.e., the compiler is forced to not evaluate !acm->throttle if !urb->status is true. The causes, unless the architecture has condition bits, a conditional jump.

The original code didn't need and normally has no jump and thi specific case was certainly fine before since the result of the ! operator is either 0 or 1 and therefore the & operator has no strange side effects.

As an example, here is how the code for x86 could have looked before

   movl   status(%edx), %edx
   testl  %edx, %edx
   movl   throttle(%eax), %eax
   sete   %dl
   testl  %eax, %eax
   sete   %al
   andb   %dl, %al
   jne    ...

[if code]

After the change the code must look something like this:

   movl   status(%edx), %edx
   testl  %edx, %edx
   jne    ...
   movl   throttle(%eax), %eax
   testl  %eax, %eax
   jne    ...

[if code]

Observe the extra 'jne' and the fact that the value of 'throttle'
element cannot be loaded until after the conditional jump. Not even
out of order execution can arrange that.

To summarize, I'd probably not be amused if you would change any of my code which takes advantage of such programming finess. I would probably have added appropriate comments to explain the code but nevertheless, replacing the more efficient code with some which is easier to understand should probably be considered on a case by case basis. Incorrect branch prediction is costly.

- --
- --------------. ,-. 444 Castro Street
Ulrich Drepper \ ,-----------------' \ Mountain View, CA 94041 USA
Red Hat --' drepper at redhat.com ---------------------------
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE+Ykf82ijCOnn/RHQRApJaAKCxM4hwu12mJVbQuD3o+t13YVxrsACgsnVH RZmgjNB5KP3Qu27iqpf5aiU=
=l7xl
-----END PGP SIGNATURE-----

Signature · 1 line
Norbert Kiesel <[email protected]>
#6 lore raw
Quoted 3 lines · Norbert Kiesel

What's IMHO more important is that the original code was producing the correct result, so the patch for acm.c is not really necessary. This is also true for the patches for gus_midi.c, gus-wave.c, and i2c-proc.c.

Even the patch isn't *necessary* (i.e. does not change behaviour) it's still a good idea.

I've been working on a -Wboolean-bitops. It seems to work at least a bit, but I don't really know anything about gcc so it's probably brain-damaged. Alas, current gcc CVS seems to have real issues with the kernel ATM (as in, segfaults on scripts/empty.c immediately).

regards
john

#7 lore raw
In reply to Ulrich Drepper

Hi!

Quoted 10 lines

drivers/usb/acm.c line 244

Quoted 4 lines
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {

To summarize, I'd probably not be amused if you would change any of my code which takes advantage of such programming finess. I would probably have added appropriate comments to explain the code but nevertheless, replacing the more efficient code with some which is easier to understand should probably be considered on a case by case basis.

Actually I feel co-responsible for acm.c, and this *is* typo. acm is for modems, thats *not* performance critical, and certainly not worth code obfuscation.

Signature · 2 lines
				Pavel
Written on sharp zaurus, because my Velo1 broke. If you have Velo you don't need...
#8 lore raw

Hi,

Alan included my 2.4.20 patches - including the one for acm.c - in 2.4.21-pre5-ac1, so I expect them to show up in mainline someday. Still working on delivering some of the 2.5.x ones...

--nk

Quoted 17 lines · Pavel Machek

Hi!

Quoted 10 lines

drivers/usb/acm.c line 244

Quoted 4 lines
drivers/usb/acm.c renamed from drivers/usb/acm.c~ +1 -1
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {

To summarize, I'd probably not be amused if you would change any of my code which takes advantage of such programming finess. I would probably have added appropriate comments to explain the code but nevertheless, replacing the more efficient code with some which is easier to understand should probably be considered on a case by case basis.

Actually I feel co-responsible for acm.c, and this *is* typo. acm is for modems, thats *not* performance critical, and certainly not worth code obfuscation.

Signature · 2 lines
Norbert Kiesel <[email protected]>
TBD Networks