static void insert_trap_and_remove_trailing_statements (gimple_stmt_iterator *si_p, tree op) { /* We want the NULL pointer dereference to actually occur so that code that wishes to catch the signal can do so. If the dereference is a load, then there's nothing to do as the LHS will be a throw-away SSA_NAME and the RHS is the NULL dereference. If the dereference is a store and we can easily transform the RHS, then simplify the RHS to enable more DCE. Note that we require the statement to be a GIMPLE_ASSIGN which filters out calls on the RHS. */ gimple stmt = gsi_stmt (*si_p); if (walk_stmt_load_store_ops (stmt, (void *)op, NULL, check_loadstore) && is_gimple_assign (stmt) && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (stmt)))) { /* We just need to turn the RHS into zero converted to the proper type. */ tree type = TREE_TYPE (gimple_assign_lhs (stmt)); gimple_assign_set_rhs_code (stmt, INTEGER_CST); gimple_assign_set_rhs1 (stmt, fold_convert (type, integer_zero_node)); update_stmt (stmt); } gimple new_stmt = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); gimple_seq seq = NULL; gimple_seq_add_stmt (&seq, new_stmt); /* If we had a NULL pointer dereference, then we want to insert the __builtin_trap after the statement, for the other cases we want to insert before the statement. */ if (walk_stmt_load_store_ops (stmt, (void *)op, check_loadstore, check_loadstore)) gsi_insert_after (si_p, seq, GSI_NEW_STMT); else gsi_insert_before (si_p, seq, GSI_NEW_STMT); /* We must remove statements from the end of the block so that we never reference a released SSA_NAME. */ basic_block bb = gimple_bb (gsi_stmt (*si_p)); for (gimple_stmt_iterator si = gsi_last_bb (bb); gsi_stmt (si) != gsi_stmt (*si_p); si = gsi_last_bb (bb)) { stmt = gsi_stmt (si); unlink_stmt_vdef (stmt); gsi_remove (&si, true); release_defs (stmt); } }
/* Look into pointer pointed to by GSIP and figure out what interesting side effects it has. */ static void check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa) { gimple stmt = gsi_stmt (*gsip); if (is_gimple_debug (stmt)) return; if (dump_file) { fprintf (dump_file, " scanning: "); print_gimple_stmt (dump_file, stmt, 0, 0); } if (gimple_has_volatile_ops (stmt) && !gimple_clobber_p (stmt)) { local->pure_const_state = IPA_NEITHER; if (dump_file) fprintf (dump_file, " Volatile stmt is not const/pure\n"); } /* Look for loads and stores. */ walk_stmt_load_store_ops (stmt, local, ipa ? check_ipa_load : check_load, ipa ? check_ipa_store : check_store); if (gimple_code (stmt) != GIMPLE_CALL && stmt_could_throw_p (stmt)) { if (cfun->can_throw_non_call_exceptions) { if (dump_file) fprintf (dump_file, " can throw; looping\n"); local->looping = true; } if (stmt_can_throw_external (stmt)) { if (dump_file) fprintf (dump_file, " can throw externally\n"); local->can_throw = true; } else if (dump_file) fprintf (dump_file, " can throw\n"); } switch (gimple_code (stmt)) { case GIMPLE_CALL: check_call (local, stmt, ipa); break; case GIMPLE_LABEL: if (DECL_NONLOCAL (gimple_label_label (stmt))) /* Target of long jump. */ { if (dump_file) fprintf (dump_file, " nonlocal label is not const/pure\n"); local->pure_const_state = IPA_NEITHER; } break; case GIMPLE_ASM: if (gimple_asm_clobbers_memory_p (stmt)) { if (dump_file) fprintf (dump_file, " memory asm clobber is not const/pure\n"); /* Abandon all hope, ye who enter here. */ local->pure_const_state = IPA_NEITHER; } if (gimple_asm_volatile_p (stmt)) { if (dump_file) fprintf (dump_file, " volatile is not const/pure\n"); /* Abandon all hope, ye who enter here. */ local->pure_const_state = IPA_NEITHER; local->looping = true; } return; default: break; } }