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