The following fixes the second bug you'll hit very fast when writing testcases for -ftrapv (maybe not so often in "real" scenarios). There are several places in the optimization pipeline where after constant folding we fail to generate trapping instructions.
The simplest solution as implemented below is to avoid constant-folding trapping instructions. Another solution would be to make sure to fold to (__builtin_trap (), value), but I didn't try that (and it's not what the existing mitigation in folding of negates does). For bit-ccp I decided to say it's not its business to re-invent constant folding (so only handle partially constant values). For CSE and simplify-rtx.c we need to make sure to _not_ record non-trapping REG_EQUAL variants, that is, when expanding a + b with overflow trapping we may not attach a simple (plus:SI reg1 reg2) REG_EQUAL note to the libcall. (similar to signed vs. unsigned ops there doesn't seem to be a way to make this plus "trapping") Bootstrapped and tested on x86_64-unknown-linux-gnu. Note that bootstrap with -ftrapv currently fails (not because of trapping but because ICEing in expr.c:9218 - probably a fallout of my earlier patch ... bah). Ok for trunk? Thanks, Richard. 2014-07-29 Richard Biener <rguent...@suse.de> PR middle-end/61893 * fold-const.c (const_binop): For trapping overflow types return NULL_TREE if the operation overflowed. * tree-ssa-ccp.c (bit_value_unop): Do not do constant folding. (bit_value_binop): Likewise. * optabs.c (emit_libcall_block_1): Allow a NULL_RTX equiv. (expand_binop): For -ftrapv optabs do not record an REG_EQUAL note. (expand_unop): Likewise. Index: gcc/fold-const.c =================================================================== *** gcc/fold-const.c (revision 213119) --- gcc/fold-const.c (working copy) *************** const_binop (enum tree_code code, tree a *** 1122,1128 **** STRIP_NOPS (arg2); if (TREE_CODE (arg1) == INTEGER_CST) ! return int_const_binop (code, arg1, arg2); if (TREE_CODE (arg1) == REAL_CST) { --- 1122,1136 ---- STRIP_NOPS (arg2); if (TREE_CODE (arg1) == INTEGER_CST) ! { ! tree res = int_const_binop (code, arg1, arg2); ! if (res ! && TYPE_OVERFLOW_TRAPS (TREE_TYPE (arg1)) ! && TREE_OVERFLOW (res) ! && !TREE_OVERFLOW (arg1) && !TREE_OVERFLOW (arg2)) ! return NULL_TREE; ! return res; ! } if (TREE_CODE (arg1) == REAL_CST) { Index: gcc/tree-ssa-ccp.c =================================================================== *** gcc/tree-ssa-ccp.c (revision 213119) --- gcc/tree-ssa-ccp.c (working copy) *************** bit_value_unop (enum tree_code code, tre *** 1456,1463 **** widest_int value, mask; prop_value_t val; ! if (rval.lattice_val == UNDEFINED) ! return rval; gcc_assert ((rval.lattice_val == CONSTANT && TREE_CODE (rval.value) == INTEGER_CST) --- 1456,1474 ---- widest_int value, mask; prop_value_t val; ! if (rval.lattice_val == UNDEFINED ! /* If the value is fully known constants assume that ! earlier simplification failed for a reason, for example ! due to -ftrapv. */ ! || (rval.lattice_val == CONSTANT ! && TREE_CODE (rval.value) == INTEGER_CST ! && rval.mask == -1)) ! { ! val.lattice_val = VARYING; ! val.value = NULL_TREE; ! val.mask = -1; ! return val; ! } gcc_assert ((rval.lattice_val == CONSTANT && TREE_CODE (rval.value) == INTEGER_CST) *************** bit_value_binop (enum tree_code code, tr *** 1492,1498 **** prop_value_t val; if (r1val.lattice_val == UNDEFINED ! || r2val.lattice_val == UNDEFINED) { val.lattice_val = VARYING; val.value = NULL_TREE; --- 1503,1518 ---- prop_value_t val; if (r1val.lattice_val == UNDEFINED ! || r2val.lattice_val == UNDEFINED ! /* If both values are fully known constants assume that ! earlier simplification failed for a reason, for example ! due to -ftrapv. */ ! || (r1val.lattice_val == CONSTANT ! && TREE_CODE (r1val.value) == INTEGER_CST ! && r1val.mask == 0 ! && r2val.lattice_val == CONSTANT ! && TREE_CODE (r2val.value) == INTEGER_CST ! && r2val.mask == 0)) { val.lattice_val = VARYING; val.value = NULL_TREE; *************** bit_value_binop (enum tree_code code, tr *** 1506,1511 **** --- 1526,1532 ---- gcc_assert ((r2val.lattice_val == CONSTANT && TREE_CODE (r2val.value) == INTEGER_CST) || r2val.mask == -1); + bit_value_binop_1 (code, type, &value, &mask, TREE_TYPE (rhs1), value_to_wide_int (r1val), r1val.mask, TREE_TYPE (rhs2), value_to_wide_int (r2val), r2val.mask); Index: gcc/optabs.c =================================================================== *** gcc/optabs.c (revision 213119) --- gcc/optabs.c (working copy) *************** expand_binop (enum machine_mode mode, op *** 2174,2184 **** insns = get_insns (); end_sequence (); target = gen_reg_rtx (mode); emit_libcall_block_1 (insns, target, value, ! gen_rtx_fmt_ee (optab_to_code (binoptab), ! mode, op0, op1), ! trapv_binoptab_p (binoptab)); return target; } --- 2174,2185 ---- insns = get_insns (); end_sequence (); + bool trapv = trapv_binoptab_p (binoptab); target = gen_reg_rtx (mode); emit_libcall_block_1 (insns, target, value, ! trapv ? NULL_RTX ! : gen_rtx_fmt_ee (optab_to_code (binoptab), ! mode, op0, op1), trapv); return target; } *************** expand_unop (enum machine_mode mode, opt *** 3297,3309 **** end_sequence (); target = gen_reg_rtx (outmode); ! eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0); ! if (GET_MODE_SIZE (outmode) < GET_MODE_SIZE (mode)) ! eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode); ! else if (GET_MODE_SIZE (outmode) > GET_MODE_SIZE (mode)) ! eq_value = simplify_gen_unary (ZERO_EXTEND, outmode, eq_value, mode); ! emit_libcall_block_1 (insns, target, value, eq_value, ! trapv_unoptab_p (unoptab)); return target; } --- 3298,3316 ---- end_sequence (); target = gen_reg_rtx (outmode); ! bool trapv = trapv_unoptab_p (unoptab); ! if (trapv) ! eq_value = NULL_RTX; ! else ! { ! eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0); ! if (GET_MODE_SIZE (outmode) < GET_MODE_SIZE (mode)) ! eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode); ! else if (GET_MODE_SIZE (outmode) > GET_MODE_SIZE (mode)) ! eq_value = simplify_gen_unary (ZERO_EXTEND, ! outmode, eq_value, mode); ! } ! emit_libcall_block_1 (insns, target, value, eq_value, trapv); return target; } *************** emit_libcall_block_1 (rtx insns, rtx tar *** 3985,3991 **** } last = emit_move_insn (target, result); ! set_dst_reg_note (last, REG_EQUAL, copy_rtx (equiv), target); if (final_dest != target) emit_move_insn (final_dest, target); --- 3992,3999 ---- } last = emit_move_insn (target, result); ! if (equiv) ! set_dst_reg_note (last, REG_EQUAL, copy_rtx (equiv), target); if (final_dest != target) emit_move_insn (final_dest, target); Index: gcc/testsuite/gcc.dg/torture/ftrapv-2.c =================================================================== *** gcc/testsuite/gcc.dg/torture/ftrapv-2.c (revision 0) --- gcc/testsuite/gcc.dg/torture/ftrapv-2.c (working copy) *************** *** 0 **** --- 1,28 ---- + /* { dg-do run } */ + /* { dg-additional-options "-ftrapv" } */ + /* { dg-require-effective-target trapping } */ + /* { dg-require-fork } */ + + #include <stdlib.h> + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> + + /* Verify SImode constant operations properly trap. PR middle-end/61893 */ + + int main(void) + { + pid_t child = fork (); + int status = 0; + if (child == 0) + { + volatile int x = __INT_MAX__ + 1; + exit (0); + } + else if (child == -1) + return 0; + if (wait (&status) == child + && status == 0) + abort (); + return 0; + }