On 03/03/2014 01:03 PM, Florian Weimer wrote:
I have code like this:
if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) {
preferred = 1;
} else {
preferred = current;
/* PINs are even numbered, PUKs are odd */
if (!(preferred & 1))
preferred++;
if (preferred >= 126)
return SC_ERROR_TOO_MANY_OBJECTS;
}
if (current > preferred || preferred > CARDOS_PIN_ID_MAX)
return SC_ERROR_TOO_MANY_OBJECTS;
On x86_64, the condition in the final if statement is gimplified as:
- D.8003 = current > preferred;
- D.8004 = preferred > 15;
- D.8005 = D.8003 | D.8004;
- if (D.8005 != 0) goto <D.8006>; else goto <D.8007>;
On rs6000 (-mtune=power7 -mcpu=power7), I get this instead:
+ if (current > preferred) goto <D.8376>; else goto <D.8378>;
+ <D.8378>:
+ if (preferred > 15) goto <D.8376>; else goto <D.8377>;
Here's a self-contained reproducer:
int g(void);
int
f(int flags, int current)
{
int preferred;
if (flags & 0x80) {
preferred = 1;
} else {
preferred = current;
if (!(preferred & 1))
preferred++;
if (preferred >= 126)
return 55;
}
if (current > preferred || preferred > 17)
return 55;
return g();
}
Obviously, this affects optimization-dependent warnings later in the
compilation.
I wonder why this happens. Shouldn't gimplification be roughly
target-independent?
It turns out that it's not gimplification, but fold which is the
culprit. In fold-const.c:fold_truth_andor, we have this code:
if (LOGICAL_OP_NON_SHORT_CIRCUIT
&& (code == TRUTH_AND_EXPR
|| code == TRUTH_ANDIF_EXPR
|| code == TRUTH_OR_EXPR
|| code == TRUTH_ORIF_EXPR))
{
…
/* Transform (A AND-IF B) into (A AND B), or (A OR-IF B)
into (A OR B).
For sequence point consistancy, we need to check for trapping,
and side-effects. */
else if (code == icode && simple_operand_p_2 (arg0)
&& simple_operand_p_2 (arg1))
return fold_build2_loc (loc, ncode, type, arg0, arg1);
rs6000 defines LOGICAL_OP_NON_SHORT_CIRCUIT as 0, so this code never
runs, and the gimplifier produces two separate if statements.
--
Florian Weimer / Red Hat Product Security Team