diff options
author | Ilya Enkovich <ilya.enkovich@intel.com> | 2014-11-05 12:42:03 +0000 |
---|---|---|
committer | Ilya Enkovich <ienkovich@gcc.gnu.org> | 2014-11-05 12:42:03 +0000 |
commit | d5e254e19c59fcc49265dda64007690af08b6e28 (patch) | |
tree | df7b4d3afe0e30fdad56e3feeb2db22600eaaec8 /gcc/tree-chkp-opt.c | |
parent | 433e4164339f18d0b8798968444a56b681b5232c (diff) | |
download | gcc-d5e254e19c59fcc49265dda64007690af08b6e28.zip gcc-d5e254e19c59fcc49265dda64007690af08b6e28.tar.gz gcc-d5e254e19c59fcc49265dda64007690af08b6e28.tar.bz2 |
ipa-chkp.c: New.
gcc/
2014-11-05 Ilya Enkovich <ilya.enkovich@intel.com>
* ipa-chkp.c: New.
* ipa-chkp.h: New.
* tree-chkp.c: New.
* tree-chkp.h: New.
* tree-chkp-opt.c: New.
* rtl-chkp.c: New.
* rtl-chkp.h: New.
* Makefile.in (OBJS): Add ipa-chkp.o, rtl-chkp.o, tree-chkp.o
tree-chkp-opt.o.
(GTFILES): Add tree-chkp.c.
* mode-classes.def (MODE_POINTER_BOUNDS): New.
* tree.def (POINTER_BOUNDS_TYPE): New.
* genmodes.c (complete_mode): Support MODE_POINTER_BOUNDS.
(POINTER_BOUNDS_MODE): New.
(make_pointer_bounds_mode): New.
* machmode.h (POINTER_BOUNDS_MODE_P): New.
* stor-layout.c (int_mode_for_mode): Support MODE_POINTER_BOUNDS.
(layout_type): Support POINTER_BOUNDS_TYPE.
* tree-pretty-print.c (dump_generic_node): Support POINTER_BOUNDS_TYPE.
* tree-core.h (tree_index): Add TI_POINTER_BOUNDS_TYPE.
* tree.c (build_int_cst_wide): Support POINTER_BOUNDS_TYPE.
(type_contains_placeholder_1): Likewise.
(build_common_tree_nodes): Initialize
pointer_bounds_type_node.
* tree.h (POINTER_BOUNDS_TYPE_P): New.
(pointer_bounds_type_node): New.
(POINTER_BOUNDS_P): New.
(BOUNDED_TYPE_P): New.
(BOUNDED_P): New.
(CALL_WITH_BOUNDS_P): New.
* gimple.h (gf_mask): Add GF_CALL_WITH_BOUNDS.
(gimple_call_with_bounds_p): New.
(gimple_call_set_with_bounds): New.
(gimple_return_retbnd): New.
(gimple_return_set_retbnd): New
* gimple.c (gimple_build_return): Increase number of ops
for return statement.
(gimple_build_call_from_tree): Propagate CALL_WITH_BOUNDS_P
flag.
* gimple-pretty-print.c (dump_gimple_return): Print second op.
* rtl.h (CALL_EXPR_WITH_BOUNDS_P): New.
* gimplify.c (gimplify_init_constructor): Avoid infinite
loop during gimplification of bounds initializer.
* calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h.
(special_function_p): Use original decl name when analyzing
instrumentation clone.
(arg_data): Add fields special_slot, pointer_arg and
pointer_offset.
(store_bounds): New.
(emit_call_1): Propagate instrumentation flag for CALL.
(initialize_argument_information): Compute pointer_arg,
pointer_offset and special_slot for pointer bounds arguments.
(finalize_must_preallocate): Preallocate when storing bounds
in bounds table.
(compute_argument_addresses): Skip pointer bounds.
(expand_call): Store bounds into tables separately. Return
result joined with resulting bounds.
* cfgexpand.c: Include tree-chkp.h, rtl-chkp.h.
(expand_call_stmt): Propagate bounds flag for CALL_EXPR.
(expand_return): Add returned bounds arg. Handle returned bounds.
(expand_gimple_stmt_1): Adjust to new expand_return signature.
(gimple_expand_cfg): Reset rtx bounds map.
* expr.c: Include tree-chkp.h, rtl-chkp.h.
(expand_assignment): Handle returned bounds.
(store_expr_with_bounds): New. Replaces store_expr with new bounds
target argument. Handle bounds returned by calls.
(store_expr): Now wraps store_expr_with_bounds.
* expr.h (store_expr_with_bounds): New.
* function.c: Include tree-chkp.h, rtl-chkp.h.
(bounds_parm_data): New.
(use_register_for_decl): Do not registerize decls used for bounds
stores and loads.
(assign_parms_augmented_arg_list): Add bounds of the result
structure pointer as the second argument.
(assign_parm_find_entry_rtl): Mark bounds are never passed on
the stack.
(assign_parm_is_stack_parm): Likewise.
(assign_parm_load_bounds): New.
(assign_bounds): New.
(assign_parms): Load bounds and determine a location for
returned bounds.
(diddle_return_value_1): New.
(diddle_return_value): Handle returned bounds.
* function.h (rtl_data): Add field for returned bounds.
* varasm.c: Include tree-chkp.h.
(output_constant): Support POINTER_BOUNDS_TYPE.
(output_constant_pool_2): Support MODE_POINTER_BOUNDS.
(ultimate_transparent_alias_target): Move up.
(make_decl_rtl): For instrumented function use
name of the original decl.
(assemble_start_function): Mark function as global
in case it is instrumentation clone of the global
function.
(do_assemble_alias): Follow transparent alias chain
for identifier. Check if original alias is public.
(maybe_assemble_visibility): Use visibility of the
original function for instrumented version.
(default_unique_section): Likewise.
* emit-rtl.c (immed_double_const): Support MODE_POINTER_BOUNDS.
(init_emit_once): Build pointer bounds zero constants.
* explow.c (trunc_int_for_mode): Support MODE_POINTER_BOUNDS.
* target.def (builtin_chkp_function): New.
(chkp_bound_type): New.
(chkp_bound_mode): New.
(chkp_make_bounds_constant): New.
(chkp_initialize_bounds): New.
(load_bounds_for_arg): New.
(store_bounds_for_arg): New.
(load_returned_bounds): New.
(store_returned_bounds): New.
(chkp_function_value_bounds): New.
(setup_incoming_vararg_bounds): New.
(function_arg): Update hook description with new possible return
value CONST_INT.
* targhooks.h (default_load_bounds_for_arg): New.
(default_store_bounds_for_arg): New.
(default_load_returned_bounds): New.
(default_store_returned_bounds): New.
(default_chkp_bound_type): New.
(default_chkp_bound_mode): New.
(default_builtin_chkp_function): New.
(default_chkp_function_value_bounds): New.
(default_chkp_make_bounds_constant): New.
(default_chkp_initialize_bounds): New.
(default_setup_incoming_vararg_bounds): New.
* targhooks.c (default_load_bounds_for_arg): New.
(default_store_bounds_for_arg): New.
(default_load_returned_bounds): New.
(default_store_returned_bounds): New.
(default_chkp_bound_type): New.
(default_chkp_bound_mode); New.
(default_builtin_chkp_function): New.
(default_chkp_function_value_bounds): New.
(default_chkp_make_bounds_constant): New.
(default_chkp_initialize_bounds): New.
(default_setup_incoming_vararg_bounds): New.
* builtin-types.def (BT_BND): New.
(BT_FN_PTR_CONST_PTR): New.
(BT_FN_CONST_PTR_CONST_PTR): New.
(BT_FN_BND_CONST_PTR): New.
(BT_FN_CONST_PTR_BND): New.
(BT_FN_PTR_CONST_PTR_SIZE): New.
(BT_FN_PTR_CONST_PTR_CONST_PTR): New.
(BT_FN_VOID_PTRPTR_CONST_PTR): New.
(BT_FN_VOID_CONST_PTR_SIZE): New.
(BT_FN_VOID_PTR_BND): New.
(BT_FN_CONST_PTR_CONST_PTR_CONST_PTR): New.
(BT_FN_BND_CONST_PTR_SIZE): New.
(BT_FN_PTR_CONST_PTR_CONST_PTR_SIZE): New.
(BT_FN_VOID_CONST_PTR_BND_CONST_PTR): New.
* chkp-builtins.def: New.
* builtins.def: include chkp-builtins.def.
(DEF_CHKP_BUILTIN): New.
* builtins.c: Include tree-chkp.h and rtl-chkp.h.
(expand_builtin): Support BUILT_IN_CHKP_INIT_PTR_BOUNDS,
BUILT_IN_CHKP_NULL_PTR_BOUNDS, BUILT_IN_CHKP_COPY_PTR_BOUNDS,
BUILT_IN_CHKP_CHECK_PTR_LBOUNDS, BUILT_IN_CHKP_CHECK_PTR_UBOUNDS,
BUILT_IN_CHKP_CHECK_PTR_BOUNDS, BUILT_IN_CHKP_SET_PTR_BOUNDS,
BUILT_IN_CHKP_NARROW_PTR_BOUNDS, BUILT_IN_CHKP_STORE_PTR_BOUNDS,
BUILT_IN_CHKP_GET_PTR_LBOUND, BUILT_IN_CHKP_GET_PTR_UBOUND,
BUILT_IN_CHKP_BNDMK, BUILT_IN_CHKP_BNDSTX, BUILT_IN_CHKP_BNDCL,
BUILT_IN_CHKP_BNDCU, BUILT_IN_CHKP_BNDLDX, BUILT_IN_CHKP_BNDRET,
BUILT_IN_CHKP_INTERSECT, BUILT_IN_CHKP_NARROW,
BUILT_IN_CHKP_EXTRACT_LOWER, BUILT_IN_CHKP_EXTRACT_UPPER.
(std_expand_builtin_va_start): Init bounds for va_list.
* cppbuiltin.c (define_builtin_macros_for_compilation_flags): Add
__CHKP__ macro when Pointer Bounds Checker is on.
* params.def (PARAM_CHKP_MAX_CTOR_SIZE): New.
* passes.def (pass_ipa_chkp_versioning): New.
(pass_early_local_passes): Renamed to pass_build_ssa_passes.
(pass_fixup_cfg): Moved to pass_chkp_instrumentation_passes.
(pass_chkp_instrumentation_passes): New.
(pass_ipa_chkp_produce_thunks): New.
(pass_local_optimization_passes): New.
(pass_chkp_opt): New.
* tree-pass.h (make_pass_ipa_chkp_versioning): New.
(make_pass_ipa_chkp_produce_thunks): New.
(make_pass_chkp): New.
(make_pass_chkp_opt): New.
(make_pass_early_local_passes): Renamed to ...
(make_pass_build_ssa_passes): This.
(make_pass_chkp_instrumentation_passes): New.
(make_pass_local_optimization_passes): New.
* passes.c (pass_manager::execute_early_local_passes): Execute
early passes in three steps.
(execute_all_early_local_passes): Renamed to ...
(execute_build_ssa_passes): This.
(pass_data_early_local_passes): Renamed to ...
(pass_data_build_ssa_passes): This.
(pass_early_local_passes): Renamed to ...
(pass_build_ssa_passes): This.
(pass_data_chkp_instrumentation_passes): New.
(pass_chkp_instrumentation_passes): New.
(pass_data_local_optimization_passes): New.
(pass_local_optimization_passes): New.
(make_pass_early_local_passes): Renamed to ...
(make_pass_build_ssa_passes): This.
(make_pass_chkp_instrumentation_passes): New.
(make_pass_local_optimization_passes): New.
* c-family/c.opt (fcheck-pointer-bounds): New.
(fchkp-check-incomplete-type): New.
(fchkp-zero-input-bounds-for-main): New.
(fchkp-first-field-has-own-bounds): New.
(fchkp-narrow-bounds): New.
(fchkp-narrow-to-innermost-array): New.
(fchkp-optimize): New.
(fchkp-use-fast-string-functions): New.
(fchkp-use-nochk-string-functions): New.
(fchkp-use-static-bounds): New.
(fchkp-use-static-const-bounds): New.
(fchkp-treat-zero-dynamic-size-as-infinite): New.
(fchkp-check-read): New.
(fchkp-check-write): New.
(fchkp-store-bounds): New.
(fchkp-instrument-calls): New.
(fchkp-instrument-marked-only): New.
(Wchkp): New.
* c-family/c-common.c (handle_bnd_variable_size_attribute): New.
(handle_bnd_legacy): New.
(handle_bnd_instrument): New.
(c_common_attribute_table): Add bnd_variable_size, bnd_legacy
and bnd_instrument. Fix documentation.
(c_common_format_attribute_table): Likewsie.
* toplev.c: include tree-chkp.h.
(process_options): Check Pointer Bounds Checker is supported.
(compile_file): Add chkp_finish_file call.
* ipa-cp.c (initialize_node_lattices): Use cgraph_local_p
to handle instrumentation clones properly.
(propagate_constants_accross_call): Do not propagate
through instrumentation thunks.
* ipa-pure-const.c (propagate_pure_const): Support
IPA_REF_CHKP.
* ipa-inline.c (early_inliner): Check edge has summary allocated.
* ipa-split.c: Include tree-chkp.h.
(find_retbnd): New.
(split_part_set_ssa_name_p): New.
(consider_split): Do not split retbnd and retval
producers.
(insert_bndret_call_after): new.
(split_function): Propagate Pointer Bounds Checker
instrumentation marks and handle returned bounds.
* tree-ssa-sccvn.h (vn_reference_op_struct): Transform opcode
into bit field and add with_bounds field.
* tree-ssa-sccvn.c (copy_reference_ops_from_call): Set
with_bounds field for instrumented calls.
* tree-ssa-pre.c (create_component_ref_by_pieces_1): Restore
CALL_WITH_BOUNDS_P flag for calls.
* tree-ssa-ccp.c: Include tree-chkp.h.
(insert_clobber_before_stack_restore): Handle
BUILT_IN_CHKP_BNDRET calls.
* tree-ssa-dce.c: Include tree-chkp.h.
(propagate_necessity): For free call fed by alloc check
bounds are also provided by the same alloc.
(eliminate_unnecessary_stmts): Handle BUILT_IN_CHKP_BNDRET
used by free calls.
* tree-inline.c: Include tree-chkp.h.
(declare_return_variable): Add arg holding
returned bounds slot. Create and initialize returned bounds var.
(remap_gimple_stmt): Handle returned bounds.
Return sequence of statements instead of a single statement.
(insert_init_stmt): Add declaration.
(remap_gimple_seq): Adjust to new remap_gimple_stmt signature.
(copy_bb): Adjust to changed return type of remap_gimple_stmt.
Properly handle bounds in va_arg_pack and va_arg_pack_len.
(expand_call_inline): Handle returned bounds. Add bounds copy
for generated mem to mem assignments.
* tree-inline.h (copy_body_data): Add fields retbnd and
assign_stmts.
* value-prof.c: Include tree-chkp.h.
(gimple_ic): Support returned bounds.
* ipa.c (cgraph_build_static_cdtor_1): Support contructors
with "chkp ctor" and "bnd_legacy" attributes.
(symtab_remove_unreachable_nodes): Keep initial values for
pointer bounds to be used for checks eliminations.
(process_references): Handle IPA_REF_CHKP.
(walk_polymorphic_call_targets): Likewise.
* ipa-visibility.c (cgraph_externally_visible_p): Mark
instrumented 'main' as externally visible.
(function_and_variable_visibility): Filter instrumentation
thunks.
* cgraph.h (cgraph_thunk_info): Add add_pointer_bounds_args
field.
(cgraph_node): Add instrumented_version, orig_decl and
instrumentation_clone fields.
(symtab_node::get_alias_target): Allow IPA_REF_CHKP reference.
(varpool_node): Add need_bounds_init field.
(cgraph_local_p): New.
* cgraph.c: Include tree-chkp.h.
(cgraph_node::remove): Fix instrumented_version
of the referenced node if any.
(cgraph_node::dump): Dump instrumentation_clone and
instrumented_version fields.
(cgraph_node::verify_node): Check correctness of IPA_REF_CHKP
references and instrumentation thunks.
(cgraph_can_remove_if_no_direct_calls_and_refs_p): Keep
all not instrumented instrumentation clones alive.
(cgraph_redirect_edge_call_stmt_to_callee): Support
returned bounds.
* cgraphbuild.c (rebuild_cgraph_edges): Rebuild IPA_REF_CHKP
reference.
(cgraph_rebuild_references): Likewise.
* cgraphunit.c: Include tree-chkp.h.
(assemble_thunks_and_aliases): Skip thunks calling instrumneted
function version.
(varpool_finalize_decl): Register statically initialized decls
in Pointer Bounds Checker.
(walk_polymorphic_call_targets): Do not mark generated call to
__builtin_unreachable as with_bounds.
(output_weakrefs): If there are both instrumented and original
versions, output only one of them.
(cgraph_node::expand_thunk): Set with_bounds flag
for created call statement.
* ipa-ref.h (ipa_ref_use): Add IPA_REF_CHKP.
(ipa_ref): increase size of use field.
* symtab.c (ipa_ref_use_name): Add element for IPA_REF_CHKP.
* varpool.c (dump_varpool_node): Dump need_bounds_init field.
(ctor_for_folding): Do not fold constant bounds vars.
* lto-streamer.h (LTO_minor_version): Change minor version from
0 to 1.
* lto-cgraph.c (compute_ltrans_boundary): Keep initial values for
pointer bounds.
(lto_output_node): Output instrumentation_clone,
thunk.add_pointer_bounds_args and orig_decl field.
(lto_output_ref): Adjust to new ipa_ref::use field size.
(input_overwrite_node): Read instrumentation_clone field.
(input_node): Read thunk.add_pointer_bounds_args and orig_decl
fields.
(input_ref): Adjust to new ipa_ref::use field size.
(input_cgraph_1): Compute instrumented_version fields and restore
IDENTIFIER_TRANSPARENT_ALIAS chains.
(lto_output_varpool_node): Output
need_bounds_init value.
(input_varpool_node): Read need_bounds_init value.
* lto-partition.c (add_symbol_to_partition_1): Keep original
and instrumented versions together.
(privatize_symbol_name): Restore transparent alias chain if required.
(add_references_to_partition): Add references to pointer bounds vars.
* dbxout.c (dbxout_type): Ignore POINTER_BOUNDS_TYPE.
* dwarf2out.c (gen_subprogram_die): Ignore bound args.
(gen_type_die_with_usage): Skip pointer bounds.
(dwarf2out_global_decl): Likewise.
(is_base_type): Support POINTER_BOUNDS_TYPE.
(gen_formal_types_die): Skip pointer bounds.
(gen_decl_die): Likewise.
* var-tracking.c (vt_add_function_parameters): Skip
bounds parameters.
* ipa-icf.c (sem_function::merge): Do not merge when instrumentation
thunk still exists.
(sem_variable::merge): Reset need_bounds_init flag.
* doc/extend.texi: Document Pointer Bounds Checker built-in functions
and attributes.
* doc/tm.texi.in (TARGET_LOAD_BOUNDS_FOR_ARG): New.
(TARGET_STORE_BOUNDS_FOR_ARG): New.
(TARGET_LOAD_RETURNED_BOUNDS): New.
(TARGET_STORE_RETURNED_BOUNDS): New.
(TARGET_CHKP_FUNCTION_VALUE_BOUNDS): New.
(TARGET_SETUP_INCOMING_VARARG_BOUNDS): New.
(TARGET_BUILTIN_CHKP_FUNCTION): New.
(TARGET_CHKP_BOUND_TYPE): New.
(TARGET_CHKP_BOUND_MODE): New.
(TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New.
(TARGET_CHKP_INITIALIZE_BOUNDS): New.
* doc/tm.texi: Regenerated.
* doc/rtl.texi (MODE_POINTER_BOUNDS): New.
(BND32mode): New.
(BND64mode): New.
* doc/invoke.texi (-mmpx): New.
(-mno-mpx): New.
(chkp-max-ctor-size): New.
* config/i386/constraints.md (w): New.
(Ti): New.
(Tb): New.
* config/i386/i386-c.c (ix86_target_macros_internal): Add __MPX__.
* config/i386/i386-modes.def (BND32): New.
(BND64): New.
* config/i386/i386-protos.h (ix86_bnd_prefixed_insn_p): New.
* config/i386/i386.c: Include tree-chkp.h, rtl-chkp.h, tree-iterator.h.
(regclass_map): Add bound registers.
(dbx_register_map): Likewise.
(dbx64_register_map): Likewise.
(svr4_dbx_register_map): Likewise.
(isa_opts): Add -mmpx.
(PTA_MPX): New.
(ix86_option_override_internal): Support MPX ISA.
(ix86_conditional_register_usage): Support bound registers.
(ix86_code_end): Add MPX bnd prefix.
(output_set_got): Likewise.
(print_reg): Avoid prefixes for bound registers.
(ix86_print_operand): Add '!' (MPX bnd) print prefix support.
(ix86_print_operand_punct_valid_p): Likewise.
(ix86_print_operand_address): Support UNSPEC_BNDMK_ADDR and
UNSPEC_BNDLDX_ADDR.
(ix86_output_call_insn): Add MPX bnd prefix to branch instructions.
(ix86_class_likely_spilled_p): Add bound regs support.
(ix86_hard_regno_mode_ok): Likewise.
(x86_order_regs_for_local_alloc): Likewise.
(ix86_bnd_prefixed_insn_p): New.
(ix86_builtins): Add
IX86_BUILTIN_BNDMK, IX86_BUILTIN_BNDSTX,
IX86_BUILTIN_BNDLDX, IX86_BUILTIN_BNDCL,
IX86_BUILTIN_BNDCU, IX86_BUILTIN_BNDRET,
IX86_BUILTIN_BNDNARROW, IX86_BUILTIN_BNDINT,
IX86_BUILTIN_SIZEOF, IX86_BUILTIN_BNDLOWER,
IX86_BUILTIN_BNDUPPER.
(builtin_isa): Add leaf_p and nothrow_p fields.
(def_builtin): Initialize leaf_p and nothrow_p.
(ix86_add_new_builtins): Handle leaf_p and nothrow_p
flags.
(bdesc_mpx): New.
(bdesc_mpx_const): New.
(ix86_init_mpx_builtins): New.
(ix86_init_builtins): Call ix86_init_mpx_builtins.
(ix86_emit_cmove): New.
(ix86_emit_move_max): New.
(ix86_expand_builtin): Expand IX86_BUILTIN_BNDMK,
IX86_BUILTIN_BNDSTX, IX86_BUILTIN_BNDLDX,
IX86_BUILTIN_BNDCL, IX86_BUILTIN_BNDCU,
IX86_BUILTIN_BNDRET, IX86_BUILTIN_BNDNARROW,
IX86_BUILTIN_BNDINT, IX86_BUILTIN_SIZEOF,
IX86_BUILTIN_BNDLOWER, IX86_BUILTIN_BNDUPPER.
(ix86_function_value_bounds): New.
(ix86_builtin_mpx_function): New.
(ix86_get_arg_address_for_bt): New.
(ix86_load_bounds): New.
(ix86_store_bounds): New.
(ix86_load_returned_bounds): New.
(ix86_store_returned_bounds): New.
(ix86_mpx_bound_mode): New.
(ix86_make_bounds_constant): New.
(ix86_initialize_bounds):
(TARGET_LOAD_BOUNDS_FOR_ARG): New.
(TARGET_STORE_BOUNDS_FOR_ARG): New.
(TARGET_LOAD_RETURNED_BOUNDS): New.
(TARGET_STORE_RETURNED_BOUNDS): New.
(TARGET_CHKP_BOUND_MODE): New.
(TARGET_BUILTIN_CHKP_FUNCTION): New.
(TARGET_CHKP_FUNCTION_VALUE_BOUNDS): New.
(TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New.
(TARGET_CHKP_INITIALIZE_BOUNDS): New.
(ix86_option_override_internal): Do not
support x32 with MPX.
(init_cumulative_args): Init stdarg, bnd_regno, bnds_in_bt
and force_bnd_pass.
(function_arg_advance_32): Return number of used integer
registers.
(function_arg_advance_64): Likewise.
(function_arg_advance_ms_64): Likewise.
(ix86_function_arg_advance): Handle pointer bounds.
(ix86_function_arg): Likewise.
(ix86_function_value_regno_p): Mark fisrt bounds registers as
possible function value.
(ix86_function_value_1): Handle pointer bounds type/mode
(ix86_return_in_memory): Likewise.
(ix86_print_operand): Analyse insn to decide abounf "bnd" prefix.
(ix86_expand_call): Generate returned bounds.
(ix86_setup_incoming_vararg_bounds): New.
(ix86_va_start): Initialize bounds for pointers in va_list.
(TARGET_SETUP_INCOMING_VARARG_BOUNDS): New.
* config/i386/i386.h (TARGET_MPX): New.
(TARGET_MPX_P): New.
(FIRST_PSEUDO_REGISTER): Fix to new value.
(FIXED_REGISTERS): Add bound registers.
(CALL_USED_REGISTERS): Likewise.
(REG_ALLOC_ORDER): Likewise.
(HARD_REGNO_NREGS): Likewise.
(VALID_BND_REG_MODE): New.
(FIRST_BND_REG): New.
(LAST_BND_REG): New.
(reg_class): Add BND_REGS.
(REG_CLASS_NAMES): Likewise.
(REG_CLASS_CONTENTS): Likewise.
(BND_REGNO_P): New.
(ANY_BND_REG_P): New.
(BNDmode): New.
(HI_REGISTER_NAMES): Add bound registers.
(ix86_args): Add bnd_regno, bnds_in_bt, force_bnd_pass and
stdarg fields.
* config/i386/i386.md (UNSPEC_BNDMK): New.
(UNSPEC_BNDMK_ADDR): New.
(UNSPEC_BNDSTX): New.
(UNSPEC_BNDLDX): New.
(UNSPEC_BNDLDX_ADDR): New.
(UNSPEC_BNDCL): New.
(UNSPEC_BNDCU): New.
(UNSPEC_BNDCN): New.
(UNSPEC_MPX_FENCE): New.
(UNSPEC_SIZEOF): New.
(BND0_REG): New.
(BND1_REG): New.
(type): Add mpxmov, mpxmk, mpxchk, mpxld, mpxst.
(length_immediate): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst.
(prefix_rep): Check for bnd prefix.
(prefix_0f): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst.
(length_nobnd): New.
(length): Use length_nobnd when specified.
(memory): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst.
(BND): New.
(bnd_ptr): New.
(BNDCHECK): New.
(bndcheck): New.
(*jcc_1): Add MPX bnd prefix.
(*jcc_2): Likewise.
(jump): Likewise.
(*indirect_jump): Likewise.
(*tablejump_1): Likewise.
(simple_return_internal): Likewise.
(simple_return_internal_long): Likewise.
(simple_return_pop_internal): Likewise.
(simple_return_indirect_internal): Likewise.
(<mode>_mk): New.
(*<mode>_mk): New.
(mov<mode>): New.
(*mov<mode>_internal_mpx): New.
(<mode>_<bndcheck>): New.
(*<mode>_<bndcheck>): New.
(<mode>_ldx): New.
(*<mode>_ldx): New.
(<mode>_stx): New.
(*<mode>_stx): New.
move_size_reloc_<mode>): New.
* config/i386/predicates.md (address_mpx_no_base_operand): New.
(address_mpx_no_index_operand): New.
(bnd_mem_operator): New.
(symbol_operand): New.
(x86_64_immediate_size_operand): New.
* config/i386/i386.opt (mmpx): New.
* config/i386/i386-builtin-types.def (BND): New.
(ULONG): New.
(BND_FTYPE_PCVOID_ULONG): New.
(VOID_FTYPE_BND_PCVOID): New.
(VOID_FTYPE_PCVOID_PCVOID_BND): New.
(BND_FTYPE_PCVOID_PCVOID): New.
(BND_FTYPE_PCVOID): New.
(BND_FTYPE_BND_BND): New.
(PVOID_FTYPE_PVOID_PVOID_ULONG): New.
(PVOID_FTYPE_PCVOID_BND_ULONG): New.
(ULONG_FTYPE_VOID): New.
(PVOID_FTYPE_BND): New.
gcc/testsuite/
2014-11-05 Ilya Enkovich <ilya.enkovich@intel.com>
* gcc.target/i386/chkp-builtins-1.c: New.
* gcc.target/i386/chkp-builtins-2.c: New.
* gcc.target/i386/chkp-builtins-3.c: New.
* gcc.target/i386/chkp-builtins-4.c: New.
* gcc.target/i386/chkp-remove-bndint-1.c: New.
* gcc.target/i386/chkp-remove-bndint-2.c: New.
* gcc.target/i386/chkp-const-check-1.c: New.
* gcc.target/i386/chkp-const-check-2.c: New.
* gcc.target/i386/chkp-lifetime-1.c: New.
* gcc.dg/pr37858.c: Replace early_local_cleanups pass name
with build_ssa_passes.
From-SVN: r217125
Diffstat (limited to 'gcc/tree-chkp-opt.c')
-rw-r--r-- | gcc/tree-chkp-opt.c | 1100 |
1 files changed, 1100 insertions, 0 deletions
diff --git a/gcc/tree-chkp-opt.c b/gcc/tree-chkp-opt.c new file mode 100644 index 0000000..383c66f --- /dev/null +++ b/gcc/tree-chkp-opt.c @@ -0,0 +1,1100 @@ +/* Pointer Bounds Checker optimization pass. + Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Ilya Enkovich (ilya.enkovich@intel.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree-core.h" +#include "tree.h" +#include "target.h" +#include "tree-cfg.h" +#include "tree-pass.h" +#include "is-a.h" +#include "cfgloop.h" +#include "stringpool.h" +#include "tree-ssa-alias.h" +#include "tree-ssanames.h" +#include "tree-ssa-operands.h" +#include "tree-ssa-address.h" +#include "tree-ssa.h" +#include "predict.h" +#include "dominance.h" +#include "cfg.h" +#include "basic-block.h" +#include "tree-ssa-loop-niter.h" +#include "gimple-expr.h" +#include "gimple.h" +#include "tree-phinodes.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#include "gimple-pretty-print.h" +#include "gimple-iterator.h" +#include "gimplify.h" +#include "gimplify-me.h" +#include "expr.h" +#include "tree-chkp.h" +#include "diagnostic.h" + +enum check_type +{ + CHECK_LOWER_BOUND, + CHECK_UPPER_BOUND +}; + +struct pol_item +{ + tree cst; + tree var; +}; + +struct address_t +{ + vec<struct pol_item> pol; +}; + +/* Structure to hold check informtation. */ +struct check_info +{ + /* Type of the check. */ + check_type type; + /* Address used for the check. */ + address_t addr; + /* Bounds used for the check. */ + tree bounds; + /* Check statement. Can be NULL for removed checks. */ + gimple stmt; +}; + +/* Structure to hold checks information for BB. */ +struct bb_checks +{ + vec<struct check_info, va_heap, vl_ptr> checks; +}; + +static void chkp_collect_value (tree ssa_name, address_t &res); + +#define chkp_bndmk_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK)) +#define chkp_intersect_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT)) +#define chkp_checkl_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL)) +#define chkp_checku_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU)) + +static vec<struct bb_checks, va_heap, vl_ptr> check_infos = vNULL; + +/* Comparator for pol_item structures I1 and I2 to be used + to find items with equal var. Also used for polynomial + sorting. */ +static int +chkp_pol_item_compare (const void *i1, const void *i2) +{ + const struct pol_item *p1 = (const struct pol_item *)i1; + const struct pol_item *p2 = (const struct pol_item *)i2; + + if (p1->var == p2->var) + return 0; + else if (p1->var > p2->var) + return 1; + else + return -1; +} + +/* Find polynomial item in ADDR with var equal to VAR + and return its index. Return -1 if item was not + found. */ +static int +chkp_pol_find (address_t &addr, tree var) +{ + int left = 0; + int right = addr.pol.length () - 1; + int n; + + while (right >= left) + { + n = (left + right) / 2; + + if (addr.pol[n].var == var + || (var && addr.pol[n].var + && TREE_CODE (var) == ADDR_EXPR + && TREE_CODE (addr.pol[n].var) == ADDR_EXPR + && TREE_OPERAND (var, 0) == TREE_OPERAND (addr.pol[n].var, 0))) + return n; + else if (addr.pol[n].var > var) + right = n - 1; + else + left = n + 1; + } + + return -1; +} + +/* Return constant CST extended to size type. */ +static tree +chkp_extend_const (tree cst) +{ + if (TYPE_PRECISION (TREE_TYPE (cst)) < TYPE_PRECISION (size_type_node)) + return build_int_cst_type (size_type_node, tree_to_shwi (cst)); + + return cst; +} + +/* Add polynomial item CST * VAR to ADDR. */ +static void +chkp_add_addr_item (address_t &addr, tree cst, tree var) +{ + int n = chkp_pol_find (addr, var); + + cst = chkp_extend_const (cst); + + if (n < 0) + { + struct pol_item item; + item.cst = cst; + item.var = var; + + addr.pol.safe_push (item); + addr.pol.qsort (&chkp_pol_item_compare); + } + else + { + addr.pol[n].cst = fold_build2 (PLUS_EXPR, TREE_TYPE (addr.pol[n].cst), + addr.pol[n].cst, cst); + if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST + && integer_zerop (addr.pol[n].cst)) + addr.pol.ordered_remove (n); + } +} + +/* Subtract polynomial item CST * VAR from ADDR. */ +static void +chkp_sub_addr_item (address_t &addr, tree cst, tree var) +{ + int n = chkp_pol_find (addr, var); + + cst = chkp_extend_const (cst); + + if (n < 0) + { + struct pol_item item; + item.cst = fold_build2 (MINUS_EXPR, TREE_TYPE (cst), + integer_zero_node, cst); + item.var = var; + + addr.pol.safe_push (item); + addr.pol.qsort (&chkp_pol_item_compare); + } + else + { + addr.pol[n].cst = fold_build2 (MINUS_EXPR, TREE_TYPE (addr.pol[n].cst), + addr.pol[n].cst, cst); + if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST + && integer_zerop (addr.pol[n].cst)) + addr.pol.ordered_remove (n); + } +} + +/* Add address DELTA to ADDR. */ +static void +chkp_add_addr_addr (address_t &addr, address_t &delta) +{ + unsigned int i; + for (i = 0; i < delta.pol.length (); i++) + chkp_add_addr_item (addr, delta.pol[i].cst, delta.pol[i].var); +} + +/* Subtract address DELTA from ADDR. */ +static void +chkp_sub_addr_addr (address_t &addr, address_t &delta) +{ + unsigned int i; + for (i = 0; i < delta.pol.length (); i++) + chkp_sub_addr_item (addr, delta.pol[i].cst, delta.pol[i].var); +} + +/* Mutiply address ADDR by integer constant MULT. */ +static void +chkp_mult_addr (address_t &addr, tree mult) +{ + unsigned int i; + for (i = 0; i < addr.pol.length (); i++) + addr.pol[i].cst = fold_build2 (MULT_EXPR, TREE_TYPE (addr.pol[i].cst), + addr.pol[i].cst, mult); +} + +/* Return 1 if we may prove ADDR has a constant value with + determined sign, which is put into *SIGN. Otherwise + return 0. */ +static bool +chkp_is_constant_addr (const address_t &addr, int *sign) +{ + *sign = 0; + + if (addr.pol.length () == 0) + return true; + else if (addr.pol.length () > 1) + return false; + else if (addr.pol[0].var) + return false; + else if (integer_zerop (addr.pol[0].cst)) + *sign = 0; + else if (tree_int_cst_sign_bit (addr.pol[0].cst)) + *sign = -1; + else + *sign = 1; + + return true; +} + +/* Dump ADDR into dump_file. */ +static void +chkp_print_addr (const address_t &addr) +{ + unsigned int n = 0; + for (n = 0; n < addr.pol.length (); n++) + { + if (n > 0) + fprintf (dump_file, " + "); + + if (addr.pol[n].var == NULL_TREE) + print_generic_expr (dump_file, addr.pol[n].cst, 0); + else + { + if (TREE_CODE (addr.pol[n].cst) != INTEGER_CST + || !integer_onep (addr.pol[n].cst)) + { + print_generic_expr (dump_file, addr.pol[n].cst, 0); + fprintf (dump_file, " * "); + } + print_generic_expr (dump_file, addr.pol[n].var, 0); + } + } +} + +/* Compute value of PTR and put it into address RES. + PTR has to be ADDR_EXPR. */ +static void +chkp_collect_addr_value (tree ptr, address_t &res) +{ + tree obj = TREE_OPERAND (ptr, 0); + address_t addr; + + switch (TREE_CODE (obj)) + { + case INDIRECT_REF: + chkp_collect_value (TREE_OPERAND (obj, 0), res); + break; + + case MEM_REF: + chkp_collect_value (TREE_OPERAND (obj, 0), res); + addr.pol.create (0); + chkp_collect_value (TREE_OPERAND (obj, 1), addr); + chkp_add_addr_addr (res, addr); + addr.pol.release (); + break; + + case ARRAY_REF: + chkp_collect_value (build_fold_addr_expr (TREE_OPERAND (obj, 0)), res); + addr.pol.create (0); + chkp_collect_value (TREE_OPERAND (obj, 1), addr); + chkp_mult_addr (addr, array_ref_element_size (obj)); + chkp_add_addr_addr (res, addr); + addr.pol.release (); + break; + + case COMPONENT_REF: + { + tree str = TREE_OPERAND (obj, 0); + tree field = TREE_OPERAND (obj, 1); + chkp_collect_value (build_fold_addr_expr (str), res); + addr.pol.create (0); + chkp_collect_value (component_ref_field_offset (obj), addr); + chkp_add_addr_addr (res, addr); + addr.pol.release (); + if (DECL_FIELD_BIT_OFFSET (field)) + { + addr.pol.create (0); + chkp_collect_value (fold_build2 (TRUNC_DIV_EXPR, size_type_node, + DECL_FIELD_BIT_OFFSET (field), + size_int (BITS_PER_UNIT)), + addr); + chkp_add_addr_addr (res, addr); + addr.pol.release (); + } + } + break; + + default: + chkp_add_addr_item (res, integer_one_node, ptr); + break; + } +} + +/* Compute value of PTR and put it into address RES. */ +static void +chkp_collect_value (tree ptr, address_t &res) +{ + gimple def_stmt; + enum gimple_code code; + enum tree_code rhs_code; + address_t addr; + tree rhs1; + + if (TREE_CODE (ptr) == INTEGER_CST) + { + chkp_add_addr_item (res, ptr, NULL); + return; + } + else if (TREE_CODE (ptr) == ADDR_EXPR) + { + chkp_collect_addr_value (ptr, res); + return; + } + else if (TREE_CODE (ptr) != SSA_NAME) + { + chkp_add_addr_item (res, integer_one_node, ptr); + return; + } + + /* Now we handle the case when polynomial is computed + for SSA NAME. */ + def_stmt = SSA_NAME_DEF_STMT (ptr); + code = gimple_code (def_stmt); + + /* Currently we do not walk through statements other + than assignment. */ + if (code != GIMPLE_ASSIGN) + { + chkp_add_addr_item (res, integer_one_node, ptr); + return; + } + + rhs_code = gimple_assign_rhs_code (def_stmt); + rhs1 = gimple_assign_rhs1 (def_stmt); + + switch (rhs_code) + { + case SSA_NAME: + case INTEGER_CST: + case ADDR_EXPR: + chkp_collect_value (rhs1, res); + break; + + case PLUS_EXPR: + case POINTER_PLUS_EXPR: + chkp_collect_value (rhs1, res); + addr.pol.create (0); + chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr); + chkp_add_addr_addr (res, addr); + addr.pol.release (); + break; + + case MINUS_EXPR: + chkp_collect_value (rhs1, res); + addr.pol.create (0); + chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr); + chkp_sub_addr_addr (res, addr); + addr.pol.release (); + break; + + case MULT_EXPR: + if (TREE_CODE (rhs1) == SSA_NAME + && TREE_CODE (gimple_assign_rhs2 (def_stmt)) == INTEGER_CST) + { + chkp_collect_value (rhs1, res); + chkp_mult_addr (res, gimple_assign_rhs2 (def_stmt)); + } + else if (TREE_CODE (gimple_assign_rhs2 (def_stmt)) == SSA_NAME + && TREE_CODE (rhs1) == INTEGER_CST) + { + chkp_collect_value (gimple_assign_rhs2 (def_stmt), res); + chkp_mult_addr (res, rhs1); + } + else + chkp_add_addr_item (res, integer_one_node, ptr); + break; + + default: + chkp_add_addr_item (res, integer_one_node, ptr); + break; + } +} + +/* Fill check_info structure *CI with information about + check STMT. */ +static void +chkp_fill_check_info (gimple stmt, struct check_info *ci) +{ + ci->addr.pol.create (0); + ci->bounds = gimple_call_arg (stmt, 1); + chkp_collect_value (gimple_call_arg (stmt, 0), ci->addr); + ci->type = (gimple_call_fndecl (stmt) == chkp_checkl_fndecl + ? CHECK_LOWER_BOUND + : CHECK_UPPER_BOUND); + ci->stmt = stmt; +} + +/* Release structures holding check information + for current function. */ +static void +chkp_release_check_info (void) +{ + unsigned int n, m; + + if (check_infos.exists ()) + { + for (n = 0; n < check_infos.length (); n++) + { + for (m = 0; m < check_infos[n].checks.length (); m++) + if (check_infos[n].checks[m].addr.pol.exists ()) + check_infos[n].checks[m].addr.pol.release (); + check_infos[n].checks.release (); + } + check_infos.release (); + } +} + +/* Create structures to hold check information + for current function. */ +static void +chkp_init_check_info (void) +{ + struct bb_checks empty_bbc; + int n; + + empty_bbc.checks = vNULL; + + chkp_release_check_info (); + + check_infos.create (last_basic_block_for_fn (cfun)); + for (n = 0; n < last_basic_block_for_fn (cfun); n++) + { + check_infos.safe_push (empty_bbc); + check_infos.last ().checks.create (0); + } +} + +/* Find all checks in current function and store info about them + in check_infos. */ +static void +chkp_gather_checks_info (void) +{ + basic_block bb; + gimple_stmt_iterator i; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Gathering information about checks...\n"); + + chkp_init_check_info (); + + FOR_EACH_BB_FN (bb, cfun) + { + struct bb_checks *bbc = &check_infos[bb->index]; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Searching checks in BB%d...\n", bb->index); + + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + { + gimple stmt = gsi_stmt (i); + + if (gimple_code (stmt) != GIMPLE_CALL) + continue; + + if (gimple_call_fndecl (stmt) == chkp_checkl_fndecl + || gimple_call_fndecl (stmt) == chkp_checku_fndecl) + { + struct check_info ci; + + chkp_fill_check_info (stmt, &ci); + bbc->checks.safe_push (ci); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Adding check information:\n"); + fprintf (dump_file, " bounds: "); + print_generic_expr (dump_file, ci.bounds, 0); + fprintf (dump_file, "\n address: "); + chkp_print_addr (ci.addr); + fprintf (dump_file, "\n check: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + } + } + } +} + +/* Return 1 if check CI against BOUNDS always pass, + -1 if check CI against BOUNDS always fails and + 0 if we cannot compute check result. */ +static int +chkp_get_check_result (struct check_info *ci, tree bounds) +{ + gimple bnd_def; + address_t bound_val; + int sign, res = 0; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Trying to compute result of the check\n"); + fprintf (dump_file, " check: "); + print_gimple_stmt (dump_file, ci->stmt, 0, 0); + fprintf (dump_file, " address: "); + chkp_print_addr (ci->addr); + fprintf (dump_file, "\n bounds: "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, "\n"); + } + + if (TREE_CODE (bounds) != SSA_NAME) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: bounds tree code is not ssa_name\n"); + return 0; + } + + bnd_def = SSA_NAME_DEF_STMT (bounds); + /* Currently we handle cases when bounds are result of bndmk + or loaded static bounds var. */ + if (gimple_code (bnd_def) == GIMPLE_CALL + && gimple_call_fndecl (bnd_def) == chkp_bndmk_fndecl) + { + bound_val.pol.create (0); + chkp_collect_value (gimple_call_arg (bnd_def, 0), bound_val); + if (ci->type == CHECK_UPPER_BOUND) + { + address_t size_val; + size_val.pol.create (0); + chkp_collect_value (gimple_call_arg (bnd_def, 1), size_val); + chkp_add_addr_addr (bound_val, size_val); + size_val.pol.release (); + chkp_add_addr_item (bound_val, integer_minus_one_node, NULL); + } + } + else if (gimple_code (bnd_def) == GIMPLE_ASSIGN + && gimple_assign_rhs1 (bnd_def) == chkp_get_zero_bounds_var ()) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: always pass with zero bounds\n"); + return 1; + } + else if (gimple_code (bnd_def) == GIMPLE_ASSIGN + && gimple_assign_rhs1 (bnd_def) == chkp_get_none_bounds_var ()) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: always fails with none bounds\n"); + return -1; + } + else if (gimple_code (bnd_def) == GIMPLE_ASSIGN + && TREE_CODE (gimple_assign_rhs1 (bnd_def)) == VAR_DECL) + { + tree bnd_var = gimple_assign_rhs1 (bnd_def); + tree var; + tree size; + + if (!DECL_INITIAL (bnd_var) + || DECL_INITIAL (bnd_var) == error_mark_node) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: cannot compute bounds\n"); + return 0; + } + + gcc_assert (TREE_CODE (DECL_INITIAL (bnd_var)) == ADDR_EXPR); + var = TREE_OPERAND (DECL_INITIAL (bnd_var), 0); + + bound_val.pol.create (0); + chkp_collect_value (DECL_INITIAL (bnd_var), bound_val); + if (ci->type == CHECK_UPPER_BOUND) + { + if (TREE_CODE (var) == VAR_DECL) + { + if (DECL_SIZE (var) + && !chkp_variable_size_type (TREE_TYPE (var))) + size = DECL_SIZE_UNIT (var); + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: cannot compute bounds\n"); + return 0; + } + } + else + { + gcc_assert (TREE_CODE (var) == STRING_CST); + size = build_int_cst (size_type_node, + TREE_STRING_LENGTH (var)); + } + + address_t size_val; + size_val.pol.create (0); + chkp_collect_value (size, size_val); + chkp_add_addr_addr (bound_val, size_val); + size_val.pol.release (); + chkp_add_addr_item (bound_val, integer_minus_one_node, NULL); + } + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: cannot compute bounds\n"); + return 0; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " bound value: "); + chkp_print_addr (bound_val); + fprintf (dump_file, "\n"); + } + + chkp_sub_addr_addr (bound_val, ci->addr); + + if (!chkp_is_constant_addr (bound_val, &sign)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: cannot compute result\n"); + + res = 0; + } + else if (sign == 0 + || (ci->type == CHECK_UPPER_BOUND && sign > 0) + || (ci->type == CHECK_LOWER_BOUND && sign < 0)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: always pass\n"); + + res = 1; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " result: always fail\n"); + + res = -1; + } + + bound_val.pol.release (); + + return res; +} + +/* Try to compare bounds value and address value + used in the check CI. If we can prove that check + always pass then remove it. */ +static void +chkp_remove_check_if_pass (struct check_info *ci) +{ + int result = 0; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Trying to remove check: "); + print_gimple_stmt (dump_file, ci->stmt, 0, 0); + } + + result = chkp_get_check_result (ci, ci->bounds); + + if (result == 1) + { + gimple_stmt_iterator i = gsi_for_stmt (ci->stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " action: delete check (always pass)\n"); + + gsi_remove (&i, true); + unlink_stmt_vdef (ci->stmt); + release_defs (ci->stmt); + ci->stmt = NULL; + } + else if (result == -1) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " action: keep check (always fail)\n"); + warning_at (gimple_location (ci->stmt), OPT_Wchkp, + "memory access check always fail"); + } + else if (result == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " action: keep check (cannot compute result)\n"); + } +} + +/* For bounds used in CI check if bounds are produced by + intersection and we may use outer bounds instead. If + transformation is possible then fix check statement and + recompute its info. */ +static void +chkp_use_outer_bounds_if_possible (struct check_info *ci) +{ + gimple bnd_def; + tree bnd1, bnd2, bnd_res = NULL; + int check_res1, check_res2; + + if (TREE_CODE (ci->bounds) != SSA_NAME) + return; + + bnd_def = SSA_NAME_DEF_STMT (ci->bounds); + if (gimple_code (bnd_def) != GIMPLE_CALL + || gimple_call_fndecl (bnd_def) != chkp_intersect_fndecl) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Check if bounds intersection is redundant: \n"); + fprintf (dump_file, " check: "); + print_gimple_stmt (dump_file, ci->stmt, 0, 0); + fprintf (dump_file, " intersection: "); + print_gimple_stmt (dump_file, bnd_def, 0, 0); + fprintf (dump_file, "\n"); + } + + bnd1 = gimple_call_arg (bnd_def, 0); + bnd2 = gimple_call_arg (bnd_def, 1); + + check_res1 = chkp_get_check_result (ci, bnd1); + check_res2 = chkp_get_check_result (ci, bnd2); + if (check_res1 == 1) + bnd_res = bnd2; + else if (check_res1 == -1) + bnd_res = bnd1; + else if (check_res2 == 1) + bnd_res = bnd1; + else if (check_res2 == -1) + bnd_res = bnd2; + + if (bnd_res) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " action: use "); + print_generic_expr (dump_file, bnd2, 0); + fprintf (dump_file, " instead of "); + print_generic_expr (dump_file, ci->bounds, 0); + fprintf (dump_file, "\n"); + } + + ci->bounds = bnd_res; + gimple_call_set_arg (ci->stmt, 1, bnd_res); + update_stmt (ci->stmt); + chkp_fill_check_info (ci->stmt, ci); + } +} + +/* Try to find checks whose bounds were produced by intersection + which does not affect check result. In such check outer bounds + are used instead. It allows to remove excess intersections + and helps to compare checks. */ +static void +chkp_remove_excess_intersections (void) +{ + basic_block bb; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Searching for redundant bounds intersections...\n"); + + FOR_EACH_BB_FN (bb, cfun) + { + struct bb_checks *bbc = &check_infos[bb->index]; + unsigned int no; + + /* Iterate through all found checks in BB. */ + for (no = 0; no < bbc->checks.length (); no++) + if (bbc->checks[no].stmt) + chkp_use_outer_bounds_if_possible (&bbc->checks[no]); + } +} + +/* Try to remove all checks which are known to alwyas pass. */ +static void +chkp_remove_constant_checks (void) +{ + basic_block bb; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Searching for redundant checks...\n"); + + FOR_EACH_BB_FN (bb, cfun) + { + struct bb_checks *bbc = &check_infos[bb->index]; + unsigned int no; + + /* Iterate through all found checks in BB. */ + for (no = 0; no < bbc->checks.length (); no++) + if (bbc->checks[no].stmt) + chkp_remove_check_if_pass (&bbc->checks[no]); + } +} + +/* Intrumentation pass inserts most of bounds creation code + in the header of the function. We want to move bounds + creation closer to bounds usage to reduce bounds lifetime. + We also try to avoid bounds creation code on paths where + bounds are not used. */ +static void +chkp_reduce_bounds_lifetime (void) +{ + basic_block bb = FALLTHRU_EDGE (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest; + gimple_stmt_iterator i; + + for (i = gsi_start_bb (bb); !gsi_end_p (i); ) + { + gimple dom_use, use_stmt, stmt = gsi_stmt (i); + basic_block dom_bb; + ssa_op_iter iter; + imm_use_iterator use_iter; + use_operand_p use_p; + tree op; + bool want_move = false; + bool deps = false; + + if (gimple_code (stmt) == GIMPLE_CALL + && gimple_call_fndecl (stmt) == chkp_bndmk_fndecl) + want_move = true; + + if (gimple_code (stmt) == GIMPLE_ASSIGN + && POINTER_BOUNDS_P (gimple_assign_lhs (stmt)) + && gimple_assign_rhs_code (stmt) == VAR_DECL) + want_move = true; + + if (!want_move) + { + gsi_next (&i); + continue; + } + + /* Check we do not increase other values lifetime. */ + FOR_EACH_PHI_OR_STMT_USE (use_p, stmt, iter, SSA_OP_USE) + { + op = USE_FROM_PTR (use_p); + + if (TREE_CODE (op) == SSA_NAME + && gimple_code (SSA_NAME_DEF_STMT (op)) != GIMPLE_NOP) + { + deps = true; + break; + } + } + + if (deps) + { + gsi_next (&i); + continue; + } + + /* Check all usages of bounds. */ + if (gimple_code (stmt) == GIMPLE_CALL) + op = gimple_call_lhs (stmt); + else + { + gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN); + op = gimple_assign_lhs (stmt); + } + + dom_use = NULL; + dom_bb = NULL; + + FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, op) + { + if (dom_bb && + dominated_by_p (CDI_DOMINATORS, + dom_bb, gimple_bb (use_stmt))) + { + dom_use = use_stmt; + dom_bb = NULL; + } + else if (dom_bb) + dom_bb = nearest_common_dominator (CDI_DOMINATORS, dom_bb, + gimple_bb (use_stmt)); + else if (!dom_use) + dom_use = use_stmt; + else if (stmt_dominates_stmt_p (use_stmt, dom_use)) + dom_use = use_stmt; + else if (!stmt_dominates_stmt_p (dom_use, use_stmt) + /* If dom_use and use_stmt are PHI nodes in one BB + then it is OK to keep any of them as dom_use. + stmt_dominates_stmt_p returns 0 for such + combination, so check it here manually. */ + && (gimple_code (dom_use) != GIMPLE_PHI + || gimple_code (use_stmt) != GIMPLE_PHI + || gimple_bb (use_stmt) != gimple_bb (dom_use)) + ) + { + dom_bb = nearest_common_dominator (CDI_DOMINATORS, + gimple_bb (use_stmt), + gimple_bb (dom_use)); + dom_use = NULL; + } + } + + /* In case there is a single use, just move bounds + creation to the use. */ + if (dom_use || dom_bb) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Moving creation of "); + print_generic_expr (dump_file, op, 0); + fprintf (dump_file, " down to its use.\n"); + } + + if (dom_use && gimple_code (dom_use) == GIMPLE_PHI) + { + dom_bb = get_immediate_dominator (CDI_DOMINATORS, + gimple_bb (dom_use)); + dom_use = NULL; + } + + if (dom_bb == bb + || (dom_use && gimple_bb (dom_use) == bb)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Cannot move statement bacause there is no " + "suitable dominator block other than entry block.\n"); + + gsi_next (&i); + } + else + { + if (dom_bb) + { + gimple_stmt_iterator last = gsi_last_bb (dom_bb); + if (!gsi_end_p (last) && stmt_ends_bb_p (gsi_stmt (last))) + gsi_move_before (&i, &last); + else + gsi_move_after (&i, &last); + } + else + { + gimple_stmt_iterator gsi = gsi_for_stmt (dom_use); + gsi_move_before (&i, &gsi); + } + + update_stmt (stmt); + } + } + else + gsi_next (&i); + } +} + +/* Initilize checker optimization pass. */ +static void +chkp_opt_init (void) +{ + check_infos.create (0); + + calculate_dominance_info (CDI_DOMINATORS); + calculate_dominance_info (CDI_POST_DOMINATORS); + + /* With LTO constant bounds vars may be not initialized by now. + Get constant bounds vars to handle their assignments during + optimizations. */ + chkp_get_zero_bounds_var (); + chkp_get_none_bounds_var (); +} + +/* Finalise checker optimization pass. */ +static void +chkp_opt_fini (void) +{ + chkp_fix_cfg (); +} + +/* Checker optimization pass function. */ +static unsigned int +chkp_opt_execute (void) +{ + chkp_opt_init(); + + chkp_gather_checks_info (); + + chkp_remove_excess_intersections (); + + chkp_remove_constant_checks (); + + chkp_reduce_bounds_lifetime (); + + chkp_release_check_info (); + + chkp_opt_fini (); + + return 0; +} + +/* Pass gate. */ +static bool +chkp_opt_gate (void) +{ + return chkp_function_instrumented_p (cfun->decl) + && (flag_chkp_optimize > 0 + || (flag_chkp_optimize == -1 && optimize > 0)); +} + +namespace { + +const pass_data pass_data_chkp_opt = +{ + GIMPLE_PASS, /* type */ + "chkpopt", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa | PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_il + | TODO_update_ssa /* todo_flags_finish */ +}; + +class pass_chkp_opt : public gimple_opt_pass +{ +public: + pass_chkp_opt (gcc::context *ctxt) + : gimple_opt_pass (pass_data_chkp_opt, ctxt) + {} + + /* opt_pass methods: */ + virtual opt_pass * clone () + { + return new pass_chkp_opt (m_ctxt); + } + + virtual bool gate (function *) + { + return chkp_opt_gate (); + } + + virtual unsigned int execute (function *) + { + return chkp_opt_execute (); + } + +}; // class pass_chkp_opt + +} // anon namespace + +gimple_opt_pass * +make_pass_chkp_opt (gcc::context *ctxt) +{ + return new pass_chkp_opt (ctxt); +} |