Masked bitfield comparisons may yield incorrect results

Charles M. Hannum root@ihack.net
Wed Mar 31 23:54:00 GMT 1999


It turns out there's at least one more bug in fold_truthop().  It's
possible for the combined ll_mask and lr_mask to be identical, but for
the bitfields being compared to be shifted in different positions.
Tests g4-g7 and h4-h7 in the expanded test program below reproduce
this condition.  The correct output for these cases is:

g4: 0
g5: 0
g6: 1
g7: 1
h4: 0
h5: 0
h6: 1
h7: 1

The incorrect output on sparc--netbsd1.3I, with my previous patch
applied, looks like:

g4: 0
g5: 1
g6: 1
g7: 0
h4: 1
h5: 0
h6: 0
h7: 1

(On a little endian machine, the result of each test is inverted.)

A patch to fix this problem is attached.  This patch also generates
more efficient code in some of the other cases, by eliminating shifts.

----- truth-test.c ---------snip-----8<-----snip-----8<-----snip-----8<-----
#include <stdio.h>

struct a {
	char a, b;
	short c;
};

int
a1()
{
	static struct a x = { 1, 2, ~1 }, y = { 65, 2, ~2 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
a2()
{
	static struct a x = { 1, 66, ~1 }, y = { 1, 2, ~2 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
a3()
{
	static struct a x = { 9, 66, ~1 }, y = { 33, 18, ~2 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct b {
	int c;
	short b, a;
};

int
b1()
{
	static struct b x = { ~1, 2, 1 }, y = { ~2, 2, 65 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
b2()
{
	static struct b x = { ~1, 66, 1 }, y = { ~2, 2, 1 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
b3()
{
	static struct b x = { ~1, 66, 9 }, y = { ~2, 18, 33 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct c {
	unsigned int c:4, b:14, a:14;
};

int
c1()
{
	static struct c x = { ~1, 2, 1 }, y = { ~2, 2, 65 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
c2()
{
	static struct c x = { ~1, 66, 1 }, y = { ~2, 2, 1 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
c3()
{
	static struct c x = { ~1, 66, 9 }, y = { ~2, 18, 33 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct d {
	unsigned int a:14, b:14, c:4;
};

int
d1()
{
	static struct d x = { 1, 2, ~1 }, y = { 65, 2, ~2 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
d2()
{
	static struct d x = { 1, 66, ~1 }, y = { 1, 2, ~2 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
d3()
{
	static struct d x = { 9, 66, ~1 }, y = { 33, 18, ~2 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct e {
	int c:4, b:14, a:14;
};

int
e1()
{
	static struct e x = { ~1, -2, -65 }, y = { ~2, -2, -1 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
e2()
{
	static struct e x = { ~1, -2, -1 }, y = { ~2, -66, -1 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
e3()
{
	static struct e x = { ~1, -18, -33 }, y = { ~2, -66, -9 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct f {
	int a:14, b:14, c:4;
};

int
f1()
{
	static struct f x = { -65, -2, ~1 }, y = { -1, -2, ~2 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
f2()
{
	static struct f x = { -1, -2, ~1 }, y = { -1, -66, ~2 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
f3()
{
	static struct f x = { -33, -18, ~1 }, y = { -9, -66, ~2 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

struct gx {
	int c:4, b:14, a:14;
};
struct gy {
	int b:14, a:14, c:4;
};

int
g1()
{
	static struct gx x = { ~1, -2, -65 };
	static struct gy y = { -2, -1, ~2 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
g2()
{
	static struct gx x = { ~1, -2, -1 };
	static struct gy y = { -66, -1, ~2 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
g3()
{
	static struct gx x = { ~1, -18, -33 };
	static struct gy y = { -66, -9, ~2 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

int
g4()
{
	static struct gx x = { ~1, 0x0020, 0x0010 };
	static struct gy y = { 0x0200, 0x0100, ~2 };

	return ((x.a & 0x00f0) == (y.a & 0x0f00) &&
		(x.b & 0x00f0) == (y.b & 0x0f00));
}

int
g5()
{
	static struct gx x = { ~1, 0x0200, 0x0100 };
	static struct gy y = { 0x0020, 0x0010, ~2 };

	return ((x.a & 0x0f00) == (y.a & 0x00f0) &&
		(x.b & 0x0f00) == (y.b & 0x00f0));
}

int
g6()
{
	static struct gx x = { ~1, 0xfe20, 0xfd10 };
	static struct gy y = { 0xc22f, 0xc11f, ~2 };

	return ((x.a & 0x03ff) == (y.a & 0x3ff0) &&
		(x.b & 0x03ff) == (y.b & 0x3ff0));
}

int
g7()
{
	static struct gx x = { ~1, 0xc22f, 0xc11f };
	static struct gy y = { 0xfe20, 0xfd10, ~2 };

	return ((x.a & 0x3ff0) == (y.a & 0x03ff) &&
		(x.b & 0x3ff0) == (y.b & 0x03ff));
}

struct hx {
	int a:14, b:14, c:4;
};
struct hy {
	int c:4, a:14, b:14;
};

int
h1()
{
	static struct hx x = { -65, -2, ~1 };
	static struct hy y = { ~2, -1, -2 };

	return (x.a == (y.a & ~64) && x.b == y.b);
}

int
h2()
{
	static struct hx x = { -1, -2, ~1 };
	static struct hy y = { ~2, -1, -66 };

	return (x.a == y.a && (x.b & ~64) == y.b);
}

int
h3()
{
	static struct hx x = { -33, -18, ~1 };
	static struct hy y = { ~2, -9, -66 };

	return ((x.a & ~8) == (y.a & ~32) && (x.b & ~64) == (y.b & ~16));
}

int
h4()
{
	static struct hx x = { 0x0010, 0x0020, ~1 };
	static struct hy y = { ~2, 0x0100, 0x0200 };

	return ((x.a & 0x00f0) == (y.a & 0x0f00) &&
		(x.b & 0x00f0) == (y.b & 0x0f00));
}

int
h5()
{
	static struct hx x = { 0x0100, 0x0200, ~1 };
	static struct hy y = { ~2, 0x0010, 0x0020 };

	return ((x.a & 0x0f00) == (y.a & 0x00f0) &&
		(x.b & 0x0f00) == (y.b & 0x00f0));
}

int
h6()
{
	static struct hx x = { 0xfd10, 0xfe20, ~1 };
	static struct hy y = { ~2, 0xc11f, 0xc22f };

	return ((x.a & 0x03ff) == (y.a & 0x3ff0) &&
		(x.b & 0x03ff) == (y.b & 0x3ff0));
}

int
h7()
{
	static struct hx x = { 0xc11f, 0xc22f, ~1 };
	static struct hy y = { ~2, 0xfd10, 0xfe20 };

	return ((x.a & 0x3ff0) == (y.a & 0x03ff) &&
		(x.b & 0x3ff0) == (y.b & 0x03ff));
}

int
main()
{

	printf("a1: %d\n", a1());
	printf("a2: %d\n", a2());
	printf("a3: %d\n", a3());
	printf("b1: %d\n", b1());
	printf("b2: %d\n", b2());
	printf("b3: %d\n", b3());
	printf("c1: %d\n", c1());
	printf("c2: %d\n", c2());
	printf("c3: %d\n", c3());
	printf("d1: %d\n", d1());
	printf("d2: %d\n", d2());
	printf("d3: %d\n", d3());
	printf("e1: %d\n", e1());
	printf("e2: %d\n", e2());
	printf("e3: %d\n", e3());
	printf("f1: %d\n", f1());
	printf("f2: %d\n", f2());
	printf("f3: %d\n", f3());
	printf("g1: %d\n", g1());
	printf("g2: %d\n", g2());
	printf("g3: %d\n", g3());
	printf("g4: %d\n", g4());
	printf("g5: %d\n", g5());
	printf("g6: %d\n", g6());
	printf("g7: %d\n", g7());
	printf("h1: %d\n", h1());
	printf("h2: %d\n", h2());
	printf("h3: %d\n", h3());
	printf("h4: %d\n", h4());
	printf("h5: %d\n", h5());
	printf("h6: %d\n", h6());
	printf("h7: %d\n", h7());
}
-----8<-----snip-----8<-----snip-----8<-----snip-----8<-----snip-----8<-----

----- truth-patch-2 --------snip-----8<-----snip-----8<-----snip-----8<-----
Index: fold-const.c
===================================================================
RCS file: /cvsroot/src/gnu/dist/gcc/fold-const.c,v
retrieving revision 1.2
diff -c -2 -r1.2 fold-const.c
*** fold-const.c	1999/03/04 05:38:06	1.2
--- fold-const.c	1999/03/04 09:44:26
***************
*** 3645,3664 ****
  
        /* Make a mask that corresponds to both fields being compared.
! 	 Do this for both items being compared.  If the masks agree,
! 	 we can do this by masking both and comparing the masked
! 	 results.  */
        ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
        lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask, 0);
!       if (operand_equal_p (ll_mask, lr_mask, 0) && lnbitsize == rnbitsize)
  	{
  	  lhs = make_bit_field_ref (ll_inner, type, lnbitsize, lnbitpos,
  				    ll_unsignedp || rl_unsignedp);
  	  rhs = make_bit_field_ref (lr_inner, type, rnbitsize, rnbitpos,
  				    lr_unsignedp || rr_unsignedp);
! 	  if (! all_ones_mask_p (ll_mask, lnbitsize))
! 	    {
! 	      lhs = build (BIT_AND_EXPR, type, lhs, ll_mask);
! 	      rhs = build (BIT_AND_EXPR, type, rhs, ll_mask);
! 	    }
  	  return build (wanted_code, truth_type, lhs, rhs);
  	}
--- 3645,3667 ----
  
        /* Make a mask that corresponds to both fields being compared.
! 	 Do this for both items being compared.  */
        ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
        lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask, 0);
! 
!       /* If the operand size is the same, and the bits being compared are
! 	 in the same position in both operands, we can use the mask to do
! 	 the bitfield extraction, rather than shifting.  */
!       if (lnbitsize == rnbitsize && xll_bitpos == xlr_bitpos)
  	{
  	  lhs = make_bit_field_ref (ll_inner, type, lnbitsize, lnbitpos,
  				    ll_unsignedp || rl_unsignedp);
+ 	  if (! all_ones_mask_p (ll_mask, lnbitsize))
+ 	    lhs = build (BIT_AND_EXPR, type, lhs, ll_mask);
+ 
  	  rhs = make_bit_field_ref (lr_inner, type, rnbitsize, rnbitpos,
  				    lr_unsignedp || rr_unsignedp);
! 	  if (! all_ones_mask_p (lr_mask, rnbitsize))
! 	    rhs = build (BIT_AND_EXPR, type, rhs, lr_mask);
! 
  	  return build (wanted_code, truth_type, lhs, rhs);
  	}
-----8<-----snip-----8<-----snip-----8<-----snip-----8<-----snip-----8<-----




More information about the Gcc-bugs mailing list