diff options
100 files changed, 11266 insertions, 292 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 922283e5ceb..e6351f49a76 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,544 @@ +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. + 2014-11-05 Bernd Schmidt <bernds@codesourcery.com> * passes.def (pass_compute_alignments, pass_duplicate_computed_gotos, diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f383032e956..2c3f02e8953 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1262,6 +1262,7 @@ OBJS = \ incpath.o \ init-regs.o \ internal-fn.o \ + ipa-chkp.o \ ipa-cp.o \ ipa-devirt.o \ ipa-polymorphic-call.o \ @@ -1340,6 +1341,7 @@ OBJS = \ reload1.o \ reorg.o \ resource.o \ + rtl-chkp.o \ rtl-error.o \ rtl.o \ rtlhash.o \ @@ -1399,6 +1401,8 @@ OBJS = \ tree-outof-ssa.o \ tree-parloops.o \ tree-phinodes.o \ + tree-chkp.o \ + tree-chkp-opt.o \ tree-predcom.o \ tree-pretty-print.o \ tree-profile.o \ @@ -2282,6 +2286,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/stringpool.c $(srcdir)/tree.c $(srcdir)/varasm.c \ $(srcdir)/gimple.h \ $(srcdir)/gimple-ssa.h \ + $(srcdir)/tree-chkp.c \ $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \ $(srcdir)/tree-cfg.c \ $(srcdir)/tree-dfa.c \ diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 6434bf2f19e..84514404cf2 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -123,6 +123,8 @@ DEF_PRIMITIVE_TYPE (BT_I4, builtin_type_for_size (BITS_PER_UNIT*4, 1)) DEF_PRIMITIVE_TYPE (BT_I8, builtin_type_for_size (BITS_PER_UNIT*8, 1)) DEF_PRIMITIVE_TYPE (BT_I16, builtin_type_for_size (BITS_PER_UNIT*16, 1)) +DEF_PRIMITIVE_TYPE (BT_BND, pointer_bounds_type_node) + DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING) DEF_POINTER_TYPE (BT_PTR_LONG, BT_LONG) DEF_POINTER_TYPE (BT_PTR_ULONGLONG, BT_ULONGLONG) @@ -224,6 +226,10 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT16, BT_UINT16, BT_UINT16) DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT32, BT_UINT32, BT_UINT32) DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_UINT64, BT_UINT64, BT_UINT64) DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_INT, BT_BOOL, BT_INT) +DEF_FUNCTION_TYPE_1 (BT_FN_PTR_CONST_PTR, BT_PTR, BT_CONST_PTR) +DEF_FUNCTION_TYPE_1 (BT_FN_CONST_PTR_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR) +DEF_FUNCTION_TYPE_1 (BT_FN_BND_CONST_PTR, BT_BND, BT_CONST_PTR) +DEF_FUNCTION_TYPE_1 (BT_FN_CONST_PTR_BND, BT_CONST_PTR, BT_BND) DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR) @@ -337,6 +343,13 @@ DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_SIZE_CONST_VPTR, BT_BOOL, BT_SIZE, BT_CONST_VOLATILE_PTR) DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_INT_BOOL, BT_BOOL, BT_INT, BT_BOOL) DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT_UINT, BT_VOID, BT_UINT, BT_UINT) +DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_SIZE, BT_PTR, BT_CONST_PTR, BT_SIZE) +DEF_FUNCTION_TYPE_2 (BT_FN_PTR_CONST_PTR_CONST_PTR, BT_PTR, BT_CONST_PTR, BT_CONST_PTR) +DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTRPTR_CONST_PTR, BT_VOID, BT_PTR_PTR, BT_CONST_PTR) +DEF_FUNCTION_TYPE_2 (BT_FN_VOID_CONST_PTR_SIZE, BT_VOID, BT_CONST_PTR, BT_SIZE) +DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_BND, BT_VOID, BT_PTR, BT_BND) +DEF_FUNCTION_TYPE_2 (BT_FN_CONST_PTR_CONST_PTR_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR) +DEF_FUNCTION_TYPE_2 (BT_FN_BND_CONST_PTR_SIZE, BT_BND, BT_CONST_PTR, BT_SIZE) DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR) @@ -420,6 +433,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I4_INT, BT_VOID, BT_VOLATILE_PTR, BT_I4, BT DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I8_INT, BT_VOID, BT_VOLATILE_PTR, BT_I8, BT_INT) DEF_FUNCTION_TYPE_3 (BT_FN_VOID_VPTR_I16_INT, BT_VOID, BT_VOLATILE_PTR, BT_I16, BT_INT) DEF_FUNCTION_TYPE_3 (BT_FN_INT_PTRPTR_SIZE_SIZE, BT_INT, BT_PTR_PTR, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_3 (BT_FN_PTR_CONST_PTR_CONST_PTR_SIZE, BT_PTR, BT_CONST_PTR, BT_CONST_PTR, BT_SIZE) +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_CONST_PTR_BND_CONST_PTR, BT_VOID, BT_CONST_PTR, BT_BND, BT_CONST_PTR) DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR) diff --git a/gcc/builtins.c b/gcc/builtins.c index 161fe8c7b3b..71fbf09caa7 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -66,6 +66,11 @@ along with GCC; see the file COPYING3. If not see #include "asan.h" #include "ubsan.h" #include "cilk.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t)); @@ -4324,6 +4329,13 @@ std_expand_builtin_va_start (tree valist, rtx nextarg) { rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE); convert_move (va_r, nextarg, 0); + + /* We do not have any valid bounds for the pointer, so + just store zero bounds for it. */ + if (chkp_function_instrumented_p (current_function_decl)) + chkp_expand_bounds_reset_for_mem (valist, + make_tree (TREE_TYPE (valist), + nextarg)); } /* Expand EXP, a call to __builtin_va_start. */ @@ -5791,7 +5803,19 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, && fcode != BUILT_IN_EXECVE && fcode != BUILT_IN_ALLOCA && fcode != BUILT_IN_ALLOCA_WITH_ALIGN - && fcode != BUILT_IN_FREE) + && fcode != BUILT_IN_FREE + && fcode != BUILT_IN_CHKP_SET_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_INIT_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_NULL_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_COPY_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_NARROW_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_STORE_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_LBOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_UBOUNDS + && fcode != BUILT_IN_CHKP_CHECK_PTR_BOUNDS + && fcode != BUILT_IN_CHKP_GET_PTR_LBOUND + && fcode != BUILT_IN_CHKP_GET_PTR_UBOUND + && fcode != BUILT_IN_CHKP_BNDRET) return expand_call (exp, target, ignore); /* The built-in function expanders test for target == const0_rtx @@ -5825,6 +5849,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, } } + gcc_assert (!CALL_WITH_BOUNDS_P (exp)); + switch (fcode) { CASE_FLT_FN (BUILT_IN_FABS): @@ -6829,6 +6855,51 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, expand_builtin_cilk_pop_frame (exp); return const0_rtx; + case BUILT_IN_CHKP_INIT_PTR_BOUNDS: + case BUILT_IN_CHKP_NULL_PTR_BOUNDS: + case BUILT_IN_CHKP_COPY_PTR_BOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_LBOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_UBOUNDS: + case BUILT_IN_CHKP_CHECK_PTR_BOUNDS: + case BUILT_IN_CHKP_SET_PTR_BOUNDS: + case BUILT_IN_CHKP_NARROW_PTR_BOUNDS: + case BUILT_IN_CHKP_STORE_PTR_BOUNDS: + case BUILT_IN_CHKP_GET_PTR_LBOUND: + case BUILT_IN_CHKP_GET_PTR_UBOUND: + /* We allow user CHKP builtins if Pointer Bounds + Checker is off. */ + if (!chkp_function_instrumented_p (current_function_decl)) + { + if (fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NARROW_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS) + return expand_normal (CALL_EXPR_ARG (exp, 0)); + else if (fcode == BUILT_IN_CHKP_GET_PTR_LBOUND) + return expand_normal (size_zero_node); + else if (fcode == BUILT_IN_CHKP_GET_PTR_UBOUND) + return expand_normal (size_int (-1)); + else + return const0_rtx; + } + /* FALLTHROUGH */ + + case BUILT_IN_CHKP_BNDMK: + case BUILT_IN_CHKP_BNDSTX: + case BUILT_IN_CHKP_BNDCL: + case BUILT_IN_CHKP_BNDCU: + case BUILT_IN_CHKP_BNDLDX: + case BUILT_IN_CHKP_BNDRET: + case BUILT_IN_CHKP_INTERSECT: + case BUILT_IN_CHKP_NARROW: + case BUILT_IN_CHKP_EXTRACT_LOWER: + case BUILT_IN_CHKP_EXTRACT_UPPER: + /* Software implementation of Pointer Bounds Checker is NYI. + Target support is required. */ + error ("Your target platform does not support -fcheck-pointer-bounds"); + break; + default: /* just do library call, if unknown builtin */ break; } diff --git a/gcc/builtins.def b/gcc/builtins.def index cd823a3be49..552becc3571 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -183,6 +183,12 @@ along with GCC; see the file COPYING3. If not see DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, BT_FN_INT_VAR, BT_LAST, \ false, false, false, ATTRS, false, flag_cilkplus) +/* Builtin used by the implementation of Pointer Bounds Checker. */ +#undef DEF_CHKP_BUILTIN +#define DEF_CHKP_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ + DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ + true, true, false, ATTRS, true, true) + /* Define an attribute list for math functions that are normally "impure" because some of them may write into global memory for `errno'. If !flag_errno_math they are instead "const". */ @@ -878,3 +884,6 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST) /* Cilk Plus builtins. */ #include "cilkplus.def" + +/* Pointer Bounds Checker builtins. */ +#include "chkp-builtins.def" diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 532923cb798..ed723146ac6 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -391,6 +391,9 @@ static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int, static tree handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *); static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *); +static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *); +static tree handle_bnd_legacy (tree *, tree, tree, int, bool *); +static tree handle_bnd_instrument (tree *, tree, tree, int, bool *); static void check_function_nonnull (tree, int, tree *); static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); @@ -623,7 +626,12 @@ const struct c_common_resword c_common_reswords[] = const unsigned int num_c_common_reswords = sizeof c_common_reswords / sizeof (struct c_common_resword); -/* Table of machine-independent attributes common to all C-like languages. */ +/* Table of machine-independent attributes common to all C-like languages. + + All attributes referencing arguments should be additionally processed + in chkp_copy_function_type_adding_bounds for correct instrumentation + by Pointer Bounds Checker. + Current list of processed common attributes: nonnull. */ const struct attribute_spec c_common_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, @@ -790,12 +798,22 @@ const struct attribute_spec c_common_attribute_table[] = handle_assume_aligned_attribute, false }, { "designated_init", 0, 0, false, true, false, handle_designated_init_attribute, false }, + { "bnd_variable_size", 0, 0, true, false, false, + handle_bnd_variable_size_attribute, false }, + { "bnd_legacy", 0, 0, true, false, false, + handle_bnd_legacy, false }, + { "bnd_instrument", 0, 0, true, false, false, + handle_bnd_instrument, false }, { NULL, 0, 0, false, false, false, NULL, false } }; /* Give the specifications for the format attributes, used by C and all - descendants. */ + descendants. + All attributes referencing arguments should be additionally processed + in chkp_copy_function_type_adding_bounds for correct instrumentation + by Pointer Bounds Checker. + Current list of processed format attributes: format, format_arg. */ const struct attribute_spec c_common_format_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, @@ -8258,6 +8276,54 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name), return NULL_TREE; } +/* Handle a "bnd_variable_size" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_bnd_variable_size_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FIELD_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "bnd_legacy" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_bnd_legacy (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "bnd_instrument" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_bnd_instrument (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Handle a "warn_unused" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 4f96cf83d84..66c62fb6424 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -323,6 +323,10 @@ Wchar-subscripts C ObjC C++ ObjC++ Var(warn_char_subscripts) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) Warn about subscripts whose type is \"char\" +Wchkp +C ObjC C++ ObjC++ Var(warn_chkp) Warning EnabledBy(Wall) +Warn about memory access errors found by Pointer Bounds Checker + Wclobbered C ObjC C++ ObjC++ Var(warn_clobbered) Warning EnabledBy(Wextra) Warn about variables that might be changed by \"longjmp\" or \"vfork\" @@ -950,6 +954,84 @@ fcanonical-system-headers C ObjC C++ ObjC++ Where shorter, use canonicalized paths to systems headers. +fcheck-pointer-bounds +Common Report Var(flag_check_pointer_bounds) +Add Pointer Bounds Checker instrumentation. fchkp-* flags are used to +control instrumentation. Currently available for C, C++ and ObjC. + +fchkp-check-incomplete-type +C ObjC C++ ObjC++ Report Var(flag_chkp_incomplete_type) Init(1) +Generate pointer bounds checks for variables with incomplete type + +fchkp-zero-input-bounds-for-main +C ObjC C++ ObjC++ Report Var(flag_chkp_zero_input_bounds_for_main) Init(0) +Use zero bounds for all incoming arguments in 'main' function. It helps when +instrumented binaries are used with legacy libs. + +fchkp-first-field-has-own-bounds +C ObjC C++ ObjC++ RejectNegative Report Var(flag_chkp_first_field_has_own_bounds) +Forces Pointer Bounds Checker to use narrowed bounds for address of the first +field in the structure. By default pointer to the first field has the same +bounds as pointer to the whole structure. + +fchkp-narrow-bounds +C ObjC C++ ObjC++ Report Var(flag_chkp_narrow_bounds) Init(1) +Control how Pointer Bounds Checker handle pointers to object fields. When +narrowing is on, field bounds are used. Otherwise full object bounds are used. + +fchkp-narrow-to-innermost-array +C ObjC C++ ObjC++ RejectNegative Report Var(flag_chkp_narrow_to_innermost_arrray) +Forces Pointer Bounds Checker to use bounds of the innermost arrays in case of +nested static arryas access. By default outermost array is used. + +fchkp-optimize +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_optimize) Init(-1) +Allow Pointer Bounds Checker optimizations. By default allowed +on optimization levels >0. + +fchkp-use-fast-string-functions +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_use_fast_string_functions) Init(0) +Allow to use *_nobnd versions of string functions by Pointer Bounds Checker. + +fchkp-use-nochk-string-functions +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_use_nochk_string_functions) Init(0) +Allow to use *_nochk versions of string functions by Pointer Bounds Checker. + +fchkp-use-static-bounds +C ObjC C++ ObjC++ Report Var(flag_chkp_use_static_bounds) Init(1) +Use statically initialized variable for vars bounds instead of +generating them each time it is required. + +fchkp-use-static-const-bounds +C ObjC C++ ObjC++ Report Var(flag_chkp_use_static_const_bounds) Init(-1) +Use statically initialized variable for constant bounds instead of +generating them each time it is required. + +fchkp-treat-zero-dynamic-size-as-infinite +C ObjC C++ ObjC++ Report Var(flag_chkp_zero_dynamic_size_as_infinite) Init(0) +With this option zero size obtained dynamically for objects with +incomplete type will be treated as infinite. + +fchkp-check-read +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_check_read) Init(1) +Generate checks for all read accesses to memory. + +fchkp-check-write +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_check_write) Init(1) +Generate checks for all write accesses to memory. + +fchkp-store-bounds +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_store_bounds) Init(1) +Generate bounds stores for pointer writes. + +fchkp-instrument-calls +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_instrument_calls) Init(1) +Generate bounds passing for calls. + +fchkp-instrument-marked-only +C ObjC C++ ObjC++ LTO Report Var(flag_chkp_instrument_marked_only) Init(0) +Instrument only functions marked with bnd_instrument attribute. + fcilkplus C ObjC C++ ObjC++ LTO Report Var(flag_cilkplus) Init(0) Enable Cilk Plus diff --git a/gcc/calls.c b/gcc/calls.c index c62dba0cf7b..43ac5d2f6b3 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see #include "tm_p.h" #include "timevar.h" #include "sbitmap.h" +#include "bitmap.h" #include "langhooks.h" #include "target.h" #include "hash-map.h" @@ -61,6 +62,8 @@ along with GCC; see the file COPYING3. If not see #include "except.h" #include "dbgcnt.h" #include "rtl-iter.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */ #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) @@ -88,6 +91,15 @@ struct arg_data /* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct form for emit_group_move. */ rtx parallel_value; + /* If value is passed in neither reg nor stack, this field holds a number + of a special slot to be used. */ + rtx special_slot; + /* For pointer bounds hold an index of parm bounds are bound to. -1 if + there is no such pointer. */ + int pointer_arg; + /* If pointer_arg refers a structure, then pointer_offset holds an offset + of a pointer in this structure. */ + int pointer_offset; /* If REG was promoted from the actual mode of the argument expression, indicates whether the promotion is sign- or zero-extended. */ int unsignedp; @@ -145,6 +157,7 @@ static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT, HOST_WIDE_INT, rtx, rtx, int, rtx, int, cumulative_args_t); static void precompute_register_parameters (int, struct arg_data *, int *); +static void store_bounds (struct arg_data *, struct arg_data *); static int store_one_arg (struct arg_data *, rtx, int, int, int); static void store_unaligned_arguments_into_pseudos (struct arg_data *, int); static int finalize_must_preallocate (int, int, struct arg_data *, @@ -409,6 +422,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU && MEM_EXPR (funmem) != NULL_TREE) set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem)); + /* Mark instrumented calls. */ + if (call && fntree) + CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree); + /* Put the register usage information there. */ add_function_usage_to (call_insn, call_fusage); @@ -515,8 +532,16 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU static int special_function_p (const_tree fndecl, int flags) { - if (fndecl && DECL_NAME (fndecl) - && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17 + tree name_decl = DECL_NAME (fndecl); + + /* For instrumentation clones we want to derive flags + from the original name. */ + if (cgraph_node::get (fndecl) + && cgraph_node::get (fndecl)->instrumentation_clone) + name_decl = DECL_NAME (cgraph_node::get (fndecl)->orig_decl); + + if (fndecl && name_decl + && IDENTIFIER_LENGTH (name_decl) <= 17 /* Exclude functions not at the file scope, or not `extern', since they are not the magic functions we would otherwise think they are. @@ -528,16 +553,16 @@ special_function_p (const_tree fndecl, int flags) || TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL) && TREE_PUBLIC (fndecl)) { - const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl)); + const char *name = IDENTIFIER_POINTER (name_decl); const char *tname = name; /* We assume that alloca will always be called by name. It makes no sense to pass it as a pointer-to-function to anything that does not understand its behavior. */ - if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 + if (((IDENTIFIER_LENGTH (name_decl) == 6 && name[0] == 'a' && ! strcmp (name, "alloca")) - || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 + || (IDENTIFIER_LENGTH (name_decl) == 16 && name[0] == '_' && ! strcmp (name, "__builtin_alloca")))) flags |= ECF_MAY_BE_ALLOCA; @@ -1126,23 +1151,86 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args_size->constant = 0; args_size->var = 0; + bitmap_obstack_initialize (NULL); + /* In this loop, we consider args in the order they are written. We fill up ARGS from the back. */ i = num_actuals - 1; { - int j = i; + int j = i, ptr_arg = -1; call_expr_arg_iterator iter; tree arg; + bitmap slots = NULL; if (struct_value_addr_value) { args[j].tree_value = struct_value_addr_value; j--; + + /* If we pass structure address then we need to + create bounds for it. Since created bounds is + a call statement, we expand it right here to avoid + fixing all other places where it may be expanded. */ + if (CALL_WITH_BOUNDS_P (exp)) + { + args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ()); + args[j].tree_value + = chkp_make_bounds_for_struct_addr (struct_value_addr_value); + expand_expr_real (args[j].tree_value, args[j].value, VOIDmode, + EXPAND_NORMAL, 0, false); + args[j].pointer_arg = j + 1; + j--; + } } FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) { tree argtype = TREE_TYPE (arg); + + /* Remember last param with pointer and associate it + with following pointer bounds. */ + if (CALL_WITH_BOUNDS_P (exp) + && chkp_type_has_pointer (argtype)) + { + if (slots) + BITMAP_FREE (slots); + ptr_arg = j; + if (!BOUNDED_TYPE_P (argtype)) + { + slots = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (argtype, slots); + } + } + else if (POINTER_BOUNDS_TYPE_P (argtype)) + { + /* We expect bounds in instrumented calls only. + Otherwise it is a sign we lost flag due to some optimization + and may emit call args incorrectly. */ + gcc_assert (CALL_WITH_BOUNDS_P (exp)); + + /* For structures look for the next available pointer. */ + if (ptr_arg != -1 && slots) + { + unsigned bnd_no = bitmap_first_set_bit (slots); + args[j].pointer_offset = + bnd_no * POINTER_SIZE / BITS_PER_UNIT; + + bitmap_clear_bit (slots, bnd_no); + + /* Check we have no more pointers in the structure. */ + if (bitmap_empty_p (slots)) + BITMAP_FREE (slots); + } + args[j].pointer_arg = ptr_arg; + + /* Check we covered all pointers in the previous + non bounds arg. */ + if (!slots) + ptr_arg = -1; + } + else + ptr_arg = -1; + if (targetm.calls.split_complex_arg && argtype && TREE_CODE (argtype) == COMPLEX_TYPE @@ -1157,8 +1245,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[j].tree_value = arg; j--; } + + if (slots) + BITMAP_FREE (slots); } + bitmap_obstack_release (NULL); + /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ for (argpos = 0; argpos < num_actuals; i--, argpos++) { @@ -1292,6 +1385,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[i].reg = targetm.calls.function_arg (args_so_far, mode, type, argpos < n_named_args); + if (args[i].reg && CONST_INT_P (args[i].reg)) + { + args[i].special_slot = args[i].reg; + args[i].reg = NULL; + } + /* If this is a sibling call and the machine has register windows, the register window has to be unwinded before calling the routine, so arguments have to go into the incoming registers. */ @@ -1325,10 +1424,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, || (args[i].pass_on_stack && args[i].reg != 0)) *must_preallocate = 1; + /* No stack allocation and padding for bounds. */ + if (POINTER_BOUNDS_P (args[i].tree_value)) + ; /* Compute the stack-size of this argument. */ - if (args[i].reg == 0 || args[i].partial != 0 - || reg_parm_stack_space > 0 - || args[i].pass_on_stack) + else if (args[i].reg == 0 || args[i].partial != 0 + || reg_parm_stack_space > 0 + || args[i].pass_on_stack) locate_and_pad_parm (mode, type, #ifdef STACK_PARMS_IN_REG_PARM_AREA 1, @@ -1542,6 +1644,12 @@ finalize_must_preallocate (int must_preallocate, int num_actuals, partial_seen = 1; else if (partial_seen && args[i].reg == 0) must_preallocate = 1; + /* We preallocate in case there are bounds passed + in the bounds table to have precomputed address + for bounds association. */ + else if (POINTER_BOUNDS_P (args[i].tree_value) + && !args[i].reg) + must_preallocate = 1; if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode && (TREE_CODE (args[i].tree_value) == CALL_EXPR @@ -1593,6 +1701,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals && args[i].partial == 0) continue; + /* Pointer Bounds are never passed on the stack. */ + if (POINTER_BOUNDS_P (args[i].tree_value)) + continue; + if (CONST_INT_P (offset)) addr = plus_constant (Pmode, arg_reg, INTVAL (offset)); else @@ -2215,6 +2327,8 @@ expand_call (tree exp, rtx target, int ignore) /* Register in which non-BLKmode value will be returned, or 0 if no value or if value is BLKmode. */ rtx valreg; + /* Register(s) in which bounds are returned. */ + rtx valbnd = NULL; /* Address where we should return a BLKmode value; 0 if value not BLKmode. */ rtx structure_value_addr = 0; @@ -2473,7 +2587,7 @@ expand_call (tree exp, rtx target, int ignore) structure_value_addr_value = make_tree (build_pointer_type (TREE_TYPE (funtype)), temp); - structure_value_addr_parm = 1; + structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1; } /* Count the arguments and set NUM_ACTUALS. */ @@ -2991,15 +3105,28 @@ expand_call (tree exp, rtx target, int ignore) /* Figure out the register where the value, if any, will come back. */ valreg = 0; + valbnd = 0; if (TYPE_MODE (rettype) != VOIDmode && ! structure_value_addr) { if (pcc_struct_value) - valreg = hard_function_value (build_pointer_type (rettype), - fndecl, NULL, (pass == 0)); + { + valreg = hard_function_value (build_pointer_type (rettype), + fndecl, NULL, (pass == 0)); + if (CALL_WITH_BOUNDS_P (exp)) + valbnd = targetm.calls. + chkp_function_value_bounds (build_pointer_type (rettype), + fndecl, (pass == 0)); + } else - valreg = hard_function_value (rettype, fndecl, fntype, - (pass == 0)); + { + valreg = hard_function_value (rettype, fndecl, fntype, + (pass == 0)); + if (CALL_WITH_BOUNDS_P (exp)) + valbnd = targetm.calls.chkp_function_value_bounds (rettype, + fndecl, + (pass == 0)); + } /* If VALREG is a PARALLEL whose first member has a zero offset, use that. This is for targets such as m68k that @@ -3040,7 +3167,10 @@ expand_call (tree exp, rtx target, int ignore) for (i = 0; i < num_actuals; i++) { - if (args[i].reg == 0 || args[i].pass_on_stack) + /* Delay bounds until all other args are stored. */ + if (POINTER_BOUNDS_P (args[i].tree_value)) + continue; + else if (args[i].reg == 0 || args[i].pass_on_stack) { rtx_insn *before_arg = get_last_insn (); @@ -3093,6 +3223,17 @@ expand_call (tree exp, rtx target, int ignore) sibcall_failure = 1; } + /* Store all bounds not passed in registers. */ + for (i = 0; i < num_actuals; i++) + { + if (POINTER_BOUNDS_P (args[i].tree_value) + && !args[i].reg) + store_bounds (&args[i], + args[i].pointer_arg == -1 + ? NULL + : &args[args[i].pointer_arg]); + } + /* If register arguments require space on the stack and stack space was not preallocated, allocate stack space here for arguments passed in registers. */ @@ -3497,6 +3638,9 @@ expand_call (tree exp, rtx target, int ignore) free (stack_usage_map_buf); + /* Join result with returned bounds so caller may use them if needed. */ + target = chkp_join_splitted_slot (target, valbnd); + return target; } @@ -4366,6 +4510,68 @@ emit_library_call_value (rtx orgfun, rtx value, return result; } + +/* Store pointer bounds argument ARG into Bounds Table entry + associated with PARM. */ +static void +store_bounds (struct arg_data *arg, struct arg_data *parm) +{ + rtx slot = NULL, ptr = NULL, addr = NULL; + + /* We may pass bounds not associated with any pointer. */ + if (!parm) + { + gcc_assert (arg->special_slot); + slot = arg->special_slot; + ptr = const0_rtx; + } + /* Find pointer associated with bounds and where it is + passed. */ + else + { + if (!parm->reg) + { + gcc_assert (!arg->special_slot); + + addr = adjust_address (parm->stack, Pmode, arg->pointer_offset); + } + else if (REG_P (parm->reg)) + { + gcc_assert (arg->special_slot); + slot = arg->special_slot; + + if (MEM_P (parm->value)) + addr = adjust_address (parm->value, Pmode, arg->pointer_offset); + else if (REG_P (parm->value)) + ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset); + else + { + gcc_assert (!arg->pointer_offset); + ptr = parm->value; + } + } + else + { + gcc_assert (GET_CODE (parm->reg) == PARALLEL); + + gcc_assert (arg->special_slot); + slot = arg->special_slot; + + if (parm->parallel_value) + ptr = chkp_get_value_with_offs (parm->parallel_value, + GEN_INT (arg->pointer_offset)); + else + gcc_unreachable (); + } + } + + /* Expand bounds. */ + if (!arg->value) + arg->value = expand_normal (arg->tree_value); + + targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot); +} + /* Store a single argument for a function call into the register or memory area where it must be passed. *ARG describes the argument value and where to pass it. diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index d338a7c3757..2df8ce3ba84 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -90,6 +90,8 @@ along with GCC; see the file COPYING3. If not see #include "recog.h" #include "output.h" #include "builtins.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" /* Some systems use __main in a way incompatible with its use in gcc, in these cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to @@ -2316,6 +2318,7 @@ expand_call_stmt (gimple stmt) CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt); CALL_EXPR_VA_ARG_PACK (exp) = gimple_call_va_arg_pack_p (stmt); SET_EXPR_LOCATION (exp, gimple_location (stmt)); + CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt); /* Ensure RTL is created for debug args. */ if (decl && DECL_HAS_DEBUG_ARGS_P (decl)) @@ -3126,11 +3129,12 @@ expand_value_return (rtx val) from the current function. */ static void -expand_return (tree retval) +expand_return (tree retval, tree bounds) { rtx result_rtl; rtx val = 0; tree retval_rhs; + rtx bounds_rtl; /* If function wants no value, give it none. */ if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE) @@ -3156,6 +3160,56 @@ expand_return (tree retval) result_rtl = DECL_RTL (DECL_RESULT (current_function_decl)); + /* Put returned bounds to the right place. */ + bounds_rtl = DECL_BOUNDS_RTL (DECL_RESULT (current_function_decl)); + if (bounds_rtl) + { + rtx addr, bnd; + + if (bounds) + { + bnd = expand_normal (bounds); + targetm.calls.store_returned_bounds (bounds_rtl, bnd); + } + else if (REG_P (bounds_rtl)) + { + addr = expand_normal (build_fold_addr_expr (retval_rhs)); + addr = gen_rtx_MEM (Pmode, addr); + bnd = targetm.calls.load_bounds_for_arg (addr, NULL, NULL); + targetm.calls.store_returned_bounds (bounds_rtl, bnd); + } + else + { + int n; + + gcc_assert (GET_CODE (bounds_rtl) == PARALLEL); + + addr = expand_normal (build_fold_addr_expr (retval_rhs)); + addr = gen_rtx_MEM (Pmode, addr); + + for (n = 0; n < XVECLEN (bounds_rtl, 0); n++) + { + rtx offs = XEXP (XVECEXP (bounds_rtl, 0, n), 1); + rtx slot = XEXP (XVECEXP (bounds_rtl, 0, n), 0); + rtx from = adjust_address (addr, Pmode, INTVAL (offs)); + rtx bnd = targetm.calls.load_bounds_for_arg (from, NULL, NULL); + targetm.calls.store_returned_bounds (slot, bnd); + } + } + } + else if (chkp_function_instrumented_p (current_function_decl) + && !BOUNDED_P (retval_rhs) + && chkp_type_has_pointer (TREE_TYPE (retval_rhs)) + && TREE_CODE (retval_rhs) != RESULT_DECL) + { + rtx addr = expand_normal (build_fold_addr_expr (retval_rhs)); + addr = gen_rtx_MEM (Pmode, addr); + + gcc_assert (MEM_P (result_rtl)); + + chkp_copy_bounds_for_stack_parm (result_rtl, addr, TREE_TYPE (retval_rhs)); + } + /* If we are returning the RESULT_DECL, then the value has already been stored into it, so we don't have to do anything special. */ if (TREE_CODE (retval_rhs) == RESULT_DECL) @@ -3261,7 +3315,7 @@ expand_gimple_stmt_1 (gimple stmt) if (!op0) expand_null_return (); else - expand_return (op0); + expand_return (op0, gimple_return_retbnd (stmt)); break; case GIMPLE_ASSIGN: @@ -5654,6 +5708,9 @@ pass_expand::execute (function *fun) rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (fun)); + if (chkp_function_instrumented_p (current_function_decl)) + chkp_reset_rtl_bounds (); + insn_locations_init (); if (!DECL_IS_BUILTIN (current_function_decl)) { diff --git a/gcc/cgraph.c b/gcc/cgraph.c index d430bc5524d..7216b897184 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -80,6 +80,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-dfa.h" #include "profile.h" #include "params.h" +#include "tree-chkp.h" /* FIXME: Only for PROP_loops, but cgraph shouldn't have to know about this. */ #include "tree-pass.h" @@ -1344,6 +1345,33 @@ cgraph_edge::redirect_call_stmt_to_callee (void) e->speculative = false; e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt, false); + + /* Fix edges for BUILT_IN_CHKP_BNDRET calls attached to the + processed call stmt. */ + if (gimple_call_with_bounds_p (new_stmt) + && gimple_call_lhs (new_stmt) + && chkp_retbnd_call_by_val (gimple_call_lhs (e2->call_stmt))) + { + tree dresult = gimple_call_lhs (new_stmt); + tree iresult = gimple_call_lhs (e2->call_stmt); + gimple dbndret = chkp_retbnd_call_by_val (dresult); + gimple ibndret = chkp_retbnd_call_by_val (iresult); + struct cgraph_edge *iedge + = e2->caller->cgraph_node::get_edge (ibndret); + struct cgraph_edge *dedge; + + if (dbndret) + { + dedge = iedge->caller->create_edge (iedge->callee, + dbndret, e->count, + e->frequency); + dedge->frequency = compute_call_stmt_bb_frequency + (dedge->caller->decl, gimple_bb (dedge->call_stmt)); + } + iedge->frequency = compute_call_stmt_bb_frequency + (iedge->caller->decl, gimple_bb (iedge->call_stmt)); + } + e->frequency = compute_call_stmt_bb_frequency (e->caller->decl, gimple_bb (e->call_stmt)); e2->frequency = compute_call_stmt_bb_frequency @@ -1776,6 +1804,12 @@ cgraph_node::remove (void) call_site_hash = NULL; } + if (instrumented_version) + { + instrumented_version->instrumented_version = NULL; + instrumented_version = NULL; + } + symtab->release_symbol (this, uid); } @@ -2027,6 +2061,11 @@ cgraph_node::dump (FILE *f) if (edge->indirect_info->polymorphic) edge->indirect_info->context.dump (f); } + + if (instrumentation_clone) + fprintf (f, " Is instrumented version.\n"); + else if (instrumented_version) + fprintf (f, " Has instrumented version.\n"); } /* Dump call graph node NODE to stderr. */ @@ -2389,6 +2428,12 @@ bool cgraph_node::can_remove_if_no_direct_calls_and_refs_p (void) { gcc_assert (!global.inlined_to); + /* Instrumentation clones should not be removed before + instrumentation happens. New callers may appear after + instrumentation. */ + if (instrumentation_clone + && !chkp_function_instrumented_p (decl)) + return false; /* Extern inlines can always go, we will use the external definition. */ if (DECL_EXTERNAL (decl)) return true; @@ -2825,7 +2870,9 @@ cgraph_node::verify_node (void) error_found = true; } for (i = 0; iterate_reference (i, ref); i++) - if (ref->use != IPA_REF_ALIAS) + if (ref->use == IPA_REF_CHKP) + ; + else if (ref->use != IPA_REF_ALIAS) { error ("Alias has non-alias reference"); error_found = true; @@ -2843,6 +2890,64 @@ cgraph_node::verify_node (void) error_found = true; } } + + /* Check instrumented version reference. */ + if (instrumented_version + && instrumented_version->instrumented_version != this) + { + error ("Instrumentation clone does not reference original node"); + error_found = true; + } + + /* Cannot have orig_decl for not instrumented nodes. */ + if (!instrumentation_clone && orig_decl) + { + error ("Not instrumented node has non-NULL original declaration"); + error_found = true; + } + + /* If original not instrumented node still exists then we may check + original declaration is set properly. */ + if (instrumented_version + && orig_decl + && orig_decl != instrumented_version->decl) + { + error ("Instrumented node has wrong original declaration"); + error_found = true; + } + + /* Check all nodes have chkp reference to their instrumented versions. */ + if (analyzed + && instrumented_version + && !instrumentation_clone) + { + bool ref_found = false; + int i; + struct ipa_ref *ref; + + for (i = 0; iterate_reference (i, ref); i++) + if (ref->use == IPA_REF_CHKP) + { + if (ref_found) + { + error ("Node has more than one chkp reference"); + error_found = true; + } + if (ref->referred != instrumented_version) + { + error ("Wrong node is referenced with chkp reference"); + error_found = true; + } + ref_found = true; + } + + if (!ref_found) + { + error ("Analyzed node has no reference to instrumented version"); + error_found = true; + } + } + if (analyzed && thunk.thunk_p) { if (!callees) @@ -2860,6 +2965,12 @@ cgraph_node::verify_node (void) error ("Thunk is not supposed to have body"); error_found = true; } + if (thunk.add_pointer_bounds_args + && !instrumented_version->semantically_equivalent_p (callees->callee)) + { + error ("Instrumentation thunk has wrong edge callee"); + error_found = true; + } } else if (analyzed && gimple_has_body_p (decl) && !TREE_ASM_WRITTEN (decl) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index ae73f075cce..e2becb96d56 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -543,6 +543,7 @@ struct GTY(()) cgraph_thunk_info { tree alias; bool this_adjusting; bool virtual_offset_p; + bool add_pointer_bounds_args; /* Set to true when alias node is thunk. */ bool thunk_p; }; @@ -1187,6 +1188,13 @@ public: cgraph_node *prev_sibling_clone; cgraph_node *clones; cgraph_node *clone_of; + /* If instrumentation_clone is 1 then instrumented_version points + to the original function used to make instrumented version. + Otherwise points to instrumented version of the function. */ + cgraph_node *instrumented_version; + /* If instrumentation_clone is 1 then orig_decl is the original + function declaration. */ + tree orig_decl; /* For functions with many calls sites it holds map from call expression to the edge to speed up cgraph_edge function. */ hash_table<cgraph_edge_hasher> *GTY(()) call_site_hash; @@ -1249,6 +1257,9 @@ public: unsigned calls_comdat_local : 1; /* True if node has been created by merge operation in IPA-ICF. */ unsigned icf_merged: 1; + /* True when function is clone created for Pointer Bounds Checker + instrumentation. */ + unsigned instrumentation_clone : 1; }; /* A cgraph node set is a collection of cgraph nodes. A cgraph node @@ -1658,6 +1669,10 @@ public: /* Set when variable is scheduled to be assembled. */ unsigned output : 1; + /* Set when variable has statically initialized pointer + or is a static bounds variable and needs initalization. */ + unsigned need_bounds_init : 1; + /* Set if the variable is dynamically initialized, except for function local statics. */ unsigned dynamically_initialized : 1; @@ -2181,6 +2196,8 @@ symtab_node::get_alias_target (void) { ipa_ref *ref = NULL; iterate_reference (0, ref); + if (ref->use == IPA_REF_CHKP) + iterate_reference (1, ref); gcc_checking_assert (ref->use == IPA_REF_ALIAS); return ref->referred; } @@ -2756,4 +2773,17 @@ ipa_polymorphic_call_context::useless_p () const { return (!outer_type && !speculative_outer_type); } + +/* Return true if NODE is local. Instrumentation clones are counted as local + only when original function is local. */ + +static inline bool +cgraph_local_p (cgraph_node *node) +{ + if (!node->instrumentation_clone || !node->instrumented_version) + return node->local.local; + + return node->local.local && node->instrumented_version->local.local; +} + #endif /* GCC_CGRAPH_H */ diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c index 51ec01bf032..c72ceab3c92 100644 --- a/gcc/cgraphbuild.c +++ b/gcc/cgraphbuild.c @@ -472,6 +472,10 @@ cgraph_edge::rebuild_edges (void) record_eh_tables (node, cfun); gcc_assert (!node->global.inlined_to); + if (node->instrumented_version + && !node->instrumentation_clone) + node->create_reference (node->instrumented_version, IPA_REF_CHKP, NULL); + return 0; } @@ -504,6 +508,10 @@ cgraph_edge::rebuild_references (void) node->record_stmt_references (gsi_stmt (gsi)); } record_eh_tables (node, cfun); + + if (node->instrumented_version + && !node->instrumentation_clone) + node->create_reference (node->instrumented_version, IPA_REF_CHKP, NULL); } namespace { diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 6f61f5c339f..d2e88474594 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -223,6 +223,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-nested.h" #include "gimplify.h" #include "dbgcnt.h" +#include "tree-chkp.h" /* Queue of cgraph nodes scheduled to be added into cgraph. This is a secondary queue used during optimization to accommodate passes that @@ -802,6 +803,9 @@ varpool_node::finalize_decl (tree decl) || (!flag_toplevel_reorder && symtab->state == EXPANSION)) node->assemble_decl (); + + if (DECL_INITIAL (decl)) + chkp_register_var_initializer (decl); } /* EDGE is an polymorphic call. Mark all possible targets as reachable @@ -875,6 +879,11 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets, edge->make_direct (target); edge->redirect_call_stmt_to_callee (); + + /* Call to __builtin_unreachable shouldn't be instrumented. */ + if (!targets.length ()) + gimple_call_set_with_bounds (edge->call_stmt, false); + if (symtab->dump_file) { fprintf (symtab->dump_file, @@ -1584,6 +1593,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk) call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs); callees->call_stmt = call; gimple_call_set_from_thunk (call, true); + gimple_call_set_with_bounds (call, instrumentation_clone); if (restmp) { gimple_call_set_lhs (call, restmp); @@ -1680,7 +1690,8 @@ cgraph_node::assemble_thunks_and_aliases (void) ipa_ref *ref; for (e = callers; e;) - if (e->caller->thunk.thunk_p) + if (e->caller->thunk.thunk_p + && !e->caller->thunk.add_pointer_bounds_args) { cgraph_node *thunk = e->caller; @@ -2087,9 +2098,13 @@ void symbol_table::output_weakrefs (void) { symtab_node *node; + cgraph_node *cnode; FOR_EACH_SYMBOL (node) if (node->alias && !TREE_ASM_WRITTEN (node->decl) + && (!(cnode = dyn_cast <cgraph_node *> (node)) + || !cnode->instrumented_version + || !TREE_ASM_WRITTEN (cnode->instrumented_version->decl)) && node->weakref) { tree target; diff --git a/gcc/chkp-builtins.def b/gcc/chkp-builtins.def new file mode 100644 index 00000000000..cae03323042 --- /dev/null +++ b/gcc/chkp-builtins.def @@ -0,0 +1,71 @@ +/* This file contains the definitions and documentation for the + builtins used in the GNU compiler. + Copyright (C) 2013 Free Software Foundation, Inc. + +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/>. */ + +/* Before including this file, you should define macros: + + DEF_BUILTIN_STUB(ENUM, NAME) + DEF_CHKP_BUILTIN(ENUM, NAME, TYPE, ATTRS) + + See builtins.def for details. */ + +/* Following builtins are used by compiler for Pointer Bounds Checker + instrumentation. Currently these generic builtins are not + implemented and target has to provide his own version. See + builtin_chkp_function target hook documentation for more details. */ +DEF_BUILTIN_STUB (BUILT_IN_CHKP_INTERSECT, "__chkp_intersect") +DEF_BUILTIN_STUB (BUILT_IN_CHKP_SIZEOF, "__chkp_sizeof") +DEF_BUILTIN_STUB (BUILT_IN_CHKP_NARROW, "__chkp_narrow") +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDCL, "__chkp_bndcl", BT_FN_VOID_PTR_BND, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDCU, "__chkp_bndcu", BT_FN_VOID_PTR_BND, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDSTX, "__chkp_bndstx", BT_FN_VOID_CONST_PTR_BND_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDLDX, "__chkp_bndldx", BT_FN_CONST_PTR_CONST_PTR_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDRET, "__chkp_bndret", BT_FN_BND_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_BNDMK, "__chkp_bndmk", BT_FN_BND_CONST_PTR_SIZE, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_EXTRACT_LOWER, "__chkp_extract_lower", BT_FN_CONST_PTR_BND, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_EXTRACT_UPPER, "__chkp_extract_upper", BT_FN_CONST_PTR_BND, ATTR_CONST_NOTHROW_LEAF_LIST) + +/* Pointer Bounds Checker builtins for users. + All builtins calls are expanded in the + Pointer Bounds Checker pass. */ +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_SET_PTR_BOUNDS, "__bnd_set_ptr_bounds", BT_FN_PTR_CONST_PTR_SIZE, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_INIT_PTR_BOUNDS, "__bnd_init_ptr_bounds", BT_FN_PTR_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_NULL_PTR_BOUNDS, "__bnd_null_ptr_bounds", BT_FN_PTR_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_COPY_PTR_BOUNDS, "__bnd_copy_ptr_bounds", BT_FN_PTR_CONST_PTR_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_NARROW_PTR_BOUNDS, "__bnd_narrow_ptr_bounds", BT_FN_PTR_CONST_PTR_CONST_PTR_SIZE, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_STORE_PTR_BOUNDS, "__bnd_store_ptr_bounds", BT_FN_VOID_PTRPTR_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_CHECK_PTR_LBOUNDS, "__bnd_chk_ptr_lbounds", BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_CHECK_PTR_UBOUNDS, "__bnd_chk_ptr_ubounds", BT_FN_VOID_CONST_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_CHECK_PTR_BOUNDS, "__bnd_chk_ptr_bounds", BT_FN_VOID_CONST_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_GET_PTR_LBOUND, "__bnd_get_ptr_lbound", BT_FN_CONST_PTR_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_GET_PTR_UBOUND, "__bnd_get_ptr_ubound", BT_FN_CONST_PTR_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST) + +/* Pointer Bounds Checker specific versions of string functions. */ +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMCPY_NOBND, "chkp_memcpy_nobnd", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMCPY_NOCHK, "chkp_memcpy_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK, "chkp_memcpy_nobnd_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMMOVE_NOBND, "chkp_memmove_nobnd", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMMOVE_NOCHK, "chkp_memmove_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK, "chkp_memmove_nobnd_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMPCPY_NOBND, "chkp_mempcpy_nobnd", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMPCPY_NOCHK, "chkp_mempcpy_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK, "chkp_mempcpy_nobnd_nochk", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMSET_NOBND, "chkp_memset_nobnd", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMSET_NOCHK, "chkp_memset_nochk", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_CHKP_BUILTIN (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK, "chkp_memset_nobnd_nochk", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md index 8e0a58355ec..4e07d705001 100644 --- a/gcc/config/i386/constraints.md +++ b/gcc/config/i386/constraints.md @@ -19,7 +19,7 @@ ;;; Unused letters: ;;; H -;;; h j w z +;;; h j z ;; Integer register constraints. ;; It is not necessary to define 'r' here. @@ -94,6 +94,9 @@ (define_register_constraint "v" "TARGET_SSE ? ALL_SSE_REGS : NO_REGS" "Any EVEX encodable SSE register (@code{%xmm0-%xmm31}).") +(define_register_constraint "w" "TARGET_MPX ? BND_REGS : NO_REGS" + "@internal Any bound register.") + ;; We use the Y prefix to denote any number of conditional register sets: ;; z First SSE register. ;; i SSE2 inter-unit moves to SSE register enabled @@ -253,6 +256,8 @@ ;; T prefix is used for different address constraints ;; v - VSIB address ;; s - address with no segment register +;; i - address with no index and no rip +;; b - address with no base and no rip (define_address_constraint "Tv" "VSIB address operand" @@ -261,3 +266,11 @@ (define_address_constraint "Ts" "Address operand without segment register" (match_operand 0 "address_no_seg_operand")) + +(define_address_constraint "Ti" + "MPX address operand without index" + (match_operand 0 "address_mpx_no_index_operand")) + +(define_address_constraint "Tb" + "MPX address operand without base" + (match_operand 0 "address_mpx_no_base_operand")) diff --git a/gcc/config/i386/i386-builtin-types.def b/gcc/config/i386/i386-builtin-types.def index 8315c5e5f06..864d0ea23a7 100644 --- a/gcc/config/i386/i386-builtin-types.def +++ b/gcc/config/i386/i386-builtin-types.def @@ -47,6 +47,7 @@ DEF_PRIMITIVE_TYPE (UCHAR, unsigned_char_type_node) DEF_PRIMITIVE_TYPE (QI, char_type_node) DEF_PRIMITIVE_TYPE (HI, intHI_type_node) DEF_PRIMITIVE_TYPE (SI, intSI_type_node) +DEF_PRIMITIVE_TYPE (BND, pointer_bounds_type_node) # ??? Logically this should be intDI_type_node, but that maps to "long" # with 64-bit, and that's not how the emmintrin.h is written. Again, # changing this would change name mangling. @@ -61,6 +62,7 @@ DEF_PRIMITIVE_TYPE (USHORT, short_unsigned_type_node) DEF_PRIMITIVE_TYPE (INT, integer_type_node) DEF_PRIMITIVE_TYPE (UINT, unsigned_type_node) DEF_PRIMITIVE_TYPE (UNSIGNED, unsigned_type_node) +DEF_PRIMITIVE_TYPE (ULONG, long_unsigned_type_node) DEF_PRIMITIVE_TYPE (LONGLONG, long_long_integer_type_node) DEF_PRIMITIVE_TYPE (ULONGLONG, long_long_unsigned_type_node) DEF_PRIMITIVE_TYPE (UINT8, unsigned_char_type_node) @@ -1242,3 +1244,15 @@ DEF_FUNCTION_TYPE_ALIAS (V2DI_FTYPE_V2DI_V2DI, TF) DEF_FUNCTION_TYPE_ALIAS (V4SF_FTYPE_V4SF_V4SF, TF) DEF_FUNCTION_TYPE_ALIAS (V4SI_FTYPE_V4SI_V4SI, TF) DEF_FUNCTION_TYPE_ALIAS (V8HI_FTYPE_V8HI_V8HI, TF) + +# MPX builtins +DEF_FUNCTION_TYPE (BND, PCVOID, ULONG) +DEF_FUNCTION_TYPE (VOID, PCVOID, BND) +DEF_FUNCTION_TYPE (VOID, PCVOID, BND, PCVOID) +DEF_FUNCTION_TYPE (BND, PCVOID, PCVOID) +DEF_FUNCTION_TYPE (BND, PCVOID) +DEF_FUNCTION_TYPE (BND, BND, BND) +DEF_FUNCTION_TYPE (PVOID, PVOID, PVOID, ULONG) +DEF_FUNCTION_TYPE (PVOID, PCVOID, BND, ULONG) +DEF_FUNCTION_TYPE (ULONG, VOID) +DEF_FUNCTION_TYPE (PVOID, BND) diff --git a/gcc/config/i386/i386-c.c b/gcc/config/i386/i386-c.c index a3858edb240..0a0775dbbcc 100644 --- a/gcc/config/i386/i386-c.c +++ b/gcc/config/i386/i386-c.c @@ -405,6 +405,8 @@ ix86_target_macros_internal (HOST_WIDE_INT isa_flag, def_or_undef (parse_in, "__XSAVEC__"); if (isa_flag & OPTION_MASK_ISA_XSAVES) def_or_undef (parse_in, "__XSAVES__"); + if (isa_flag & OPTION_MASK_ISA_MPX) + def_or_undef (parse_in, "__MPX__"); } diff --git a/gcc/config/i386/i386-modes.def b/gcc/config/i386/i386-modes.def index c24abe6fea7..37752386cb5 100644 --- a/gcc/config/i386/i386-modes.def +++ b/gcc/config/i386/i386-modes.def @@ -90,6 +90,9 @@ VECTOR_MODE (INT, QI, 12); /* V12QI */ VECTOR_MODE (INT, QI, 14); /* V14QI */ VECTOR_MODE (INT, HI, 6); /* V6HI */ +POINTER_BOUNDS_MODE (BND32, 8); +POINTER_BOUNDS_MODE (BND64, 16); + INT_MODE (OI, 32); INT_MODE (XI, 64); diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index c9cee996e2b..d14a107e8c2 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -232,6 +232,8 @@ extern void ix86_expand_sse2_mulv4si3 (rtx, rtx, rtx); extern void ix86_expand_sse2_mulvxdi3 (rtx, rtx, rtx); extern void ix86_expand_sse2_abs (rtx, rtx); +extern bool ix86_bnd_prefixed_insn_p (rtx); + /* In i386-c.c */ extern void ix86_target_macros (void); extern void ix86_register_pragmas (void); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index c528599f868..8f03aa25e1e 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -99,6 +99,9 @@ along with GCC; see the file COPYING3. If not see #include "shrink-wrap.h" #include "builtins.h" #include "rtl-iter.h" +#include "tree-iterator.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" static rtx legitimize_dllimport_symbol (rtx, bool); static rtx legitimize_pe_coff_extern_decl (rtx, bool); @@ -2145,6 +2148,8 @@ enum reg_class const regclass_map[FIRST_PSEUDO_REGISTER] = /* Mask registers. */ MASK_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, MASK_EVEX_REGS, + /* MPX bound registers */ + BND_REGS, BND_REGS, BND_REGS, BND_REGS, }; /* The "default" register map used in 32bit mode. */ @@ -2161,6 +2166,7 @@ int const dbx_register_map[FIRST_PSEUDO_REGISTER] = -1, -1, -1, -1, -1, -1, -1, -1, /* AVX-512 registers 16-23*/ -1, -1, -1, -1, -1, -1, -1, -1, /* AVX-512 registers 24-31*/ 93, 94, 95, 96, 97, 98, 99, 100, /* Mask registers */ + 101, 102, 103, 104, /* bound registers */ }; /* The "default" register map used in 64bit mode. */ @@ -2177,6 +2183,7 @@ int const dbx64_register_map[FIRST_PSEUDO_REGISTER] = 67, 68, 69, 70, 71, 72, 73, 74, /* AVX-512 registers 16-23 */ 75, 76, 77, 78, 79, 80, 81, 82, /* AVX-512 registers 24-31 */ 118, 119, 120, 121, 122, 123, 124, 125, /* Mask registers */ + 126, 127, 128, 129, /* bound registers */ }; /* Define the register numbers to be used in Dwarf debugging information. @@ -2245,6 +2252,7 @@ int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER] = -1, -1, -1, -1, -1, -1, -1, -1, /* AVX-512 registers 16-23*/ -1, -1, -1, -1, -1, -1, -1, -1, /* AVX-512 registers 24-31*/ 93, 94, 95, 96, 97, 98, 99, 100, /* Mask registers */ + 101, 102, 103, 104, /* bound registers */ }; /* Define parameter passing and return registers. */ @@ -2646,6 +2654,7 @@ ix86_target_string (HOST_WIDE_INT isa, int flags, const char *arch, { "-mclflushopt", OPTION_MASK_ISA_CLFLUSHOPT }, { "-mxsavec", OPTION_MASK_ISA_XSAVEC }, { "-mxsaves", OPTION_MASK_ISA_XSAVES }, + { "-mmpx", OPTION_MASK_ISA_MPX }, }; /* Flag options. */ @@ -3135,6 +3144,7 @@ ix86_option_override_internal (bool main_args_p, #define PTA_AVX512ER (HOST_WIDE_INT_1 << 41) #define PTA_AVX512PF (HOST_WIDE_INT_1 << 42) #define PTA_AVX512CD (HOST_WIDE_INT_1 << 43) +#define PTA_MPX (HOST_WIDE_INT_1 << 44) #define PTA_SHA (HOST_WIDE_INT_1 << 45) #define PTA_PREFETCHWT1 (HOST_WIDE_INT_1 << 46) #define PTA_CLFLUSHOPT (HOST_WIDE_INT_1 << 47) @@ -3720,12 +3730,21 @@ ix86_option_override_internal (bool main_args_p, if (processor_alias_table[i].flags & PTA_AVX512VL && !(opts->x_ix86_isa_flags_explicit & OPTION_MASK_ISA_AVX512VL)) opts->x_ix86_isa_flags |= OPTION_MASK_ISA_AVX512VL; + if (processor_alias_table[i].flags & PTA_MPX + && !(opts->x_ix86_isa_flags_explicit & OPTION_MASK_ISA_MPX)) + opts->x_ix86_isa_flags |= OPTION_MASK_ISA_MPX; if (processor_alias_table[i].flags & (PTA_PREFETCH_SSE | PTA_SSE)) x86_prefetch_sse = true; break; } + if (TARGET_X32 && (opts->x_ix86_isa_flags & OPTION_MASK_ISA_MPX)) + error ("Intel MPX does not support x32"); + + if (TARGET_X32 && (ix86_isa_flags & OPTION_MASK_ISA_MPX)) + error ("Intel MPX does not support x32"); + if (!strcmp (opts->x_ix86_arch_string, "generic")) error ("generic CPU can be used only for %stune=%s %s", prefix, suffix, sw); @@ -4388,6 +4407,11 @@ ix86_conditional_register_usage (void) for (i = FIRST_MASK_REG; i <= LAST_MASK_REG; i++) fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = ""; } + + /* If MPX is disabled, squash the registers. */ + if (! TARGET_MPX) + for (i = FIRST_BND_REG; i <= LAST_BND_REG; i++) + fixed_regs[i] = call_used_regs[i] = 1, reg_names[i] = ""; } @@ -6286,10 +6310,15 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */ FIXME: once typesytem is fixed, we won't need this code anymore. */ if (i && i->local && i->can_change_signature) fntype = TREE_TYPE (fndecl); + cum->stdarg = stdarg_p (fntype); cum->maybe_vaarg = (fntype ? (!prototype_p (fntype) || stdarg_p (fntype)) : !libname); + cum->bnd_regno = FIRST_BND_REG; + cum->bnds_in_bt = 0; + cum->force_bnd_pass = 0; + if (!TARGET_64BIT) { /* If there are variable arguments, then we won't pass anything @@ -7224,13 +7253,17 @@ construct_container (machine_mode mode, machine_mode orig_mode, /* Update the data in CUM to advance over an argument of mode MODE and data type TYPE. (TYPE is null for libcalls where that information - may not be available.) */ + may not be available.) -static void + Return a number of integer regsiters advanced over. */ + +static int function_arg_advance_32 (CUMULATIVE_ARGS *cum, machine_mode mode, const_tree type, HOST_WIDE_INT bytes, HOST_WIDE_INT words) { + int res = 0; + switch (mode) { default: @@ -7248,7 +7281,8 @@ function_arg_advance_32 (CUMULATIVE_ARGS *cum, machine_mode mode, cum->words += words; cum->nregs -= words; cum->regno += words; - + if (cum->nregs >= 0) + res = words; if (cum->nregs <= 0) { cum->nregs = 0; @@ -7319,9 +7353,11 @@ function_arg_advance_32 (CUMULATIVE_ARGS *cum, machine_mode mode, } break; } + + return res; } -static void +static int function_arg_advance_64 (CUMULATIVE_ARGS *cum, machine_mode mode, const_tree type, HOST_WIDE_INT words, bool named) { @@ -7330,7 +7366,7 @@ function_arg_advance_64 (CUMULATIVE_ARGS *cum, machine_mode mode, /* Unnamed 512 and 256bit vector mode parameters are passed on stack. */ if (!named && (VALID_AVX512F_REG_MODE (mode) || VALID_AVX256_REG_MODE (mode))) - return; + return 0; if (!examine_argument (mode, type, 0, &int_nregs, &sse_nregs) && sse_nregs <= cum->sse_nregs && int_nregs <= cum->nregs) @@ -7339,16 +7375,18 @@ function_arg_advance_64 (CUMULATIVE_ARGS *cum, machine_mode mode, cum->sse_nregs -= sse_nregs; cum->regno += int_nregs; cum->sse_regno += sse_nregs; + return int_nregs; } else { int align = ix86_function_arg_boundary (mode, type) / BITS_PER_WORD; cum->words = (cum->words + align - 1) & ~(align - 1); cum->words += words; + return 0; } } -static void +static int function_arg_advance_ms_64 (CUMULATIVE_ARGS *cum, HOST_WIDE_INT bytes, HOST_WIDE_INT words) { @@ -7360,7 +7398,9 @@ function_arg_advance_ms_64 (CUMULATIVE_ARGS *cum, HOST_WIDE_INT bytes, { cum->nregs -= 1; cum->regno += 1; + return 1; } + return 0; } /* Update the data in CUM to advance over an argument of mode MODE and @@ -7373,6 +7413,7 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, { CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); HOST_WIDE_INT bytes, words; + int nregs; if (mode == BLKmode) bytes = int_size_in_bytes (type); @@ -7383,12 +7424,51 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, if (type) mode = type_natural_mode (type, NULL, false); + if ((type && POINTER_BOUNDS_TYPE_P (type)) + || POINTER_BOUNDS_MODE_P (mode)) + { + /* If we pass bounds in BT then just update remained bounds count. */ + if (cum->bnds_in_bt) + { + cum->bnds_in_bt--; + return; + } + + /* Update remained number of bounds to force. */ + if (cum->force_bnd_pass) + cum->force_bnd_pass--; + + cum->bnd_regno++; + + return; + } + + /* The first arg not going to Bounds Tables resets this counter. */ + cum->bnds_in_bt = 0; + /* For unnamed args we always pass bounds to avoid bounds mess when + passed and received types do not match. If bounds do not follow + unnamed arg, still pretend required number of bounds were passed. */ + if (cum->force_bnd_pass) + { + cum->bnd_regno += cum->force_bnd_pass; + cum->force_bnd_pass = 0; + } + if (TARGET_64BIT && (cum ? cum->call_abi : ix86_abi) == MS_ABI) - function_arg_advance_ms_64 (cum, bytes, words); + nregs = function_arg_advance_ms_64 (cum, bytes, words); else if (TARGET_64BIT) - function_arg_advance_64 (cum, mode, type, words, named); + nregs = function_arg_advance_64 (cum, mode, type, words, named); else - function_arg_advance_32 (cum, mode, type, bytes, words); + nregs = function_arg_advance_32 (cum, mode, type, bytes, words); + + /* For stdarg we expect bounds to be passed for each value passed + in register. */ + if (cum->stdarg) + cum->force_bnd_pass = nregs; + /* For pointers passed in memory we expect bounds passed in Bounds + Table. */ + if (!nregs) + cum->bnds_in_bt = chkp_type_bounds_count (type); } /* Define where to put the arguments to a function. @@ -7623,6 +7703,23 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode, HOST_WIDE_INT bytes, words; rtx arg; + /* All pointer bounds argumntas are handled separately here. */ + if ((type && POINTER_BOUNDS_TYPE_P (type)) + || POINTER_BOUNDS_MODE_P (mode)) + { + /* Return NULL if bounds are forced to go in Bounds Table. */ + if (cum->bnds_in_bt) + arg = NULL; + /* Return the next available bound reg if any. */ + else if (cum->bnd_regno <= LAST_BND_REG) + arg = gen_rtx_REG (BNDmode, cum->bnd_regno); + /* Return the next special slot number otherwise. */ + else + arg = GEN_INT (cum->bnd_regno - LAST_BND_REG - 1); + + return arg; + } + if (mode == BLKmode) bytes = int_size_in_bytes (type); else @@ -7896,6 +7993,9 @@ ix86_function_value_regno_p (const unsigned int regno) case SI_REG: return TARGET_64BIT && ix86_abi != MS_ABI; + case FIRST_BND_REG: + return chkp_function_instrumented_p (current_function_decl); + /* Complex values are returned in %st(0)/%st(1) pair. */ case ST0_REG: case ST1_REG: @@ -8072,7 +8172,10 @@ ix86_function_value_1 (const_tree valtype, const_tree fntype_or_decl, fn = fntype_or_decl; fntype = fn ? TREE_TYPE (fn) : fntype_or_decl; - if (TARGET_64BIT && ix86_function_type_abi (fntype) == MS_ABI) + if ((valtype && POINTER_BOUNDS_TYPE_P (valtype)) + || POINTER_BOUNDS_MODE_P (mode)) + return gen_rtx_REG (BNDmode, FIRST_BND_REG); + else if (TARGET_64BIT && ix86_function_type_abi (fntype) == MS_ABI) return function_value_ms_64 (orig_mode, mode, valtype); else if (TARGET_64BIT) return function_value_64 (orig_mode, mode, valtype); @@ -8090,6 +8193,57 @@ ix86_function_value (const_tree valtype, const_tree fntype_or_decl, bool) return ix86_function_value_1 (valtype, fntype_or_decl, orig_mode, mode); } +/* Return an RTX representing a place where a function returns + or recieves pointer bounds or NULL if no bounds are returned. + + VALTYPE is a data type of a value returned by the function. + + FN_DECL_OR_TYPE is a tree node representing FUNCTION_DECL + or FUNCTION_TYPE of the function. + + If OUTGOING is false, return a place in which the caller will + see the return value. Otherwise, return a place where a + function returns a value. */ + +static rtx +ix86_function_value_bounds (const_tree valtype, + const_tree fntype_or_decl ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + rtx res = NULL_RTX; + + if (BOUNDED_TYPE_P (valtype)) + res = gen_rtx_REG (BNDmode, FIRST_BND_REG); + else if (chkp_type_has_pointer (valtype)) + { + bitmap slots; + rtx bounds[2]; + bitmap_iterator bi; + unsigned i, bnd_no = 0; + + bitmap_obstack_initialize (NULL); + slots = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (valtype, slots); + + EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi) + { + rtx reg = gen_rtx_REG (BNDmode, FIRST_BND_REG + bnd_no); + rtx offs = GEN_INT (i * POINTER_SIZE / BITS_PER_UNIT); + gcc_assert (bnd_no < 2); + bounds[bnd_no++] = gen_rtx_EXPR_LIST (VOIDmode, reg, offs); + } + + res = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (bnd_no, bounds)); + + BITMAP_FREE (slots); + bitmap_obstack_release (NULL); + } + else + res = NULL_RTX; + + return res; +} + /* Pointer function arguments and return values are promoted to word_mode. */ @@ -8136,6 +8290,9 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) const machine_mode mode = type_natural_mode (type, NULL, true); HOST_WIDE_INT size; + if (POINTER_BOUNDS_TYPE_P (type)) + return false; + if (TARGET_64BIT) { if (ix86_function_type_abi (fntype) == MS_ABI) @@ -8451,6 +8608,71 @@ ix86_setup_incoming_varargs (cumulative_args_t cum_v, machine_mode mode, setup_incoming_varargs_64 (&next_cum); } +static void +ix86_setup_incoming_vararg_bounds (cumulative_args_t cum_v, + enum machine_mode mode, + tree type, + int *pretend_size ATTRIBUTE_UNUSED, + int no_rtl) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + CUMULATIVE_ARGS next_cum; + tree fntype; + rtx save_area; + int bnd_reg, i, max; + + gcc_assert (!no_rtl); + + /* Do nothing if we use plain pointer to argument area. */ + if (!TARGET_64BIT || cum->call_abi == MS_ABI) + return; + + fntype = TREE_TYPE (current_function_decl); + + /* For varargs, we do not want to skip the dummy va_dcl argument. + For stdargs, we do want to skip the last named argument. */ + next_cum = *cum; + if (stdarg_p (fntype)) + ix86_function_arg_advance (pack_cumulative_args (&next_cum), mode, type, + true); + save_area = frame_pointer_rtx; + + max = cum->regno + cfun->va_list_gpr_size / UNITS_PER_WORD; + if (max > X86_64_REGPARM_MAX) + max = X86_64_REGPARM_MAX; + + bnd_reg = cum->bnd_regno + cum->force_bnd_pass; + if (chkp_function_instrumented_p (current_function_decl)) + for (i = cum->regno; i < max; i++) + { + rtx addr = plus_constant (Pmode, save_area, i * UNITS_PER_WORD); + rtx reg = gen_rtx_REG (DImode, + x86_64_int_parameter_registers[i]); + rtx ptr = reg; + rtx bounds; + + if (bnd_reg <= LAST_BND_REG) + bounds = gen_rtx_REG (BNDmode, bnd_reg); + else + { + rtx ldx_addr = + plus_constant (Pmode, arg_pointer_rtx, + (LAST_BND_REG - bnd_reg) * GET_MODE_SIZE (Pmode)); + bounds = gen_reg_rtx (BNDmode); + emit_insn (BNDmode == BND64mode + ? gen_bnd64_ldx (bounds, ldx_addr, ptr) + : gen_bnd32_ldx (bounds, ldx_addr, ptr)); + } + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_stx (addr, ptr, bounds) + : gen_bnd32_stx (addr, ptr, bounds)); + + bnd_reg++; + } +} + + /* Checks if TYPE is of kind va_list char *. */ static bool @@ -8525,6 +8747,13 @@ ix86_va_start (tree valist, rtx nextarg) crtl->args.arg_offset_rtx, NULL_RTX, 0, OPTAB_LIB_WIDEN); convert_move (va_r, next, 0); + + /* Store zero bounds for va_list. */ + if (chkp_function_instrumented_p (current_function_decl)) + chkp_expand_bounds_reset_for_mem (valist, + make_tree (TREE_TYPE (valist), + next)); + } return; } @@ -8578,6 +8807,11 @@ ix86_va_start (tree valist, rtx nextarg) t = make_tree (type, ovf_rtx); if (words != 0) t = fold_build_pointer_plus_hwi (t, words * UNITS_PER_WORD); + + /* Store zero bounds for overflow area pointer. */ + if (chkp_function_instrumented_p (current_function_decl)) + chkp_expand_bounds_reset_for_mem (ovf, t); + t = build2 (MODIFY_EXPR, type, ovf, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -8590,6 +8824,11 @@ ix86_va_start (tree valist, rtx nextarg) t = make_tree (type, frame_pointer_rtx); if (!ix86_varargs_gpr_size) t = fold_build_pointer_plus_hwi (t, -8 * X86_64_REGPARM_MAX); + + /* Store zero bounds for save area pointer. */ + if (chkp_function_instrumented_p (current_function_decl)) + chkp_expand_bounds_reset_for_mem (sav, t); + t = build2 (MODIFY_EXPR, type, sav, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -9343,7 +9582,7 @@ ix86_code_end (void) xops[0] = gen_rtx_REG (Pmode, regno); xops[1] = gen_rtx_MEM (Pmode, stack_pointer_rtx); output_asm_insn ("mov%z0\t{%1, %0|%0, %1}", xops); - fputs ("\tret\n", asm_out_file); + output_asm_insn ("%!ret", NULL); final_end_function (); init_insn_lengths (); free_after_compilation (cfun); @@ -9401,7 +9640,7 @@ output_set_got (rtx dest, rtx label) xops[2] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); xops[2] = gen_rtx_MEM (QImode, xops[2]); - output_asm_insn ("call\t%X2", xops); + output_asm_insn ("%!call\t%X2", xops); #if TARGET_MACHO /* Output the Mach-O "canonical" pic base label name ("Lxx$pb") here. @@ -12584,6 +12823,10 @@ darwin_local_data_pic (rtx disp) static bool ix86_legitimate_constant_p (machine_mode, rtx x) { + /* Pointer bounds constants are not valid. */ + if (POINTER_BOUNDS_MODE_P (GET_MODE (x))) + return false; + switch (GET_CODE (x)) { case CONST: @@ -14833,7 +15076,7 @@ print_reg (rtx x, int code, FILE *file) case 8: case 4: case 12: - if (! ANY_FP_REG_P (x) && ! ANY_MASK_REG_P (x)) + if (! ANY_FP_REG_P (x) && ! ANY_MASK_REG_P (x) && ! ANY_BND_REG_P (x)) putc (code == 8 && TARGET_64BIT ? 'r' : 'e', file); /* FALLTHRU */ case 16: @@ -14922,6 +15165,7 @@ print_reg (rtx x, int code, FILE *file) ~ -- print "i" if TARGET_AVX2, "f" otherwise. @ -- print a segment register of thread base pointer load ^ -- print addr32 prefix if TARGET_64BIT and Pmode != word_mode + ! -- print MPX prefix for jxx/call/ret instructions if required. */ void @@ -15464,6 +15708,11 @@ ix86_print_operand (FILE *file, rtx x, int code) fputs ("addr32 ", file); return; + case '!': + if (ix86_bnd_prefixed_insn_p (current_output_insn)) + fputs ("bnd ", file); + return; + default: output_operand_lossage ("invalid operand code '%c'", code); } @@ -15606,7 +15855,7 @@ static bool ix86_print_operand_punct_valid_p (unsigned char code) { return (code == '@' || code == '*' || code == '+' || code == '&' - || code == ';' || code == '~' || code == '^'); + || code == ';' || code == '~' || code == '^' || code == '!'); } /* Print a memory operand whose address is ADDR. */ @@ -15636,6 +15885,25 @@ ix86_print_operand_address (FILE *file, rtx addr) ok = ix86_decompose_address (XVECEXP (addr, 0, 0), &parts); code = 'q'; } + else if (GET_CODE (addr) == UNSPEC && XINT (addr, 1) == UNSPEC_BNDMK_ADDR) + { + ok = ix86_decompose_address (XVECEXP (addr, 0, 1), &parts); + gcc_assert (parts.base == NULL_RTX || parts.index == NULL_RTX); + if (parts.base != NULL_RTX) + { + parts.index = parts.base; + parts.scale = 1; + } + parts.base = XVECEXP (addr, 0, 0); + addr = XVECEXP (addr, 0, 0); + } + else if (GET_CODE (addr) == UNSPEC && XINT (addr, 1) == UNSPEC_BNDLDX_ADDR) + { + ok = ix86_decompose_address (XVECEXP (addr, 0, 0), &parts); + gcc_assert (parts.index == NULL_RTX); + parts.index = XVECEXP (addr, 0, 1); + addr = XVECEXP (addr, 0, 0); + } else ok = ix86_decompose_address (addr, &parts); @@ -25151,8 +25419,21 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, } call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1); + if (retval) - call = gen_rtx_SET (VOIDmode, retval, call); + { + /* We should add bounds as destination register in case + pointer with bounds may be returned. */ + if (TARGET_MPX && SCALAR_INT_MODE_P (GET_MODE (retval))) + { + rtx b0 = gen_rtx_REG (BND64mode, FIRST_BND_REG); + rtx b1 = gen_rtx_REG (BND64mode, FIRST_BND_REG + 1); + retval = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (3, retval, b0, b1)); + chkp_put_regs_to_expr_list (retval); + } + + call = gen_rtx_SET (VOIDmode, retval, call); + } vec[vec_len++] = call; if (pop) @@ -25199,13 +25480,13 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) if (SIBLING_CALL_P (insn)) { if (direct_p) - xasm = "jmp\t%P0"; + xasm = "%!jmp\t%P0"; /* SEH epilogue detection requires the indirect branch case to include REX.W. */ else if (TARGET_SEH) - xasm = "rex.W jmp %A0"; + xasm = "%!rex.W jmp %A0"; else - xasm = "jmp\t%A0"; + xasm = "%!jmp\t%A0"; output_asm_insn (xasm, &call_op); return ""; @@ -25242,9 +25523,9 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) } if (direct_p) - xasm = "call\t%P0"; + xasm = "%!call\t%P0"; else - xasm = "call\t%A0"; + xasm = "%!call\t%A0"; output_asm_insn (xasm, &call_op); @@ -29959,6 +30240,19 @@ enum ix86_builtins IX86_BUILTIN_XABORT, IX86_BUILTIN_XTEST, + /* MPX */ + 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, + /* BMI instructions. */ IX86_BUILTIN_BEXTR32, IX86_BUILTIN_BEXTR64, @@ -30036,6 +30330,8 @@ struct builtin_isa { enum ix86_builtin_func_type tcode; /* type to use in the declaration */ HOST_WIDE_INT isa; /* isa_flags this builtin is defined for */ bool const_p; /* true if the declaration is constant */ + bool leaf_p; /* true if the declaration has leaf attribute */ + bool nothrow_p; /* true if the declaration has nothrow attribute */ bool set_and_not_built_p; }; @@ -30087,6 +30383,8 @@ def_builtin (HOST_WIDE_INT mask, const char *name, ix86_builtins[(int) code] = NULL_TREE; ix86_builtins_isa[(int) code].tcode = tcode; ix86_builtins_isa[(int) code].name = name; + ix86_builtins_isa[(int) code].leaf_p = false; + ix86_builtins_isa[(int) code].nothrow_p = false; ix86_builtins_isa[(int) code].const_p = false; ix86_builtins_isa[(int) code].set_and_not_built_p = true; } @@ -30137,6 +30435,11 @@ ix86_add_new_builtins (HOST_WIDE_INT isa) ix86_builtins[i] = decl; if (ix86_builtins_isa[i].const_p) TREE_READONLY (decl) = 1; + if (ix86_builtins_isa[i].leaf_p) + DECL_ATTRIBUTES (decl) = build_tree_list (get_identifier ("leaf"), + NULL_TREE); + if (ix86_builtins_isa[i].nothrow_p) + TREE_NOTHROW (decl) = 1; } } } @@ -32558,6 +32861,27 @@ static const struct builtin_description bdesc_round_args[] = { OPTION_MASK_ISA_AVX512DQ, CODE_FOR_avx512dq_rangepv8df_mask_round, "__builtin_ia32_rangepd512_mask", IX86_BUILTIN_RANGEPD512, UNKNOWN, (int) V8DF_FTYPE_V8DF_V8DF_INT_V8DF_QI_INT }, }; +/* Bultins for MPX. */ +static const struct builtin_description bdesc_mpx[] = +{ + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndstx", IX86_BUILTIN_BNDSTX, UNKNOWN, (int) VOID_FTYPE_PCVOID_BND_PCVOID }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndcl", IX86_BUILTIN_BNDCL, UNKNOWN, (int) VOID_FTYPE_PCVOID_BND }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndcu", IX86_BUILTIN_BNDCU, UNKNOWN, (int) VOID_FTYPE_PCVOID_BND }, +}; + +/* Const builtins for MPX. */ +static const struct builtin_description bdesc_mpx_const[] = +{ + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndmk", IX86_BUILTIN_BNDMK, UNKNOWN, (int) BND_FTYPE_PCVOID_ULONG }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndldx", IX86_BUILTIN_BNDLDX, UNKNOWN, (int) BND_FTYPE_PCVOID_PCVOID }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_narrow_bounds", IX86_BUILTIN_BNDNARROW, UNKNOWN, (int) PVOID_FTYPE_PCVOID_BND_ULONG }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndint", IX86_BUILTIN_BNDINT, UNKNOWN, (int) BND_FTYPE_BND_BND }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_sizeof", IX86_BUILTIN_SIZEOF, UNKNOWN, (int) ULONG_FTYPE_VOID }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndlower", IX86_BUILTIN_BNDLOWER, UNKNOWN, (int) PVOID_FTYPE_BND }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndupper", IX86_BUILTIN_BNDUPPER, UNKNOWN, (int) PVOID_FTYPE_BND }, + { OPTION_MASK_ISA_MPX, (enum insn_code)0, "__builtin_ia32_bndret", IX86_BUILTIN_BNDRET, UNKNOWN, (int) BND_FTYPE_PCVOID }, +}; + /* FMA4 and XOP. */ #define MULTI_ARG_4_DF2_DI_I V2DF_FTYPE_V2DF_V2DF_V2DI_INT #define MULTI_ARG_4_DF2_DI_I1 V4DF_FTYPE_V4DF_V4DF_V4DI_INT @@ -33559,6 +33883,67 @@ ix86_init_mmx_sse_builtins (void) } } +static void +ix86_init_mpx_builtins () +{ + const struct builtin_description * d; + enum ix86_builtin_func_type ftype; + tree decl; + size_t i; + + for (i = 0, d = bdesc_mpx; + i < ARRAY_SIZE (bdesc_mpx); + i++, d++) + { + if (d->name == 0) + continue; + + ftype = (enum ix86_builtin_func_type) d->flag; + decl = def_builtin (d->mask, d->name, ftype, d->code); + + /* With no leaf and nothrow flags for MPX builtins + abnormal edges may follow its call when setjmp + presents in the function. Since we may have a lot + of MPX builtins calls it causes lots of useless + edges and enormous PHI nodes. To avoid this we mark + MPX builtins as leaf and nothrow. */ + if (decl) + { + DECL_ATTRIBUTES (decl) = build_tree_list (get_identifier ("leaf"), + NULL_TREE); + TREE_NOTHROW (decl) = 1; + } + else + { + ix86_builtins_isa[(int)d->code].leaf_p = true; + ix86_builtins_isa[(int)d->code].nothrow_p = true; + } + } + + for (i = 0, d = bdesc_mpx_const; + i < ARRAY_SIZE (bdesc_mpx_const); + i++, d++) + { + if (d->name == 0) + continue; + + ftype = (enum ix86_builtin_func_type) d->flag; + decl = def_builtin_const (d->mask, d->name, ftype, d->code); + + if (decl) + { + DECL_ATTRIBUTES (decl) = build_tree_list (get_identifier ("leaf"), + NULL_TREE); + TREE_NOTHROW (decl) = 1; + } + else + { + ix86_builtins_isa[(int)d->code].leaf_p = true; + ix86_builtins_isa[(int)d->code].nothrow_p = true; + } + } +} + /* This adds a condition to the basic_block NEW_BB in function FUNCTION_DECL to return a pointer to VERSION_DECL if the outcome of the expression formed by PREDICATE_CHAIN is true. This function will be called during @@ -35097,6 +35482,7 @@ ix86_init_builtins (void) ix86_init_tm_builtins (); ix86_init_mmx_sse_builtins (); + ix86_init_mpx_builtins (); if (TARGET_LP64) ix86_init_builtins_va_builtins_abi (); @@ -37723,6 +38109,37 @@ ix86_expand_vec_set_builtin (tree exp) return target; } +/* Emit conditional move of SRC to DST with condition + OP1 CODE OP2. */ +static void +ix86_emit_cmove (rtx dst, rtx src, enum rtx_code code, rtx op1, rtx op2) +{ + rtx t; + + if (TARGET_CMOVE) + { + t = ix86_expand_compare (code, op1, op2); + emit_insn (gen_rtx_SET (VOIDmode, dst, + gen_rtx_IF_THEN_ELSE (GET_MODE (dst), t, + src, dst))); + } + else + { + rtx nomove = gen_label_rtx (); + emit_cmp_and_jump_insns (op1, op2, reverse_condition (code), + const0_rtx, GET_MODE (op1), 1, nomove); + emit_move_insn (dst, src); + emit_label (nomove); + } +} + +/* Choose max of DST and SRC and put it to DST. */ +static void +ix86_emit_move_max (rtx dst, rtx src) +{ + ix86_emit_cmove (dst, src, LTU, dst, src); +} + /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient (and in mode MODE if that's convenient). @@ -37788,6 +38205,343 @@ ix86_expand_builtin (tree exp, rtx target, rtx subtarget, switch (fcode) { + case IX86_BUILTIN_BNDMK: + if (!target + || GET_MODE (target) != BNDmode + || !register_operand (target, BNDmode)) + target = gen_reg_rtx (BNDmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + if (!register_operand (op0, Pmode)) + op0 = ix86_zero_extend_to_Pmode (op0); + if (!register_operand (op1, Pmode)) + op1 = ix86_zero_extend_to_Pmode (op1); + + /* Builtin arg1 is size of block but instruction op1 should + be (size - 1). */ + op1 = expand_simple_binop (Pmode, PLUS, op1, constm1_rtx, + NULL_RTX, 1, OPTAB_DIRECT); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_mk (target, op0, op1) + : gen_bnd32_mk (target, op0, op1)); + return target; + + case IX86_BUILTIN_BNDSTX: + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + arg2 = CALL_EXPR_ARG (exp, 2); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + op2 = expand_normal (arg2); + + if (!register_operand (op0, Pmode)) + op0 = ix86_zero_extend_to_Pmode (op0); + if (!register_operand (op1, BNDmode)) + op1 = copy_to_mode_reg (BNDmode, op1); + if (!register_operand (op2, Pmode)) + op2 = ix86_zero_extend_to_Pmode (op2); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_stx (op2, op0, op1) + : gen_bnd32_stx (op2, op0, op1)); + return 0; + + case IX86_BUILTIN_BNDLDX: + if (!target + || GET_MODE (target) != BNDmode + || !register_operand (target, BNDmode)) + target = gen_reg_rtx (BNDmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + if (!register_operand (op0, Pmode)) + op0 = ix86_zero_extend_to_Pmode (op0); + if (!register_operand (op1, Pmode)) + op1 = ix86_zero_extend_to_Pmode (op1); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_ldx (target, op0, op1) + : gen_bnd32_ldx (target, op0, op1)); + return target; + + case IX86_BUILTIN_BNDCL: + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + if (!register_operand (op0, Pmode)) + op0 = ix86_zero_extend_to_Pmode (op0); + if (!register_operand (op1, BNDmode)) + op1 = copy_to_mode_reg (BNDmode, op1); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_cl (op1, op0) + : gen_bnd32_cl (op1, op0)); + return 0; + + case IX86_BUILTIN_BNDCU: + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + if (!register_operand (op0, Pmode)) + op0 = ix86_zero_extend_to_Pmode (op0); + if (!register_operand (op1, BNDmode)) + op1 = copy_to_mode_reg (BNDmode, op1); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_cu (op1, op0) + : gen_bnd32_cu (op1, op0)); + return 0; + + case IX86_BUILTIN_BNDRET: + arg0 = CALL_EXPR_ARG (exp, 0); + gcc_assert (TREE_CODE (arg0) == SSA_NAME); + target = chkp_get_rtl_bounds (arg0); + + /* If no bounds were specified for returned value, + then use INIT bounds. It usually happens when + some built-in function is expanded. */ + if (!target) + { + rtx t1 = gen_reg_rtx (Pmode); + rtx t2 = gen_reg_rtx (Pmode); + target = gen_reg_rtx (BNDmode); + emit_move_insn (t1, const0_rtx); + emit_move_insn (t2, constm1_rtx); + emit_insn (BNDmode == BND64mode + ? gen_bnd64_mk (target, t1, t2) + : gen_bnd32_mk (target, t1, t2)); + } + + gcc_assert (target && REG_P (target)); + return target; + + case IX86_BUILTIN_BNDNARROW: + { + rtx m1, m1h1, m1h2, lb, ub, t1; + + /* Return value and lb. */ + arg0 = CALL_EXPR_ARG (exp, 0); + /* Bounds. */ + arg1 = CALL_EXPR_ARG (exp, 1); + /* Size. */ + arg2 = CALL_EXPR_ARG (exp, 2); + + lb = expand_normal (arg0); + op1 = expand_normal (arg1); + op2 = expand_normal (arg2); + + /* Size was passed but we need to use (size - 1) as for bndmk. */ + op2 = expand_simple_binop (Pmode, PLUS, op2, constm1_rtx, + NULL_RTX, 1, OPTAB_DIRECT); + + /* Add LB to size and inverse to get UB. */ + op2 = expand_simple_binop (Pmode, PLUS, op2, lb, + op2, 1, OPTAB_DIRECT); + ub = expand_simple_unop (Pmode, NOT, op2, op2, 1); + + if (!register_operand (lb, Pmode)) + lb = ix86_zero_extend_to_Pmode (lb); + if (!register_operand (ub, Pmode)) + ub = ix86_zero_extend_to_Pmode (ub); + + /* We need to move bounds to memory before any computations. */ + if (MEM_P (op1)) + m1 = op1; + else + { + m1 = assign_386_stack_local (BNDmode, SLOT_TEMP); + emit_move_insn (m1, op1); + } + + /* Generate mem expression to be used for access to LB and UB. */ + m1h1 = adjust_address (m1, Pmode, 0); + m1h2 = adjust_address (m1, Pmode, GET_MODE_SIZE (Pmode)); + + t1 = gen_reg_rtx (Pmode); + + /* Compute LB. */ + emit_move_insn (t1, m1h1); + ix86_emit_move_max (t1, lb); + emit_move_insn (m1h1, t1); + + /* Compute UB. UB is stored in 1's complement form. Therefore + we also use max here. */ + emit_move_insn (t1, m1h2); + ix86_emit_move_max (t1, ub); + emit_move_insn (m1h2, t1); + + op2 = gen_reg_rtx (BNDmode); + emit_move_insn (op2, m1); + + return chkp_join_splitted_slot (lb, op2); + } + + case IX86_BUILTIN_BNDINT: + { + rtx res, rh1, rh2, lb1, lb2, ub1, ub2; + + if (!target + || GET_MODE (target) != BNDmode + || !register_operand (target, BNDmode)) + target = gen_reg_rtx (BNDmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + arg1 = CALL_EXPR_ARG (exp, 1); + + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + res = assign_386_stack_local (BNDmode, SLOT_TEMP); + rh1 = adjust_address (res, Pmode, 0); + rh2 = adjust_address (res, Pmode, GET_MODE_SIZE (Pmode)); + + /* Put first bounds to temporaries. */ + lb1 = gen_reg_rtx (Pmode); + ub1 = gen_reg_rtx (Pmode); + if (MEM_P (op0)) + { + emit_move_insn (lb1, adjust_address (op0, Pmode, 0)); + emit_move_insn (ub1, adjust_address (op0, Pmode, + GET_MODE_SIZE (Pmode))); + } + else + { + emit_move_insn (res, op0); + emit_move_insn (lb1, rh1); + emit_move_insn (ub1, rh2); + } + + /* Put second bounds to temporaries. */ + lb2 = gen_reg_rtx (Pmode); + ub2 = gen_reg_rtx (Pmode); + if (MEM_P (op1)) + { + emit_move_insn (lb2, adjust_address (op1, Pmode, 0)); + emit_move_insn (ub2, adjust_address (op1, Pmode, + GET_MODE_SIZE (Pmode))); + } + else + { + emit_move_insn (res, op1); + emit_move_insn (lb2, rh1); + emit_move_insn (ub2, rh2); + } + + /* Compute LB. */ + ix86_emit_move_max (lb1, lb2); + emit_move_insn (rh1, lb1); + + /* Compute UB. UB is stored in 1's complement form. Therefore + we also use max here. */ + ix86_emit_move_max (ub1, ub2); + emit_move_insn (rh2, ub1); + + emit_move_insn (target, res); + + return target; + } + + case IX86_BUILTIN_SIZEOF: + { + tree name; + rtx symbol; + + if (!target + || GET_MODE (target) != Pmode + || !register_operand (target, Pmode)) + target = gen_reg_rtx (Pmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + gcc_assert (TREE_CODE (arg0) == VAR_DECL); + + name = DECL_ASSEMBLER_NAME (arg0); + symbol = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (name)); + + emit_insn (Pmode == SImode + ? gen_move_size_reloc_si (target, symbol) + : gen_move_size_reloc_di (target, symbol)); + + return target; + } + + case IX86_BUILTIN_BNDLOWER: + { + rtx mem, hmem; + + if (!target + || GET_MODE (target) != Pmode + || !register_operand (target, Pmode)) + target = gen_reg_rtx (Pmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_normal (arg0); + + /* We need to move bounds to memory first. */ + if (MEM_P (op0)) + mem = op0; + else + { + mem = assign_386_stack_local (BNDmode, SLOT_TEMP); + emit_move_insn (mem, op0); + } + + /* Generate mem expression to access LB and load it. */ + hmem = adjust_address (mem, Pmode, 0); + emit_move_insn (target, hmem); + + return target; + } + + case IX86_BUILTIN_BNDUPPER: + { + rtx mem, hmem, res; + + if (!target + || GET_MODE (target) != Pmode + || !register_operand (target, Pmode)) + target = gen_reg_rtx (Pmode); + + arg0 = CALL_EXPR_ARG (exp, 0); + op0 = expand_normal (arg0); + + /* We need to move bounds to memory first. */ + if (MEM_P (op0)) + mem = op0; + else + { + mem = assign_386_stack_local (BNDmode, SLOT_TEMP); + emit_move_insn (mem, op0); + } + + /* Generate mem expression to access UB. */ + hmem = adjust_address (mem, Pmode, GET_MODE_SIZE (Pmode)); + + /* We need to inverse all bits of UB. */ + res = expand_simple_unop (Pmode, NOT, hmem, target, 1); + + if (res != target) + emit_move_insn (target, res); + + return target; + } + case IX86_BUILTIN_MASKMOVQ: case IX86_BUILTIN_MASKMOVDQU: icode = (fcode == IX86_BUILTIN_MASKMOVQ @@ -39072,6 +39826,193 @@ static tree ix86_get_builtin (enum ix86_builtins code) return NULL_TREE; } +/* Return function decl for target specific builtin + for given MPX builtin passed i FCODE. */ +static tree +ix86_builtin_mpx_function (unsigned fcode) +{ + switch (fcode) + { + case BUILT_IN_CHKP_BNDMK: + return ix86_builtins[IX86_BUILTIN_BNDMK]; + + case BUILT_IN_CHKP_BNDSTX: + return ix86_builtins[IX86_BUILTIN_BNDSTX]; + + case BUILT_IN_CHKP_BNDLDX: + return ix86_builtins[IX86_BUILTIN_BNDLDX]; + + case BUILT_IN_CHKP_BNDCL: + return ix86_builtins[IX86_BUILTIN_BNDCL]; + + case BUILT_IN_CHKP_BNDCU: + return ix86_builtins[IX86_BUILTIN_BNDCU]; + + case BUILT_IN_CHKP_BNDRET: + return ix86_builtins[IX86_BUILTIN_BNDRET]; + + case BUILT_IN_CHKP_INTERSECT: + return ix86_builtins[IX86_BUILTIN_BNDINT]; + + case BUILT_IN_CHKP_NARROW: + return ix86_builtins[IX86_BUILTIN_BNDNARROW]; + + case BUILT_IN_CHKP_SIZEOF: + return ix86_builtins[IX86_BUILTIN_SIZEOF]; + + case BUILT_IN_CHKP_EXTRACT_LOWER: + return ix86_builtins[IX86_BUILTIN_BNDLOWER]; + + case BUILT_IN_CHKP_EXTRACT_UPPER: + return ix86_builtins[IX86_BUILTIN_BNDUPPER]; + + default: + return NULL_TREE; + } + + gcc_unreachable (); +} + +/* Helper function for ix86_load_bounds and ix86_store_bounds. + + Return an address to be used to load/store bounds for pointer + passed in SLOT. + + SLOT_NO is an integer constant holding number of a target + dependent special slot to be used in case SLOT is not a memory. + + SPECIAL_BASE is a pointer to be used as a base of fake address + to access special slots in Bounds Table. SPECIAL_BASE[-1], + SPECIAL_BASE[-2] etc. will be used as fake pointer locations. */ + +static rtx +ix86_get_arg_address_for_bt (rtx slot, rtx slot_no, rtx special_base) +{ + rtx addr = NULL; + + /* NULL slot means we pass bounds for pointer not passed to the + function at all. Register slot means we pass pointer in a + register. In both these cases bounds are passed via Bounds + Table. Since we do not have actual pointer stored in memory, + we have to use fake addresses to access Bounds Table. We + start with (special_base - sizeof (void*)) and decrease this + address by pointer size to get addresses for other slots. */ + if (!slot || REG_P (slot)) + { + gcc_assert (CONST_INT_P (slot_no)); + addr = plus_constant (Pmode, special_base, + -(INTVAL (slot_no) + 1) * GET_MODE_SIZE (Pmode)); + } + /* If pointer is passed in a memory then its address is used to + access Bounds Table. */ + else if (MEM_P (slot)) + { + addr = XEXP (slot, 0); + if (!register_operand (addr, Pmode)) + addr = copy_addr_to_reg (addr); + } + else + gcc_unreachable (); + + return addr; +} + +/* Expand pass uses this hook to load bounds for function parameter + PTR passed in SLOT in case its bounds are not passed in a register. + + If SLOT is a memory, then bounds are loaded as for regular pointer + loaded from memory. PTR may be NULL in case SLOT is a memory. + In such case value of PTR (if required) may be loaded from SLOT. + + If SLOT is NULL or a register then SLOT_NO is an integer constant + holding number of the target dependent special slot which should be + used to obtain bounds. + + Return loaded bounds. */ + +static rtx +ix86_load_bounds (rtx slot, rtx ptr, rtx slot_no) +{ + rtx reg = gen_reg_rtx (BNDmode); + rtx addr; + + /* Get address to be used to access Bounds Table. Special slots start + at the location of return address of the current function. */ + addr = ix86_get_arg_address_for_bt (slot, slot_no, arg_pointer_rtx); + + /* Load pointer value from a memory if we don't have it. */ + if (!ptr) + { + gcc_assert (MEM_P (slot)); + ptr = copy_addr_to_reg (slot); + } + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_ldx (reg, addr, ptr) + : gen_bnd32_ldx (reg, addr, ptr)); + + return reg; +} + +/* Expand pass uses this hook to store BOUNDS for call argument PTR + passed in SLOT in case BOUNDS are not passed in a register. + + If SLOT is a memory, then BOUNDS are stored as for regular pointer + stored in memory. PTR may be NULL in case SLOT is a memory. + In such case value of PTR (if required) may be loaded from SLOT. + + If SLOT is NULL or a register then SLOT_NO is an integer constant + holding number of the target dependent special slot which should be + used to store BOUNDS. */ + +static void +ix86_store_bounds (rtx ptr, rtx slot, rtx bounds, rtx slot_no) +{ + rtx addr; + + /* Get address to be used to access Bounds Table. Special slots start + at the location of return address of a called function. */ + addr = ix86_get_arg_address_for_bt (slot, slot_no, stack_pointer_rtx); + + /* Load pointer value from a memory if we don't have it. */ + if (!ptr) + { + gcc_assert (MEM_P (slot)); + ptr = copy_addr_to_reg (slot); + } + + gcc_assert (POINTER_BOUNDS_MODE_P (GET_MODE (bounds))); + if (!register_operand (bounds, BNDmode)) + bounds = copy_to_mode_reg (BNDmode, bounds); + + emit_insn (BNDmode == BND64mode + ? gen_bnd64_stx (addr, ptr, bounds) + : gen_bnd32_stx (addr, ptr, bounds)); +} + +/* Load and return bounds returned by function in SLOT. */ + +static rtx +ix86_load_returned_bounds (rtx slot) +{ + rtx res; + + gcc_assert (REG_P (slot)); + res = gen_reg_rtx (BNDmode); + emit_move_insn (res, slot); + + return res; +} + +/* Store BOUNDS returned by function into SLOT. */ + +static void +ix86_store_returned_bounds (rtx slot, rtx bounds) +{ + gcc_assert (REG_P (slot)); + emit_move_insn (slot, bounds); +} + /* Returns a function decl for a vectorized version of the builtin function with builtin function code FN and the result vector type TYPE, or NULL_TREE if it is not available. */ @@ -40192,6 +41133,7 @@ ix86_class_likely_spilled_p (reg_class_t rclass) case SSE_FIRST_REG: case FP_TOP_REG: case FP_SECOND_REG: + case BND_REGS: return true; default: @@ -40539,6 +41481,8 @@ ix86_hard_regno_mode_ok (int regno, machine_mode mode) if (MASK_REGNO_P (regno)) return (VALID_MASK_REG_MODE (mode) || (TARGET_AVX512BW && VALID_MASK_AVX512BW_MODE (mode))); + if (BND_REGNO_P (regno)) + return VALID_BND_REG_MODE (mode); if (SSE_REGNO_P (regno)) { /* We implement the move patterns for all vector modes into and @@ -41370,6 +42314,10 @@ x86_order_regs_for_local_alloc (void) for (i = FIRST_MASK_REG; i <= LAST_MASK_REG; i++) reg_alloc_order [pos++] = i; + /* MPX bound registers. */ + for (i = FIRST_BND_REG; i <= LAST_BND_REG; i++) + reg_alloc_order [pos++] = i; + /* x87 registers. */ if (TARGET_SSE_MATH) for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) @@ -48559,6 +49507,27 @@ ix86_expand_sse2_mulvxdi3 (rtx op0, rtx op1, rtx op2) gen_rtx_MULT (mode, op1, op2)); } +/* Return 1 if control tansfer instruction INSN + should be encoded with bnd prefix. + If insn is NULL then return 1 when control + transfer instructions should be prefixed with + bnd by default for current function. */ + +bool +ix86_bnd_prefixed_insn_p (rtx insn) +{ + /* For call insns check special flag. */ + if (insn && CALL_P (insn)) + { + rtx call = get_call_rtx_from (insn); + if (call) + return CALL_EXPR_WITH_BOUNDS_P (call); + } + + /* All other insns are prefixed only if function is instrumented. */ + return chkp_function_instrumented_p (current_function_decl); +} + /* Calculate integer abs() using only SSE2 instructions. */ void @@ -50163,6 +51132,73 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update) atomic_feraiseexcept_call); } +/* Return mode to be used for bounds or VOIDmode + if bounds are not supported. */ + +static enum machine_mode +ix86_mpx_bound_mode () +{ + /* Do not support pointer checker if MPX + is not enabled. */ + if (!TARGET_MPX) + { + if (flag_check_pointer_bounds) + warning (0, "Pointer Checker requires MPX support on this target." + " Use -mmpx options to enable MPX."); + return VOIDmode; + } + + return BNDmode; +} + +/* Return constant used to statically initialize constant bounds. + + This function is used to create special bound values. For now + only INIT bounds and NONE bounds are expected. More special + values may be added later. */ + +static tree +ix86_make_bounds_constant (HOST_WIDE_INT lb, HOST_WIDE_INT ub) +{ + tree low = lb ? build_minus_one_cst (pointer_sized_int_node) + : build_zero_cst (pointer_sized_int_node); + tree high = ub ? build_zero_cst (pointer_sized_int_node) + : build_minus_one_cst (pointer_sized_int_node); + + /* This function is supposed to be used to create INIT and + NONE bounds only. */ + gcc_assert ((lb == 0 && ub == -1) + || (lb == -1 && ub == 0)); + + return build_complex (NULL, low, high); +} + +/* Generate a list of statements STMTS to initialize pointer bounds + variable VAR with bounds LB and UB. Return the number of generated + statements. */ + +static int +ix86_initialize_bounds (tree var, tree lb, tree ub, tree *stmts) +{ + tree bnd_ptr = build_pointer_type (pointer_sized_int_node); + tree lhs, modify, var_p; + + ub = build1 (BIT_NOT_EXPR, pointer_sized_int_node, ub); + var_p = fold_convert (bnd_ptr, build_fold_addr_expr (var)); + + lhs = build1 (INDIRECT_REF, pointer_sized_int_node, var_p); + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, lb); + append_to_statement_list (modify, stmts); + + lhs = build1 (INDIRECT_REF, pointer_sized_int_node, + build2 (POINTER_PLUS_EXPR, bnd_ptr, var_p, + TYPE_SIZE_UNIT (pointer_sized_int_node))); + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, ub); + append_to_statement_list (modify, stmts); + + return 2; +} + /* Initialize the GCC target structure. */ #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory @@ -50588,6 +51624,36 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update) #undef TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS #define TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS true +#undef TARGET_LOAD_BOUNDS_FOR_ARG +#define TARGET_LOAD_BOUNDS_FOR_ARG ix86_load_bounds + +#undef TARGET_STORE_BOUNDS_FOR_ARG +#define TARGET_STORE_BOUNDS_FOR_ARG ix86_store_bounds + +#undef TARGET_LOAD_RETURNED_BOUNDS +#define TARGET_LOAD_RETURNED_BOUNDS ix86_load_returned_bounds + +#undef TARGET_STORE_RETURNED_BOUNDS +#define TARGET_STORE_RETURNED_BOUNDS ix86_store_returned_bounds + +#undef TARGET_CHKP_BOUND_MODE +#define TARGET_CHKP_BOUND_MODE ix86_mpx_bound_mode + +#undef TARGET_BUILTIN_CHKP_FUNCTION +#define TARGET_BUILTIN_CHKP_FUNCTION ix86_builtin_mpx_function + +#undef TARGET_CHKP_FUNCTION_VALUE_BOUNDS +#define TARGET_CHKP_FUNCTION_VALUE_BOUNDS ix86_function_value_bounds + +#undef TARGET_CHKP_MAKE_BOUNDS_CONSTANT +#define TARGET_CHKP_MAKE_BOUNDS_CONSTANT ix86_make_bounds_constant + +#undef TARGET_CHKP_INITIALIZE_BOUNDS +#define TARGET_CHKP_INITIALIZE_BOUNDS ix86_initialize_bounds + +#undef TARGET_SETUP_INCOMING_VARARG_BOUNDS +#define TARGET_SETUP_INCOMING_VARARG_BOUNDS ix86_setup_incoming_vararg_bounds + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-i386.h" diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 61beb6676c5..53dfd223cda 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -144,6 +144,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define TARGET_XSAVEOPT_P(x) TARGET_ISA_XSAVEOPT_P(x) #define TARGET_PREFETCHWT1 TARGET_ISA_PREFETCHWT1 #define TARGET_PREFETCHWT1_P(x) TARGET_ISA_PREFETCHWT1_P(x) +#define TARGET_MPX TARGET_ISA_MPX +#define TARGET_MPX_P(x) TARGET_ISA_MPX_P(x) #define TARGET_LP64 TARGET_ABI_64 #define TARGET_LP64_P(x) TARGET_ABI_64_P(x) @@ -943,7 +945,7 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); eliminated during reloading in favor of either the stack or frame pointer. */ -#define FIRST_PSEUDO_REGISTER 77 +#define FIRST_PSEUDO_REGISTER 81 /* Number of hardware registers that go into the DWARF-2 unwind info. If not defined, equals FIRST_PSEUDO_REGISTER. */ @@ -975,7 +977,9 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); /*xmm24,xmm25,xmm26,xmm27,xmm28,xmm29,xmm30,xmm31*/ \ 0, 0, 0, 0, 0, 0, 0, 0, \ /* k0, k1, k2, k3, k4, k5, k6, k7*/ \ - 0, 0, 0, 0, 0, 0, 0, 0 } + 0, 0, 0, 0, 0, 0, 0, 0, \ +/* b0, b1, b2, b3*/ \ + 0, 0, 0, 0 } /* 1 for registers not available across function calls. These must include the FIXED_REGISTERS and also any @@ -1009,7 +1013,9 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); /*xmm24,xmm25,xmm26,xmm27,xmm28,xmm29,xmm30,xmm31*/ \ 6, 6, 6, 6, 6, 6, 6, 6, \ /* k0, k1, k2, k3, k4, k5, k6, k7*/ \ - 1, 1, 1, 1, 1, 1, 1, 1 } + 1, 1, 1, 1, 1, 1, 1, 1, \ +/* b0, b1, b2, b3*/ \ + 1, 1, 1, 1 } /* Order in which to allocate registers. Each register must be listed once, even those in FIXED_REGISTERS. List frame pointer @@ -1025,7 +1031,8 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, \ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, \ - 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 } + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, \ + 78, 79, 80 } /* ADJUST_REG_ALLOC_ORDER is a macro which permits reg_alloc_order to be rearranged based on a particular function. When using sse math, @@ -1046,8 +1053,8 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); applied to them. */ #define HARD_REGNO_NREGS(REGNO, MODE) \ - (STACK_REGNO_P (REGNO) || SSE_REGNO_P (REGNO) \ - || MMX_REGNO_P (REGNO) || MASK_REGNO_P (REGNO) \ + (STACK_REGNO_P (REGNO) || SSE_REGNO_P (REGNO) || MMX_REGNO_P (REGNO) \ + || MASK_REGNO_P (REGNO) || BND_REGNO_P (REGNO) \ ? (COMPLEX_MODE_P (MODE) ? 2 : 1) \ : ((MODE) == XFmode \ ? (TARGET_64BIT ? 2 : 3) \ @@ -1102,6 +1109,9 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); || (MODE) == V2SImode || (MODE) == SImode \ || (MODE) == V4HImode || (MODE) == V8QImode) +#define VALID_BND_REG_MODE(MODE) \ + (TARGET_64BIT ? (MODE) == BND64mode : (MODE) == BND32mode) + #define VALID_DFP_MODE_P(MODE) \ ((MODE) == SDmode || (MODE) == DDmode || (MODE) == TDmode) @@ -1210,6 +1220,9 @@ extern const char *host_detect_local_cpu (int argc, const char **argv); #define FIRST_MASK_REG (LAST_EXT_REX_SSE_REG + 1) /*69*/ #define LAST_MASK_REG (FIRST_MASK_REG + 7) /*76*/ +#define FIRST_BND_REG (LAST_MASK_REG + 1) /*77*/ +#define LAST_BND_REG (FIRST_BND_REG + 3) /*80*/ + /* Override this in other tm.h files to cope with various OS lossage requiring a frame pointer. */ #ifndef SUBTARGET_FRAME_POINTER_REQUIRED @@ -1292,6 +1305,7 @@ enum reg_class SSE_FIRST_REG, SSE_REGS, EVEX_SSE_REGS, + BND_REGS, ALL_SSE_REGS, MMX_REGS, FP_TOP_SSE_REGS, @@ -1349,6 +1363,7 @@ enum reg_class "SSE_FIRST_REG", \ "SSE_REGS", \ "EVEX_SSE_REGS", \ + "BND_REGS", \ "ALL_SSE_REGS", \ "MMX_REGS", \ "FP_TOP_SSE_REGS", \ @@ -1368,37 +1383,38 @@ enum reg_class TARGET_CONDITIONAL_REGISTER_USAGE. */ #define REG_CLASS_CONTENTS \ -{ { 0x00, 0x0, 0x0 }, \ - { 0x01, 0x0, 0x0 }, /* AREG */ \ - { 0x02, 0x0, 0x0 }, /* DREG */ \ - { 0x04, 0x0, 0x0 }, /* CREG */ \ - { 0x08, 0x0, 0x0 }, /* BREG */ \ - { 0x10, 0x0, 0x0 }, /* SIREG */ \ - { 0x20, 0x0, 0x0 }, /* DIREG */ \ - { 0x03, 0x0, 0x0 }, /* AD_REGS */ \ - { 0x0f, 0x0, 0x0 }, /* Q_REGS */ \ - { 0x1100f0, 0x1fe0, 0x0 }, /* NON_Q_REGS */ \ - { 0x7f, 0x1fe0, 0x0 }, /* INDEX_REGS */ \ - { 0x1100ff, 0x0, 0x0 }, /* LEGACY_REGS */ \ - { 0x07, 0x0, 0x0 }, /* CLOBBERED_REGS */ \ - { 0x1100ff, 0x1fe0, 0x0 }, /* GENERAL_REGS */ \ - { 0x100, 0x0, 0x0 }, /* FP_TOP_REG */ \ - { 0x0200, 0x0, 0x0 }, /* FP_SECOND_REG */ \ - { 0xff00, 0x0, 0x0 }, /* FLOAT_REGS */ \ - { 0x200000, 0x0, 0x0 }, /* SSE_FIRST_REG */ \ -{ 0x1fe00000, 0x1fe000, 0x0 }, /* SSE_REGS */ \ - { 0x0,0xffe00000, 0x1f }, /* EVEX_SSE_REGS */ \ -{ 0x1fe00000,0xffffe000, 0x1f }, /* ALL_SSE_REGS */ \ -{ 0xe0000000, 0x1f, 0x0 }, /* MMX_REGS */ \ -{ 0x1fe00100,0xffffe000, 0x1f }, /* FP_TOP_SSE_REG */ \ -{ 0x1fe00200,0xffffe000, 0x1f }, /* FP_SECOND_SSE_REG */ \ -{ 0x1fe0ff00,0xffffe000, 0x1f }, /* FLOAT_SSE_REGS */ \ -{ 0x11ffff, 0x1fe0, 0x0 }, /* FLOAT_INT_REGS */ \ -{ 0x1ff100ff,0xffffffe0, 0x1f }, /* INT_SSE_REGS */ \ -{ 0x1ff1ffff,0xffffffe0, 0x1f }, /* FLOAT_INT_SSE_REGS */ \ - { 0x0, 0x0,0x1fc0 }, /* MASK_EVEX_REGS */ \ - { 0x0, 0x0,0x1fe0 }, /* MASK_REGS */ \ -{ 0xffffffff,0xffffffff,0x1fff } \ +{ { 0x00, 0x0, 0x0 }, \ + { 0x01, 0x0, 0x0 }, /* AREG */ \ + { 0x02, 0x0, 0x0 }, /* DREG */ \ + { 0x04, 0x0, 0x0 }, /* CREG */ \ + { 0x08, 0x0, 0x0 }, /* BREG */ \ + { 0x10, 0x0, 0x0 }, /* SIREG */ \ + { 0x20, 0x0, 0x0 }, /* DIREG */ \ + { 0x03, 0x0, 0x0 }, /* AD_REGS */ \ + { 0x0f, 0x0, 0x0 }, /* Q_REGS */ \ + { 0x1100f0, 0x1fe0, 0x0 }, /* NON_Q_REGS */ \ + { 0x7f, 0x1fe0, 0x0 }, /* INDEX_REGS */ \ + { 0x1100ff, 0x0, 0x0 }, /* LEGACY_REGS */ \ + { 0x07, 0x0, 0x0 }, /* CLOBBERED_REGS */ \ + { 0x1100ff, 0x1fe0, 0x0 }, /* GENERAL_REGS */ \ + { 0x100, 0x0, 0x0 }, /* FP_TOP_REG */ \ + { 0x0200, 0x0, 0x0 }, /* FP_SECOND_REG */ \ + { 0xff00, 0x0, 0x0 }, /* FLOAT_REGS */ \ + { 0x200000, 0x0, 0x0 }, /* SSE_FIRST_REG */ \ +{ 0x1fe00000, 0x1fe000, 0x0 }, /* SSE_REGS */ \ + { 0x0,0xffe00000, 0x1f }, /* EVEX_SSE_REGS */ \ + { 0x0, 0x0,0x1e000 }, /* BND_REGS */ \ +{ 0x1fe00000,0xffffe000, 0x1f }, /* ALL_SSE_REGS */ \ +{ 0xe0000000, 0x1f, 0x0 }, /* MMX_REGS */ \ +{ 0x1fe00100,0xffffe000, 0x1f }, /* FP_TOP_SSE_REG */ \ +{ 0x1fe00200,0xffffe000, 0x1f }, /* FP_SECOND_SSE_REG */ \ +{ 0x1fe0ff00,0xffffe000, 0x1f }, /* FLOAT_SSE_REGS */ \ +{ 0x11ffff, 0x1fe0, 0x0 }, /* FLOAT_INT_REGS */ \ +{ 0x1ff100ff,0xffffffe0, 0x1f }, /* INT_SSE_REGS */ \ +{ 0x1ff1ffff,0xffffffe0, 0x1f }, /* FLOAT_INT_SSE_REGS */ \ + { 0x0, 0x0, 0x1fc0 }, /* MASK_EVEX_REGS */ \ + { 0x0, 0x0, 0x1fe0 }, /* MASK_REGS */ \ +{ 0xffffffff,0xffffffff, 0x1fff } \ } /* The same information, inverted: @@ -1475,6 +1491,9 @@ enum reg_class #define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X))) #define CC_REGNO_P(X) ((X) == FLAGS_REG || (X) == FPSR_REG) +#define BND_REGNO_P(N) IN_RANGE ((N), FIRST_BND_REG, LAST_BND_REG) +#define ANY_BND_REG_P(X) (REG_P (X) && BND_REGNO_P (REGNO (X))) + /* The class value for index registers, and the one for base regs. */ #define INDEX_REG_CLASS INDEX_REGS @@ -1644,6 +1663,10 @@ typedef struct ix86_args { int float_in_sse; /* Set to 1 or 2 for 32bit targets if SFmode/DFmode arguments should be passed in SSE registers. Otherwise 0. */ + int bnd_regno; /* next available bnd register number */ + int bnds_in_bt; /* number of bounds expected in BT. */ + int force_bnd_pass; /* number of bounds expected for stdarg arg. */ + int stdarg; /* Set to 1 if function is stdarg. */ enum calling_abi call_abi; /* Set to SYSV_ABI for sysv abi. Otherwise MS_ABI for ms abi. */ } CUMULATIVE_ARGS; @@ -1921,6 +1944,9 @@ do { \ between pointers and any other objects of this machine mode. */ #define Pmode (ix86_pmode == PMODE_DI ? DImode : SImode) +/* Specify the machine mode that bounds have. */ +#define BNDmode (ix86_pmode == PMODE_DI ? BND64mode : BND32mode) + /* A C expression whose value is zero if pointers that need to be extended from being `POINTER_SIZE' bits wide to `Pmode' are sign-extended and greater then zero if they are zero-extended and less then zero if the @@ -2031,7 +2057,8 @@ do { \ "xmm20", "xmm21", "xmm22", "xmm23", \ "xmm24", "xmm25", "xmm26", "xmm27", \ "xmm28", "xmm29", "xmm30", "xmm31", \ - "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7" } + "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", \ + "bnd0", "bnd1", "bnd2", "bnd3" } #define REGISTER_NAMES HI_REGISTER_NAMES diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index f8dd5475ede..7e26c693d67 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -63,6 +63,7 @@ ;; ~ -- print "i" if TARGET_AVX2, "f" otherwise. ;; @ -- print a segment register of thread base pointer load ;; ^ -- print addr32 prefix if TARGET_64BIT and Pmode != word_mode +;; ! -- print MPX prefix for jxx/call/ret instructions if required. (define_c_enum "unspec" [ ;; Relocation specifiers @@ -78,6 +79,7 @@ UNSPEC_PLTOFF UNSPEC_MACHOPIC_OFFSET UNSPEC_PCREL + UNSPEC_SIZEOF ;; Prologue support UNSPEC_STACK_ALLOC @@ -182,6 +184,16 @@ ;; For AVX512F support UNSPEC_KMOV + + UNSPEC_BNDMK + UNSPEC_BNDMK_ADDR + UNSPEC_BNDSTX + UNSPEC_BNDLDX + UNSPEC_BNDLDX_ADDR + UNSPEC_BNDCL + UNSPEC_BNDCU + UNSPEC_BNDCN + UNSPEC_MPX_FENCE ]) (define_c_enum "unspecv" [ @@ -365,6 +377,8 @@ (MASK5_REG 74) (MASK6_REG 75) (MASK7_REG 76) + (BND0_REG 77) + (BND1_REG 78) ]) ;; Insns whose names begin with "x86_" are emitted by gen_FOO calls @@ -399,7 +413,8 @@ ssecvt,ssecvt1,sseicvt,sseins, sseshuf,sseshuf1,ssemuladd,sse4arg, lwp,mskmov,msklog, - mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft" + mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft, + mpxmov,mpxmk,mpxchk,mpxld,mpxst" (const_string "other")) ;; Main data type used by the insn @@ -435,7 +450,8 @@ ;; The (bounding maximum) length of an instruction immediate. (define_attr "length_immediate" "" (cond [(eq_attr "type" "incdec,setcc,icmov,str,lea,other,multi,idiv,leave, - bitmanip,imulx,msklog,mskmov") + bitmanip,imulx,msklog,mskmov,mpxmk,mpxmov,mpxchk, + mpxld,mpxst") (const_int 0) (eq_attr "unit" "i387,sse,mmx") (const_int 0) @@ -490,13 +506,17 @@ (const_int 0) (and (eq_attr "unit" "sse") (eq_attr "mode" "SF,DF")) (const_int 1) + (and (eq_attr "type" "ibr,call,callv") + (match_test "ix86_bnd_prefixed_insn_p (insn)")) + (const_int 1) ] (const_int 0))) ;; Set when 0f opcode prefix is used. (define_attr "prefix_0f" "" (if_then_else - (ior (eq_attr "type" "imovx,setcc,icmov,bitmanip,msklog,mskmov") + (ior (eq_attr "type" "imovx,setcc,icmov,bitmanip,msklog,mskmov, + mpxmk,mpxmov,mpxchk,mpxld,mpxst") (eq_attr "unit" "sse,mmx")) (const_int 1) (const_int 0))) @@ -599,12 +619,19 @@ ] (const_int 1))) +;; When this attribute is set, calculate total insn length from +;; length_nobnd attribute, prefixed with eventual bnd prefix byte +(define_attr "length_nobnd" "" (const_int 0)) + ;; The (bounding maximum) length of an instruction in bytes. ;; ??? fistp and frndint are in fact fldcw/{fistp,frndint}/fldcw sequences. ;; Later we may want to split them and compute proper length as for ;; other insns. (define_attr "length" "" - (cond [(eq_attr "type" "other,multi,fistp,frndint") + (cond [(eq_attr "length_nobnd" "!0") + (plus (symbol_ref ("ix86_bnd_prefixed_insn_p (insn)")) + (attr "length_nobnd")) + (eq_attr "type" "other,multi,fistp,frndint") (const_int 16) (eq_attr "type" "fcmp") (const_int 4) @@ -645,12 +672,16 @@ (define_attr "memory" "none,load,store,both,unknown" (cond [(eq_attr "type" "other,multi,str,lwp") (const_string "unknown") - (eq_attr "type" "lea,fcmov,fpspc") + (eq_attr "type" "lea,fcmov,fpspc,mpxmk,mpxchk") (const_string "none") (eq_attr "type" "fistp,leave") (const_string "both") (eq_attr "type" "frndint") (const_string "load") + (eq_attr "type" "mpxld") + (const_string "load") + (eq_attr "type" "mpxst") + (const_string "store") (eq_attr "type" "push") (if_then_else (match_operand 1 "memory_operand") (const_string "both") @@ -696,7 +727,7 @@ fmov,fcmp,fsgn, sse,ssemov,ssecmp,ssecomi,ssecvt,ssecvt1,sseicvt, sselog1,sseshuf1,sseadd1,sseiadd1,sseishft1, - mmx,mmxmov,mmxcmp,mmxcvt,mskmov,msklog") + mmx,mmxmov,mmxcmp,mmxcvt,mskmov,msklog,mpxmov") (match_operand 2 "memory_operand")) (const_string "load") (and (eq_attr "type" "icmov,ssemuladd,sse4arg") @@ -964,6 +995,21 @@ (define_mode_iterator DWIH [(SI "!TARGET_64BIT") (DI "TARGET_64BIT")]) +;; Bound modes. +(define_mode_iterator BND [(BND32 "!TARGET_LP64") + (BND64 "TARGET_LP64")]) + +;; Pointer mode corresponding to bound mode. +(define_mode_attr bnd_ptr [(BND32 "SI") (BND64 "DI")]) + +;; MPX check types +(define_int_iterator BNDCHECK [UNSPEC_BNDCL UNSPEC_BNDCU UNSPEC_BNDCN]) + +;; Check name +(define_int_attr bndcheck [(UNSPEC_BNDCL "cl") + (UNSPEC_BNDCU "cu") + (UNSPEC_BNDCN "cn")]) + ;; Instruction suffix for integer modes. (define_mode_attr imodesuffix [(QI "b") (HI "w") (SI "l") (DI "q")]) @@ -10832,10 +10878,10 @@ (label_ref (match_operand 0)) (pc)))] "" - "%+j%C1\t%l0" + "%!%+j%C1\t%l0" [(set_attr "type" "ibr") (set_attr "modrm" "0") - (set (attr "length") + (set (attr "length_nobnd") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) @@ -10850,10 +10896,10 @@ (pc) (label_ref (match_operand 0))))] "" - "%+j%c1\t%l0" + "%!%+j%c1\t%l0" [(set_attr "type" "ibr") (set_attr "modrm" "0") - (set (attr "length") + (set (attr "length_nobnd") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) @@ -11291,9 +11337,9 @@ [(set (pc) (label_ref (match_operand 0)))] "" - "jmp\t%l0" + "%!jmp\t%l0" [(set_attr "type" "ibr") - (set (attr "length") + (set (attr "length_nobnd") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -126)) (lt (minus (match_dup 0) (pc)) @@ -11313,7 +11359,7 @@ (define_insn "*indirect_jump" [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))] "" - "jmp\t%A0" + "%!jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) @@ -11362,7 +11408,7 @@ [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw")) (use (label_ref (match_operand 1)))] "" - "jmp\t%A0" + "%!jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) @@ -11907,8 +11953,8 @@ (define_insn "simple_return_internal" [(simple_return)] "reload_completed" - "ret" - [(set_attr "length" "1") + "%!ret" + [(set_attr "length_nobnd" "1") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "0") (set_attr "modrm" "0")]) @@ -11920,7 +11966,12 @@ [(simple_return) (unspec [(const_int 0)] UNSPEC_REP)] "reload_completed" - "rep%; ret" +{ + if (ix86_bnd_prefixed_insn_p (insn)) + return "%!ret"; + + return "rep%; ret"; +} [(set_attr "length" "2") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "0") @@ -11931,8 +11982,8 @@ [(simple_return) (use (match_operand:SI 0 "const_int_operand"))] "reload_completed" - "ret\t%0" - [(set_attr "length" "3") + "%!ret\t%0" + [(set_attr "length_nobnd" "3") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "2") (set_attr "modrm" "0")]) @@ -11941,7 +11992,7 @@ [(simple_return) (use (match_operand:SI 0 "register_operand" "r"))] "reload_completed" - "jmp\t%A0" + "%!jmp\t%A0" [(set_attr "type" "ibr") (set_attr "length_immediate" "0")]) @@ -18611,6 +18662,174 @@ (set_attr "atom_sse_attr" "fence") (set_attr "memory" "unknown")]) +;; MPX instructions + +(define_expand "<mode>_mk" + [(set (match_operand:BND 0 "register_operand") + (unspec:BND + [(mem:<bnd_ptr> + (match_par_dup 3 + [(match_operand:<bnd_ptr> 1 "register_operand") + (match_operand:<bnd_ptr> 2 "address_mpx_no_base_operand")]))] + UNSPEC_BNDMK))] + "TARGET_MPX" +{ + operands[3] = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, operands[1], + operands[2]), + UNSPEC_BNDMK_ADDR); +}) + +(define_insn "*<mode>_mk" + [(set (match_operand:BND 0 "register_operand" "=w") + (unspec:BND + [(match_operator:<bnd_ptr> 3 "bnd_mem_operator" + [(unspec:<bnd_ptr> + [(match_operand:<bnd_ptr> 1 "register_operand" "r") + (match_operand:<bnd_ptr> 2 "address_mpx_no_base_operand" "Tb")] + UNSPEC_BNDMK_ADDR)])] + UNSPEC_BNDMK))] + "TARGET_MPX" + "bndmk\t{%3, %0|%0, %3}" + [(set_attr "type" "mpxmk")]) + +(define_expand "mov<mode>" + [(set (match_operand:BND 0 "general_operand") + (match_operand:BND 1 "general_operand"))] + "TARGET_MPX" +{ + ix86_expand_move (<MODE>mode, operands);DONE; +}) + +(define_insn "*mov<mode>_internal_mpx" + [(set (match_operand:BND 0 "nonimmediate_operand" "=w,m") + (match_operand:BND 1 "general_operand" "wm,w"))] + "TARGET_MPX" + "bndmov\t{%1, %0|%0, %1}" + [(set_attr "type" "mpxmov")]) + +(define_expand "<mode>_<bndcheck>" + [(parallel [(unspec [(match_operand:BND 0 "register_operand") + (match_operand:<bnd_ptr> 1 "address_no_seg_operand")] BNDCHECK) + (set (match_dup 2) + (unspec:BLK [(match_dup 2)] UNSPEC_MPX_FENCE))])] + "TARGET_MPX" +{ + operands[2] = gen_rtx_MEM (BLKmode, operands[1]); + MEM_VOLATILE_P (operands[2]) = 1; +}) + +(define_insn "*<mode>_<bndcheck>" + [(parallel [(unspec [(match_operand:BND 0 "register_operand" "w") + (match_operand:<bnd_ptr> 1 "address_no_seg_operand" "Ts")] BNDCHECK) + (set (match_operand:BLK 2 "bnd_mem_operator") + (unspec:BLK [(match_dup 2)] UNSPEC_MPX_FENCE))])] + "TARGET_MPX" + "bnd<bndcheck>\t{%a1, %0|%0, %a1}" + [(set_attr "type" "mpxchk")]) + +(define_expand "<mode>_ldx" + [(parallel [(set:BND (match_operand:BND 0 "register_operand") + (unspec:BND + [(mem:<bnd_ptr> + (match_par_dup 3 + [(match_operand:<bnd_ptr> 1 "address_mpx_no_index_operand") + (match_operand:<bnd_ptr> 2 "register_operand")]))] + UNSPEC_BNDLDX)) + (use (mem:BLK (match_dup 1)))])] + "TARGET_MPX" +{ + /* Avoid registers which connot be used as index. */ + if (!index_register_operand (operands[2], Pmode)) + { + rtx temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, operands[2]); + operands[2] = temp; + } + + /* If it was a register originally then it may have + mode other than Pmode. We need to extend in such + case because bndldx may work only with Pmode regs. */ + if (GET_MODE (operands[2]) != Pmode) + operands[2] = ix86_zero_extend_to_Pmode (operands[2]); + + operands[3] = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, operands[1], + operands[2]), + UNSPEC_BNDLDX_ADDR); +}) + +(define_insn "*<mode>_ldx" + [(parallel [(set:BND (match_operand:BND 0 "register_operand" "=w") + (unspec:BND + [(match_operator:<bnd_ptr> 3 "bnd_mem_operator" + [(unspec:<bnd_ptr> + [(match_operand:<bnd_ptr> 1 "address_mpx_no_index_operand" "Ti") + (match_operand:<bnd_ptr> 2 "register_operand" "l")] + UNSPEC_BNDLDX_ADDR)])] + UNSPEC_BNDLDX)) + (use (mem:BLK (match_dup 1)))])] + "TARGET_MPX" + "bndldx\t{%3, %0|%0, %3}" + [(set_attr "type" "mpxld")]) + +(define_expand "<mode>_stx" + [(parallel [(unspec [(mem:<bnd_ptr> + (match_par_dup 3 + [(match_operand:<bnd_ptr> 0 "address_mpx_no_index_operand") + (match_operand:<bnd_ptr> 1 "register_operand")])) + (match_operand:BND 2 "register_operand")] UNSPEC_BNDSTX) + (set (match_dup 4) + (unspec:BLK [(match_dup 4)] UNSPEC_MPX_FENCE))])] + "TARGET_MPX" +{ + /* Avoid registers which connot be used as index. */ + if (!index_register_operand (operands[1], Pmode)) + { + rtx temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, operands[1]); + operands[1] = temp; + } + + /* If it was a register originally then it may have + mode other than Pmode. We need to extend in such + case because bndstx may work only with Pmode regs. */ + if (GET_MODE (operands[1]) != Pmode) + operands[1] = ix86_zero_extend_to_Pmode (operands[1]); + + operands[3] = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, operands[0], + operands[1]), + UNSPEC_BNDLDX_ADDR); + operands[4] = gen_rtx_MEM (BLKmode, operands[0]); + MEM_VOLATILE_P (operands[4]) = 1; +}) + +(define_insn "*<mode>_stx" + [(parallel [(unspec [(match_operator:<bnd_ptr> 3 "bnd_mem_operator" + [(unspec:<bnd_ptr> + [(match_operand:<bnd_ptr> 0 "address_mpx_no_index_operand" "Ti") + (match_operand:<bnd_ptr> 1 "register_operand" "l")] + UNSPEC_BNDLDX_ADDR)]) + (match_operand:BND 2 "register_operand" "w")] UNSPEC_BNDSTX) + (set (match_operand:BLK 4 "bnd_mem_operator") + (unspec:BLK [(match_dup 4)] UNSPEC_MPX_FENCE))])] + "TARGET_MPX" + "bndstx\t{%2, %3|%3, %2}" + [(set_attr "type" "mpxst")]) + +(define_insn "move_size_reloc_<mode>" + [(set (match_operand:SWI48 0 "register_operand" "=r") + (unspec:SWI48 + [(match_operand:SWI48 1 "symbol_operand")] + UNSPEC_SIZEOF))] + "TARGET_MPX" +{ + if (x86_64_immediate_size_operand (operands[1], VOIDmode)) + return "mov{l}\t{%1@SIZE, %k0|%k0, %1@SIZE}"; + else + return "movabs{q}\t{%1@SIZE, %0|%0, %1@SIZE}"; +} + [(set_attr "type" "imov") + (set_attr "mode" "<MODE>")]) + (include "mmx.md") (include "sse.md") (include "sync.md") diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index acf6b375157..5dfa9bf8b96 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -814,6 +814,10 @@ mrtm Target Report Mask(ISA_RTM) Var(ix86_isa_flags) Save Support RTM built-in functions and code generation +mmpx +Target Report Mask(ISA_MPX) Var(ix86_isa_flags) Save +Support MPX code generation + mstack-protector-guard= Target RejectNegative Joined Enum(stack_protector_guard) Var(ix86_stack_protector_guard) Init(SSP_TLS) Use given stack-protector guard diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 7d0b51e1d14..bdcdf180995 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -124,6 +124,10 @@ (match_test "TARGET_64BIT") (match_test "REGNO (op) > BX_REG"))) +;; Return true if VALUE is symbol reference +(define_predicate "symbol_operand" + (match_code "symbol_ref")) + ;; Return true if VALUE can be stored in a sign extended immediate field. (define_predicate "x86_64_immediate_operand" (match_code "const_int,symbol_ref,label_ref,const") @@ -336,6 +340,14 @@ return false; }) +;; Return true if size of VALUE can be stored in a sign +;; extended immediate field. +(define_predicate "x86_64_immediate_size_operand" + (and (match_code "symbol_ref") + (ior (not (match_test "TARGET_64BIT")) + (match_test "ix86_cmodel == CM_SMALL") + (match_test "ix86_cmodel == CM_KERNEL")))) + ;; Return true if OP is general operand representable on x86_64. (define_predicate "x86_64_general_operand" (if_then_else (match_test "TARGET_64BIT") @@ -1006,9 +1018,74 @@ return true; }) +;; Return true if op is valid MPX address operand without base +(define_predicate "address_mpx_no_base_operand" + (match_operand 0 "address_operand") +{ + struct ix86_address parts; + int ok; + + ok = ix86_decompose_address (op, &parts); + gcc_assert (ok); + + if (parts.index && parts.base) + return false; + + if (parts.seg != SEG_DEFAULT) + return false; + + /* Do not support (%rip). */ + if (parts.disp && flag_pic && TARGET_64BIT + && SYMBOLIC_CONST (parts.disp)) + { + if (GET_CODE (parts.disp) != CONST + || GET_CODE (XEXP (parts.disp, 0)) != PLUS + || GET_CODE (XEXP (XEXP (parts.disp, 0), 0)) != UNSPEC + || !CONST_INT_P (XEXP (XEXP (parts.disp, 0), 1)) + || (XINT (XEXP (XEXP (parts.disp, 0), 0), 1) != UNSPEC_DTPOFF + && XINT (XEXP (XEXP (parts.disp, 0), 0), 1) != UNSPEC_NTPOFF)) + return false; + } + + return true; +}) + +;; Return true if op is valid MPX address operand without index +(define_predicate "address_mpx_no_index_operand" + (match_operand 0 "address_operand") +{ + struct ix86_address parts; + int ok; + + ok = ix86_decompose_address (op, &parts); + gcc_assert (ok); + + if (parts.index) + return false; + + if (parts.seg != SEG_DEFAULT) + return false; + + /* Do not support (%rip). */ + if (parts.disp && flag_pic && TARGET_64BIT + && SYMBOLIC_CONST (parts.disp) + && (GET_CODE (parts.disp) != CONST + || GET_CODE (XEXP (parts.disp, 0)) != PLUS + || GET_CODE (XEXP (XEXP (parts.disp, 0), 0)) != UNSPEC + || !CONST_INT_P (XEXP (XEXP (parts.disp, 0), 1)) + || (XINT (XEXP (XEXP (parts.disp, 0), 0), 1) != UNSPEC_DTPOFF + && XINT (XEXP (XEXP (parts.disp, 0), 0), 1) != UNSPEC_NTPOFF))) + return false; + + return true; +}) + (define_predicate "vsib_mem_operator" (match_code "mem")) +(define_predicate "bnd_mem_operator" + (match_code "mem")) + ;; Return true if the rtx is known to be at least 32 bits aligned. (define_predicate "aligned_operand" (match_operand 0 "general_operand") diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c index fbcd9b06c65..3fc2f8adb83 100644 --- a/gcc/cppbuiltin.c +++ b/gcc/cppbuiltin.c @@ -107,6 +107,9 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile) flag_finite_math_only); if (flag_cilkplus) cpp_define (pfile, "__cilk=200"); + + if (flag_check_pointer_bounds) + cpp_define (pfile, "__CHKP__"); } diff --git a/gcc/dbxout.c b/gcc/dbxout.c index 78ffb145bec..aa15a395aad 100644 --- a/gcc/dbxout.c +++ b/gcc/dbxout.c @@ -2325,6 +2325,10 @@ dbxout_type (tree type, int full) dbxout_type (TREE_TYPE (type), 0); break; + case POINTER_BOUNDS_TYPE: + /* No debug info for pointer bounds type supported yet. */ + break; + default: gcc_unreachable (); } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 6db142e4d6c..cba38847e67 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -79,6 +79,7 @@ extensions, accepted by GCC in C90 mode and in C++. * x86 specific memory model extensions for transactional memory:: x86 memory models. * Object Size Checking:: Built-in functions for limited buffer overflow checking. +* Pointer Bounds Checker builtins:: Built-in functions for Pointer Bounds Checker. * Cilk Plus Builtins:: Built-in functions for the Cilk Plus language extension. * Other Builtins:: Other built-in functions. * Target Builtins:: Built-in functions specific to particular targets. @@ -2180,7 +2181,8 @@ attributes are currently defined for functions on all targets: @code{returns_nonnull}, @code{gnu_inline}, @code{externally_visible}, @code{hot}, @code{cold}, @code{artificial}, @code{no_sanitize_address}, @code{no_address_safety_analysis}, -@code{no_sanitize_undefined}, @code{no_reorder}, +@code{no_sanitize_undefined}, @code{no_reorder}, @code{bnd_legacy}, +@code{bnd_instrument}, @code{error} and @code{warning}. Several other attributes are defined for functions on particular target systems. Other attributes, including @code{section} are @@ -3702,6 +3704,18 @@ The @code{no_sanitize_undefined} attribute on functions is used to inform the compiler that it should not check for undefined behavior in the function when compiling with the @option{-fsanitize=undefined} option. +@item bnd_legacy +@cindex @code{bnd_legacy} function attribute +The @code{bnd_legacy} attribute on functions is used to inform +compiler that function should not be instrumented when compiled +with @option{-fcheck-pointer-bounds} option. + +@item bnd_instrument +@cindex @code{bnd_instrument} function attribute +The @code{bnd_instrument} attribute on functions is used to inform +compiler that function should be instrumented when compiled +with @option{-fchkp-instrument-marked-only} option. + @item regparm (@var{number}) @cindex @code{regparm} attribute @cindex functions that are passed arguments in registers on the 386 @@ -5642,11 +5656,11 @@ placed in either the @code{.bss_below100} section or the The keyword @code{__attribute__} allows you to specify special attributes of @code{struct} and @code{union} types when you define such types. This keyword is followed by an attribute specification -inside double parentheses. Seven attributes are currently defined for +inside double parentheses. Eight attributes are currently defined for types: @code{aligned}, @code{packed}, @code{transparent_union}, -@code{unused}, @code{deprecated}, @code{visibility}, and -@code{may_alias}. Other attributes are defined for functions -(@pxref{Function Attributes}), labels (@pxref{Label +@code{unused}, @code{deprecated}, @code{visibility}, @code{may_alias} +and @code{bnd_variable_size}. Other attributes are defined for +functions (@pxref{Function Attributes}), labels (@pxref{Label Attributes}) and for variables (@pxref{Variable Attributes}). You may also specify any one of these attributes with @samp{__} @@ -5952,6 +5966,35 @@ initialization will result in future breakage. GCC emits warnings based on this attribute by default; use @option{-Wno-designated-init} to suppress them. +@item bnd_variable_size +When applied to a structure field, this attribute tells Pointer +Bounds Checker that the size of this field should not be computed +using static type information. It may be used to mark variable +sized static array fields placed at the end of a structure. + +@smallexample +struct S +@{ + int size; + char data[1]; +@} +S *p = (S *)malloc (sizeof(S) + 100); +p->data[10] = 0; //Bounds violation +@end smallexample + +By using an attribute for a field we may avoid bound violation +we most probably do not want to see: + +@smallexample +struct S +@{ + int size; + char data[1] __attribute__((bnd_variable_size)); +@} +S *p = (S *)malloc (sizeof(S) + 100); +p->data[10] = 0; //OK +@end smallexample + @end table To specify multiple attributes, separate them by commas within the @@ -8610,6 +8653,176 @@ format string @var{fmt}. If the compiler is able to optimize them to @code{fputc} etc.@: functions, it does, otherwise the checking function is called and the @var{flag} argument passed to it. +@node Pointer Bounds Checker builtins +@section Pointer Bounds Checker Built-in Functions +@findex __builtin___bnd_set_ptr_bounds +@findex __builtin___bnd_narrow_ptr_bounds +@findex __builtin___bnd_copy_ptr_bounds +@findex __builtin___bnd_init_ptr_bounds +@findex __builtin___bnd_null_ptr_bounds +@findex __builtin___bnd_store_ptr_bounds +@findex __builtin___bnd_chk_ptr_lbounds +@findex __builtin___bnd_chk_ptr_ubounds +@findex __builtin___bnd_chk_ptr_bounds +@findex __builtin___bnd_get_ptr_lbound +@findex __builtin___bnd_get_ptr_ubound + +GCC provides a set of built-in functions to control Pointer Bounds Checker +instrumentation. Note that all Pointer Bounds Checker builtins are allowed +to use even if you compile with Pointer Bounds Checker off. The builtins +behavior may differ in such case as documented below. + +@deftypefn {Built-in Function} void * __builtin___bnd_set_ptr_bounds (const void * @var{q}, size_t @var{size}) + +This built-in function returns a new pointer with the value of @var{q}, and +associate it with the bounds [@var{q}, @var{q}+@var{size}-1]. With Pointer +Bounds Checker off built-in function just returns the first argument. + +@smallexample +extern void *__wrap_malloc (size_t n) +@{ + void *p = (void *)__real_malloc (n); + if (!p) return __builtin___bnd_null_ptr_bounds (p); + return __builtin___bnd_set_ptr_bounds (p, n); +@} +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} void * __builtin___bnd_narrow_ptr_bounds (const void * @var{p}, const void * @var{q}, size_t @var{size}) + +This built-in function returns a new pointer with the value of @var{p} +and associate it with the narrowed bounds formed by the intersection +of bounds associated with @var{q} and the [@var{p}, @var{p} + @var{size} - 1]. +With Pointer Bounds Checker off built-in function just returns the first +argument. + +@smallexample +void init_objects (object *objs, size_t size) +@{ + size_t i; + /* Initialize objects one-by-one passing pointers with bounds of an object, + not the full array of objects. */ + for (i = 0; i < size; i++) + init_object (__builtin___bnd_narrow_ptr_bounds (objs + i, objs, sizeof(object))); +@} +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} void * __builtin___bnd_copy_ptr_bounds (const void * @var{q}, const void * @var{r}) + +This built-in function returns a new pointer with the value of @var{q}, +and associate it with the bounds already associated with pointer @var{r}. +With Pointer Bounds Checker off built-in function just returns the first +argument. + +@smallexample +/* Here is a way to get pointer to object's field but + still with the full object's bounds. */ +int *field_ptr = __builtin___bnd_copy_ptr_bounds (&objptr->int_filed, objptr); +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} void * __builtin___bnd_init_ptr_bounds (const void * @var{q}) + +This built-in function returns a new pointer with the value of @var{q}, and +associate it with INIT (allowing full memory access) bounds. With Pointer +Bounds Checker off built-in function just returns the first argument. + +@end deftypefn + +@deftypefn {Built-in Function} void * __builtin___bnd_null_ptr_bounds (const void * @var{q}) + +This built-in function returns a new pointer with the value of @var{q}, and +associate it with NULL (allowing no memory access) bounds. With Pointer +Bounds Checker off built-in function just returns the first argument. + +@end deftypefn + +@deftypefn {Built-in Function} void __builtin___bnd_store_ptr_bounds (const void ** @var{ptr_addr}, const void * @var{ptr_val}) + +This built-in function stores the bounds associated with pointer @var{ptr_val} +and location @var{ptr_addr} into Bounds Table. This can be useful to propagate +bounds from legacy code without touching the associated pointer's memory when +pointers were copied as integers. With Pointer Bounds Checker off built-in +function call is ignored. + +@end deftypefn + +@deftypefn {Built-in Function} void __builtin___bnd_chk_ptr_lbounds (const void * @var{q}) + +This built-in function checks if the pointer @var{q} is within the lower +bound of its associated bounds. With Pointer Bounds Checker off built-in +function call is ignored. + +@smallexample +extern void *__wrap_memset (void *dst, int c, size_t len) +@{ + if (len > 0) + @{ + __builtin___bnd_chk_ptr_lbounds (dst); + __builtin___bnd_chk_ptr_ubounds ((char *)dst + len - 1); + __real_memset (dst, c, len); + @} + return dst; +@} +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} void __builtin___bnd_chk_ptr_ubounds (const void * @var{q}) + +This built-in function checks if the pointer @var{q} is within the upper +bound of its associated bounds. With Pointer Bounds Checker off built-in +function call is ignored. + +@end deftypefn + +@deftypefn {Built-in Function} void __builtin___bnd_chk_ptr_bounds (const void * @var{q}, size_t @var{size}) + +This built-in function checks if [@var{q}, @var{q} + @var{size} - 1] is within +the lower and upper bounds associated with @var{q}. With Pointer Bounds Checker +off built-in function call is ignored. + +@smallexample +extern void *__wrap_memcpy (void *dst, const void *src, size_t n) +@{ + if (n > 0) + @{ + __bnd_chk_ptr_bounds (dst, n); + __bnd_chk_ptr_bounds (src, n); + __real_memcpy (dst, src, n); + @} + return dst; +@} +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} const void * __builtin___bnd_get_ptr_lbound (const void * @var{q}) + +This built-in function returns the lower bound (which is a pointer) associated +with the pointer @var{q}. This is at least useful for debugging using printf. +With Pointer Bounds Checker off built-in function returns 0. + +@smallexample +void *lb = __builtin___bnd_get_ptr_lbound (q); +void *ub = __builtin___bnd_get_ptr_ubound (q); +printf ("q = %p lb(q) = %p ub(q) = %p", q, lb, ub); +@end smallexample + +@end deftypefn + +@deftypefn {Built-in Function} const void * __builtin___bnd_get_ptr_ubound (const void * @var{q}) + +This built-in function returns the upper bound (which is a pointer) associated +with the pointer @var{q}. With Pointer Bounds Checker off built-in function +returns -1. + +@end deftypefn + @node Cilk Plus Builtins @section Cilk Plus C/C++ language extension Built-in Functions. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b75ebc7535c..b7c8001b044 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -682,7 +682,7 @@ Objective-C and Objective-C++ Dialects}. -maes -mpclmul -mfsgsbase -mrdrnd -mf16c -mfma -mprefetchwt1 @gol -mclflushopt -mxsavec -mxsaves @gol -msse4a -m3dnow -mpopcnt -mabm -mbmi -mtbm -mfma4 -mxop -mlzcnt @gol --mbmi2 -mfxsr -mxsave -mxsaveopt -mrtm -mlwp -mthreads @gol +-mbmi2 -mfxsr -mxsave -mxsaveopt -mrtm -mlwp -mmpx -mthreads @gol -mno-align-stringops -minline-all-stringops @gol -minline-stringops-dynamically -mstringop-strategy=@var{alg} @gol -mmemcpy-strategy=@var{strategy} -mmemset-strategy=@var{strategy} @@ -10561,6 +10561,12 @@ is greater or equal to this number, use callbacks instead of inline checks. E.g. to disable inline code use @option{--param asan-instrumentation-with-call-threshold=0}. +@item chkp-max-ctor-size +Static constructors generated by Pointer Bounds Checker may become very +large and significantly increase compile time at optimization level +@option{-O1} and higher. This parameter is a maximum nubmer of statements +in a single generated constructor. Default value is 5000. + @end table @end table @@ -15774,6 +15780,8 @@ preferred alignment to @option{-mpreferred-stack-boundary=2}. @itemx -mno-xsavec @itemx -mxsaves @itemx -mno-xsaves +@itemx -mmpx +@itemx -mno-mpx @opindex mmmx @opindex mno-mmx @opindex msse @@ -15783,7 +15791,7 @@ preferred alignment to @option{-mpreferred-stack-boundary=2}. These switches enable or disable the use of instructions in the MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, AVX, AVX2, AVX512F, AVX512PF, AVX512ER, AVX512CD, SHA, AES, PCLMUL, FSGSBASE, RDRND, F16C, FMA, SSE4A, FMA4, XOP, LWP, ABM, -BMI, BMI2, FXSR, XSAVE, XSAVEOPT, LZCNT, RTM, or 3DNow!@: +BMI, BMI2, FXSR, XSAVE, XSAVEOPT, LZCNT, RTM, MPX or 3DNow!@: extended instruction sets. These extensions are also available as built-in functions: see @ref{X86 Built-in Functions}, for details of the functions enabled and diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi index 109cea58745..dd29e00f90b 100644 --- a/gcc/doc/rtl.texi +++ b/gcc/doc/rtl.texi @@ -1296,6 +1296,12 @@ These modes stand for a complex number represented as a pair of integer values. The integer values are in @code{QImode}, @code{HImode}, @code{SImode}, @code{DImode}, @code{TImode}, and @code{OImode}, respectively. + +@findex BND32mode +@findex BND64mode +@item BND32mode BND64mode +These modes stand for bounds for pointer of 32 and 64 bit size respectively. +Mode size is double pointer mode size. @end table The machine description defines @code{Pmode} as a C macro which expands @@ -1383,6 +1389,12 @@ any @code{CC_MODE} modes listed in the @file{@var{machine}-modes.def}. @xref{Jump Patterns}, also see @ref{Condition Code}. +@findex MODE_POINTER_BOUNDS +@item MODE_POINTER_BOUNDS +Pointer bounds modes. Used to represent values of pointer bounds type. +Operations in these modes may be executed as NOPs depending on hardware +features and environment setup. + @findex MODE_RANDOM @item MODE_RANDOM This is a catchall mode class for modes which don't fit into the above diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 88631a1acef..1bb31830ce1 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -3843,6 +3843,12 @@ The return value is usually either a @code{reg} RTX for the hard register in which to pass the argument, or zero to pass the argument on the stack. +The return value can be a @code{const_int} which means argument is +passed in a target specific slot with specified number. Target hooks +should be used to store or load argument in such case. See +@code{TARGET_STORE_BOUNDS_FOR_ARG} and @code{TARGET_LOAD_BOUNDS_FOR_ARG} +for more information. + The value of the expression can also be a @code{parallel} RTX@. This is used when an argument is passed in multiple locations. The mode of the @code{parallel} should be the mode of the entire argument. The @@ -4979,6 +4985,49 @@ defined, then define this hook to return @code{true} if Otherwise, you should not define this hook. @end deftypefn +@deftypefn {Target Hook} rtx TARGET_LOAD_BOUNDS_FOR_ARG (rtx @var{slot}, rtx @var{arg}, rtx @var{slot_no}) +This hook is used by expand pass to emit insn to load bounds of +@var{arg} passed in @var{slot}. Expand pass uses this hook in case +bounds of @var{arg} are not passed in register. If @var{slot} is a +memory, then bounds are loaded as for regular pointer loaded from +memory. If @var{slot} is not a memory then @var{slot_no} is an integer +constant holding number of the target dependent special slot which +should be used to obtain bounds. Hook returns RTX holding loaded bounds. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_STORE_BOUNDS_FOR_ARG (rtx @var{arg}, rtx @var{slot}, rtx @var{bounds}, rtx @var{slot_no}) +This hook is used by expand pass to emit insns to store @var{bounds} of +@var{arg} passed in @var{slot}. Expand pass uses this hook in case +@var{bounds} of @var{arg} are not passed in register. If @var{slot} is a +memory, then @var{bounds} are stored as for regular pointer stored in +memory. If @var{slot} is not a memory then @var{slot_no} is an integer +constant holding number of the target dependent special slot which +should be used to store @var{bounds}. +@end deftypefn + +@deftypefn {Target Hook} rtx TARGET_LOAD_RETURNED_BOUNDS (rtx @var{slot}) +This hook is used by expand pass to emit insn to load bounds +returned by function call in @var{slot}. Hook returns RTX holding +loaded bounds. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_STORE_RETURNED_BOUNDS (rtx @var{slot}, rtx @var{bounds}) +This hook is used by expand pass to emit insn to store @var{bounds} +returned by function call into @var{slot}. +@end deftypefn + +@deftypefn {Target Hook} rtx TARGET_CHKP_FUNCTION_VALUE_BOUNDS (const_tree @var{ret_type}, const_tree @var{fn_decl_or_type}, bool @var{outgoing}) +Define this to return an RTX representing the place where a function +returns bounds for returned pointers. Arguments meaning is similar to +@code{TARGET_FUNCTION_VALUE}. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_SETUP_INCOMING_VARARG_BOUNDS (cumulative_args_t @var{args_so_far}, enum machine_mode @var{mode}, tree @var{type}, int *@var{pretend_args_size}, int @var{second_time}) +Use it to store bounds for anonymous register arguments stored +into the stack. Arguments meaning is similar to +@code{TARGET_SETUP_INCOMING_VARARGS}. +@end deftypefn + @node Trampolines @section Trampolines for Nested Functions @cindex trampolines for nested functions @@ -10754,6 +10803,93 @@ ignored. This function should return the result of the call to the built-in function. @end deftypefn +@deftypefn {Target Hook} tree TARGET_BUILTIN_CHKP_FUNCTION (unsigned @var{fcode}) +This hook allows target to redefine built-in functions used by +Pointer Bounds Checker for code instrumentation. Hook should return +fndecl of function implementing generic builtin whose code is +passed in @var{fcode}. Currently following built-in functions are +obtained using this hook: +@deftypefn {Built-in Function} __bounds_type __chkp_bndmk (const void *@var{lb}, size_t @var{size}) +Function code - BUILT_IN_CHKP_BNDMK. This built-in function is used +by Pointer Bounds Checker to create bound values. @var{lb} holds low +bound of the resulting bounds. @var{size} holds size of created bounds. +@end deftypefn + +@deftypefn {Built-in Function} void __chkp_bndstx (const void *@var{ptr}, __bounds_type @var{b}, const void **@var{loc}) +Function code - @code{BUILT_IN_CHKP_BNDSTX}. This built-in function is used +by Pointer Bounds Checker to store bounds @var{b} for pointer @var{ptr} +when @var{ptr} is stored by address @var{loc}. +@end deftypefn + +@deftypefn {Built-in Function} __bounds_type __chkp_bndldx (const void **@var{loc}, const void *@var{ptr}) +Function code - @code{BUILT_IN_CHKP_BNDLDX}. This built-in function is used +by Pointer Bounds Checker to get bounds of pointer @var{ptr} loaded by +address @var{loc}. +@end deftypefn + +@deftypefn {Built-in Function} void __chkp_bndcl (const void *@var{ptr}, __bounds_type @var{b}) +Function code - @code{BUILT_IN_CHKP_BNDCL}. This built-in function is used +by Pointer Bounds Checker to perform check for pointer @var{ptr} against +lower bound of bounds @var{b}. +@end deftypefn + +@deftypefn {Built-in Function} void __chkp_bndcu (const void *@var{ptr}, __bounds_type @var{b}) +Function code - @code{BUILT_IN_CHKP_BNDCU}. This built-in function is used +by Pointer Bounds Checker to perform check for pointer @var{ptr} against +upper bound of bounds @var{b}. +@end deftypefn + +@deftypefn {Built-in Function} __bounds_type __chkp_bndret (void *@var{ptr}) +Function code - @code{BUILT_IN_CHKP_BNDRET}. This built-in function is used +by Pointer Bounds Checker to obtain bounds returned by a call statement. +@var{ptr} passed to built-in is @code{SSA_NAME} returned by the call. +@end deftypefn + +@deftypefn {Built-in Function} __bounds_type __chkp_intersect (__bounds_type @var{b1}, __bounds_type @var{b2}) +Function code - @code{BUILT_IN_CHKP_INTERSECT}. This built-in function +returns intersection of bounds @var{b1} and @var{b2}. +@end deftypefn + +@deftypefn {Built-in Function} __bounds_type __chkp_narrow (const void *@var{ptr}, __bounds_type @var{b}, size_t @var{s}) +Function code - @code{BUILT_IN_CHKP_NARROW}. This built-in function +returns intersection of bounds @var{b} and +[@var{ptr}, @var{ptr} + @var{s} - @code{1}]. +@end deftypefn + +@deftypefn {Built-in Function} size_t __chkp_sizeof (const void *@var{ptr}) +Function code - @code{BUILT_IN_CHKP_SIZEOF}. This built-in function +returns size of object referenced by @var{ptr}. @var{ptr} is always +@code{ADDR_EXPR} of @code{VAR_DECL}. This built-in is used by +Pointer Bounds Checker when bounds of object cannot be computed statically +(e.g. object has incomplete type). +@end deftypefn + +@deftypefn {Built-in Function} const void *__chkp_extract_lower (__bounds_type @var{b}) +Function code - @code{BUILT_IN_CHKP_EXTRACT_LOWER}. This built-in function +returns lower bound of bounds @var{b}. +@end deftypefn + +@deftypefn {Built-in Function} const void *__chkp_extract_upper (__bounds_type @var{b}) +Function code - @code{BUILT_IN_CHKP_EXTRACT_UPPER}. This built-in function +returns upper bound of bounds @var{b}. +@end deftypefn +@end deftypefn +@deftypefn {Target Hook} tree TARGET_CHKP_BOUND_TYPE (void) +Return type to be used for bounds +@end deftypefn +@deftypefn {Target Hook} {enum machine_mode} TARGET_CHKP_BOUND_MODE (void) +Return mode to be used for bounds. +@end deftypefn +@deftypefn {Target Hook} tree TARGET_CHKP_MAKE_BOUNDS_CONSTANT (HOST_WIDE_INT @var{lb}, HOST_WIDE_INT @var{ub}) +Return constant used to statically initialize constant bounds +with specified lower bound @var{lb} and upper bounds @var{ub}. +@end deftypefn +@deftypefn {Target Hook} int TARGET_CHKP_INITIALIZE_BOUNDS (tree @var{var}, tree @var{lb}, tree @var{ub}, tree *@var{stmts}) +Generate a list of statements @var{stmts} to initialize pointer +bounds variable @var{var} with bounds @var{lb} and @var{ub}. Return +the number of generated statements. +@end deftypefn + @deftypefn {Target Hook} tree TARGET_RESOLVE_OVERLOADED_BUILTIN (unsigned int @var{loc}, tree @var{fndecl}, void *@var{arglist}) Select a replacement for a machine specific built-in function that was set up by @samp{TARGET_INIT_BUILTINS}. This is done diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 856e4dc5e39..bf1595c6a2e 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -3862,6 +3862,18 @@ These machine description macros help implement varargs: @hook TARGET_PRETEND_OUTGOING_VARARGS_NAMED +@hook TARGET_LOAD_BOUNDS_FOR_ARG + +@hook TARGET_STORE_BOUNDS_FOR_ARG + +@hook TARGET_LOAD_RETURNED_BOUNDS + +@hook TARGET_STORE_RETURNED_BOUNDS + +@hook TARGET_CHKP_FUNCTION_VALUE_BOUNDS + +@hook TARGET_SETUP_INCOMING_VARARG_BOUNDS + @node Trampolines @section Trampolines for Nested Functions @cindex trampolines for nested functions @@ -7930,6 +7942,12 @@ to by @var{ce_info}. @hook TARGET_EXPAND_BUILTIN +@hook TARGET_BUILTIN_CHKP_FUNCTION +@hook TARGET_CHKP_BOUND_TYPE +@hook TARGET_CHKP_BOUND_MODE +@hook TARGET_CHKP_MAKE_BOUNDS_CONSTANT +@hook TARGET_CHKP_INITIALIZE_BOUNDS + @hook TARGET_RESOLVE_OVERLOADED_BUILTIN @hook TARGET_FOLD_BUILTIN diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 7a29aae0bf8..b16883fceb7 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -10391,6 +10391,7 @@ is_base_type (tree type) case FIXED_POINT_TYPE: case COMPLEX_TYPE: case BOOLEAN_TYPE: + case POINTER_BOUNDS_TYPE: return 1; case ARRAY_TYPE: @@ -17813,18 +17814,21 @@ gen_formal_types_die (tree function_or_method_type, dw_die_ref context_die) break; /* Output a (nameless) DIE to represent the formal parameter itself. */ - parm_die = gen_formal_parameter_die (formal_type, NULL, - true /* Emit name attribute. */, - context_die); - if (TREE_CODE (function_or_method_type) == METHOD_TYPE - && link == first_parm_type) + if (!POINTER_BOUNDS_TYPE_P (formal_type)) { - add_AT_flag (parm_die, DW_AT_artificial, 1); - if (dwarf_version >= 3 || !dwarf_strict) - add_AT_die_ref (context_die, DW_AT_object_pointer, parm_die); + parm_die = gen_formal_parameter_die (formal_type, NULL, + true /* Emit name attribute. */, + context_die); + if (TREE_CODE (function_or_method_type) == METHOD_TYPE + && link == first_parm_type) + { + add_AT_flag (parm_die, DW_AT_artificial, 1); + if (dwarf_version >= 3 || !dwarf_strict) + add_AT_die_ref (context_die, DW_AT_object_pointer, parm_die); + } + else if (arg && DECL_ARTIFICIAL (arg)) + add_AT_flag (parm_die, DW_AT_artificial, 1); } - else if (arg && DECL_ARTIFICIAL (arg)) - add_AT_flag (parm_die, DW_AT_artificial, 1); link = TREE_CHAIN (link); if (arg) @@ -18598,7 +18602,7 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) gen_formal_parameter_pack_die (generic_decl_parm, parm, subr_die, &parm); - else if (parm) + else if (parm && !POINTER_BOUNDS_P (parm)) { dw_die_ref parm_die = gen_decl_die (parm, NULL, subr_die); @@ -18610,6 +18614,8 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) parm = DECL_CHAIN (parm); } + else if (parm) + parm = DECL_CHAIN (parm); if (generic_decl_parm) generic_decl_parm = DECL_CHAIN (generic_decl_parm); @@ -20103,6 +20109,7 @@ gen_type_die_with_usage (tree type, dw_die_ref context_die, case FIXED_POINT_TYPE: case COMPLEX_TYPE: case BOOLEAN_TYPE: + case POINTER_BOUNDS_TYPE: /* No DIEs needed for fundamental types. */ break; @@ -20584,6 +20591,12 @@ gen_decl_die (tree decl, tree origin, dw_die_ref context_die) if (DECL_P (decl_or_origin) && DECL_IGNORED_P (decl_or_origin)) return NULL; + /* Ignore pointer bounds decls. */ + if (DECL_P (decl_or_origin) + && TREE_TYPE (decl_or_origin) + && POINTER_BOUNDS_P (decl_or_origin)) + return NULL; + switch (TREE_CODE (decl_or_origin)) { case ERROR_MARK: @@ -20791,7 +20804,8 @@ dwarf2out_global_decl (tree decl) declarations, file-scope (extern) function declarations (which had no corresponding body) and file-scope tagged type declarations and definitions which have not yet been forced out. */ - if (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl)) + if ((TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl)) + && !POINTER_BOUNDS_P (decl)) dwarf2out_decl (decl); } diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 1606232f462..04f677eb608 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -640,7 +640,8 @@ immed_double_const (HOST_WIDE_INT i0, HOST_WIDE_INT i1, machine_mode mode) || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT /* We can get a 0 for an error mark. */ || GET_MODE_CLASS (mode) == MODE_VECTOR_INT - || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT); + || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT + || GET_MODE_CLASS (mode) == MODE_POINTER_BOUNDS); if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) return gen_int_mode (i0, mode); @@ -6129,6 +6130,14 @@ init_emit_once (void) if (STORE_FLAG_VALUE == 1) const_tiny_rtx[1][(int) BImode] = const1_rtx; + for (mode = GET_CLASS_NARROWEST_MODE (MODE_POINTER_BOUNDS); + mode != VOIDmode; + mode = GET_MODE_WIDER_MODE (mode)) + { + wide_int wi_zero = wi::zero (GET_MODE_PRECISION (mode)); + const_tiny_rtx[0][mode] = immed_wide_int_const (wi_zero, mode); + } + pc_rtx = gen_rtx_fmt_ (PC, VOIDmode); ret_rtx = gen_rtx_fmt_ (RETURN, VOIDmode); simple_return_rtx = gen_rtx_fmt_ (SIMPLE_RETURN, VOIDmode); diff --git a/gcc/explow.c b/gcc/explow.c index 44a1223b131..6ae015cea66 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -59,7 +59,8 @@ trunc_int_for_mode (HOST_WIDE_INT c, machine_mode mode) int width = GET_MODE_PRECISION (mode); /* You want to truncate to a _what_? */ - gcc_assert (SCALAR_INT_MODE_P (mode)); + gcc_assert (SCALAR_INT_MODE_P (mode) + || POINTER_BOUNDS_MODE_P (mode)); /* Canonicalize BImode to 0 and STORE_FLAG_VALUE. */ if (mode == BImode) diff --git a/gcc/expr.c b/gcc/expr.c index f4047c418cd..f824cef3894 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -79,6 +79,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-address.h" #include "cfgexpand.h" #include "builtins.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" #ifndef STACK_PUSH_CODE #ifdef STACK_GROWS_DOWNWARD @@ -5006,9 +5008,14 @@ expand_assignment (tree to, tree from, bool nontemporal) || TREE_CODE (to) == SSA_NAME)) { rtx value; + rtx bounds; push_temp_slots (); value = expand_normal (from); + + /* Split value and bounds to store them separately. */ + chkp_split_slot (value, &value, &bounds); + if (to_rtx == 0) to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE); @@ -5042,6 +5049,15 @@ expand_assignment (tree to, tree from, bool nontemporal) emit_move_insn (to_rtx, value); } + + /* Store bounds if required. */ + if (bounds + && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to)))) + { + gcc_assert (MEM_P (to_rtx)); + chkp_emit_bounds_store (bounds, value, to_rtx); + } + preserve_temp_slots (to_rtx); pop_temp_slots (); return; @@ -5117,7 +5133,7 @@ expand_assignment (tree to, tree from, bool nontemporal) /* Compute FROM and store the value in the rtx we got. */ push_temp_slots (); - result = store_expr (from, to_rtx, 0, nontemporal); + result = store_expr_with_bounds (from, to_rtx, 0, nontemporal, to); preserve_temp_slots (result); pop_temp_slots (); return; @@ -5154,10 +5170,14 @@ emit_storent_insn (rtx to, rtx from) If CALL_PARAM_P is nonzero, this is a store into a call param on the stack, and block moves may need to be treated specially. - If NONTEMPORAL is true, try using a nontemporal store instruction. */ + If NONTEMPORAL is true, try using a nontemporal store instruction. + + If BTARGET is not NULL then computed bounds of EXP are + associated with BTARGET. */ rtx -store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) +store_expr_with_bounds (tree exp, rtx target, int call_param_p, + bool nontemporal, tree btarget) { rtx temp; rtx alt_rtl = NULL_RTX; @@ -5178,8 +5198,8 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) part. */ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL); - return store_expr (TREE_OPERAND (exp, 1), target, call_param_p, - nontemporal); + return store_expr_with_bounds (TREE_OPERAND (exp, 1), target, + call_param_p, nontemporal, btarget); } else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode) { @@ -5193,13 +5213,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) do_pending_stack_adjust (); NO_DEFER_POP; jumpifnot (TREE_OPERAND (exp, 0), lab1, -1); - store_expr (TREE_OPERAND (exp, 1), target, call_param_p, - nontemporal); + store_expr_with_bounds (TREE_OPERAND (exp, 1), target, call_param_p, + nontemporal, btarget); emit_jump_insn (gen_jump (lab2)); emit_barrier (); emit_label (lab1); - store_expr (TREE_OPERAND (exp, 2), target, call_param_p, - nontemporal); + store_expr_with_bounds (TREE_OPERAND (exp, 2), target, call_param_p, + nontemporal, btarget); emit_label (lab2); OK_DEFER_POP; @@ -5251,6 +5271,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) temp = expand_expr (exp, inner_target, VOIDmode, call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL); + /* Handle bounds returned by call. */ + if (TREE_CODE (exp) == CALL_EXPR) + { + rtx bounds; + chkp_split_slot (temp, &temp, &bounds); + if (bounds && btarget) + { + gcc_assert (TREE_CODE (btarget) == SSA_NAME); + rtx tmp = targetm.calls.load_returned_bounds (bounds); + chkp_set_rtl_bounds (btarget, tmp); + } + } + /* If TEMP is a VOIDmode constant, use convert_modes to make sure that we properly convert it. */ if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode) @@ -5332,6 +5365,19 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) (call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL), &alt_rtl, false); + + /* Handle bounds returned by call. */ + if (TREE_CODE (exp) == CALL_EXPR) + { + rtx bounds; + chkp_split_slot (temp, &temp, &bounds); + if (bounds && btarget) + { + gcc_assert (TREE_CODE (btarget) == SSA_NAME); + rtx tmp = targetm.calls.load_returned_bounds (bounds); + chkp_set_rtl_bounds (btarget, tmp); + } + } } /* If TEMP is a VOIDmode constant and the mode of the type of EXP is not @@ -5496,6 +5542,13 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) return NULL_RTX; } + +/* Same as store_expr_with_bounds but ignoring bounds of EXP. */ +rtx +store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) +{ + return store_expr_with_bounds (exp, target, call_param_p, nontemporal, NULL); +} /* Return true if field F of structure TYPE is a flexible array. */ diff --git a/gcc/expr.h b/gcc/expr.h index 5793560dd71..a188b6d6863 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -272,6 +272,7 @@ extern void expand_assignment (tree, tree, bool); and storing the value into TARGET. If SUGGEST_REG is nonzero, copy the value through a register and return that register, if that is possible. */ +extern rtx store_expr_with_bounds (tree, rtx, int, bool, tree); extern rtx store_expr (tree, rtx, int, bool); /* Given an rtx that may include add and multiply operations, diff --git a/gcc/function.c b/gcc/function.c index 91e3b2c14fb..0ffff5b77bc 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -78,6 +78,8 @@ along with GCC; see the file COPYING3. If not see #include "shrink-wrap.h" #include "toplev.h" #include "rtl-iter.h" +#include "tree-chkp.h" +#include "rtl-chkp.h" /* So we can assign to cfun in this file. */ #undef cfun @@ -2101,6 +2103,14 @@ use_register_for_decl (const_tree decl) if (TREE_ADDRESSABLE (decl)) return false; + /* Decl is implicitly addressible by bound stores and loads + if it is an aggregate holding bounds. */ + if (chkp_function_instrumented_p (current_function_decl) + && TREE_TYPE (decl) + && !BOUNDED_P (decl) + && chkp_type_has_pointer (TREE_TYPE (decl))) + return false; + /* Only register-like things go in registers. */ if (DECL_MODE (decl) == BLKmode) return false; @@ -2221,6 +2231,15 @@ struct assign_parm_data_one BOOL_BITFIELD loaded_in_reg : 1; }; +struct bounds_parm_data +{ + assign_parm_data_one parm_data; + tree bounds_parm; + tree ptr_parm; + rtx ptr_entry; + int bound_no; +}; + /* A subroutine of assign_parms. Initialize ALL. */ static void @@ -2332,6 +2351,23 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all) fnargs.safe_insert (0, decl); all->function_result_decl = decl; + + /* If function is instrumented then bounds of the + passed structure address is the second argument. */ + if (chkp_function_instrumented_p (fndecl)) + { + decl = build_decl (DECL_SOURCE_LOCATION (fndecl), + PARM_DECL, get_identifier (".result_bnd"), + pointer_bounds_type_node); + DECL_ARG_TYPE (decl) = pointer_bounds_type_node; + DECL_ARTIFICIAL (decl) = 1; + DECL_NAMELESS (decl) = 1; + TREE_CONSTANT (decl) = 1; + + DECL_CHAIN (decl) = DECL_CHAIN (all->orig_fnargs); + DECL_CHAIN (all->orig_fnargs) = decl; + fnargs.safe_insert (1, decl); + } } /* If the target wants to split complex arguments into scalars, do so. */ @@ -2472,7 +2508,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, it came in a register so that REG_PARM_STACK_SPACE isn't skipped. In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0 as it was the previous time. */ - in_regs = entry_parm != 0; + in_regs = (entry_parm != 0) || POINTER_BOUNDS_TYPE_P (data->passed_type); #ifdef STACK_PARMS_IN_REG_PARM_AREA in_regs = true; #endif @@ -2561,8 +2597,12 @@ static bool assign_parm_is_stack_parm (struct assign_parm_data_all *all, struct assign_parm_data_one *data) { + /* Bounds are never passed on the stack to keep compatibility + with not instrumented code. */ + if (POINTER_BOUNDS_TYPE_P (data->passed_type)) + return false; /* Trivially true if we've no incoming register. */ - if (data->entry_parm == NULL) + else if (data->entry_parm == NULL) ; /* Also true if we're partially in registers and partially not, since we've arranged to drop the entire argument on the stack. */ @@ -3371,6 +3411,123 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all, } } +/* Load bounds of PARM from bounds table. */ +static void +assign_parm_load_bounds (struct assign_parm_data_one *data, + tree parm, + rtx entry, + unsigned bound_no) +{ + bitmap_iterator bi; + unsigned i, offs = 0; + int bnd_no = -1; + rtx slot = NULL, ptr = NULL; + + if (parm) + { + bitmap slots; + bitmap_obstack_initialize (NULL); + slots = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (TREE_TYPE (parm), slots); + EXECUTE_IF_SET_IN_BITMAP (slots, 0, i, bi) + { + if (bound_no) + bound_no--; + else + { + bnd_no = i; + break; + } + } + BITMAP_FREE (slots); + bitmap_obstack_release (NULL); + } + + /* We may have bounds not associated with any pointer. */ + if (bnd_no != -1) + offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT; + + /* Find associated pointer. */ + if (bnd_no == -1) + { + /* If bounds are not associated with any bounds, + then it is passed in a register or special slot. */ + gcc_assert (data->entry_parm); + ptr = const0_rtx; + } + else if (MEM_P (entry)) + slot = adjust_address (entry, Pmode, offs); + else if (REG_P (entry)) + ptr = gen_rtx_REG (Pmode, REGNO (entry) + bnd_no); + else if (GET_CODE (entry) == PARALLEL) + ptr = chkp_get_value_with_offs (entry, GEN_INT (offs)); + else + gcc_unreachable (); + data->entry_parm = targetm.calls.load_bounds_for_arg (slot, ptr, + data->entry_parm); +} + +/* Assign RTL expressions to the function's bounds parameters BNDARGS. */ + +static void +assign_bounds (vec<bounds_parm_data> &bndargs, + struct assign_parm_data_all &all) +{ + unsigned i, pass, handled = 0; + bounds_parm_data *pbdata; + + if (!bndargs.exists ()) + return; + + /* We make few passes to store input bounds. Firstly handle bounds + passed in registers. After that we load bounds passed in special + slots. Finally we load bounds from Bounds Table. */ + for (pass = 0; pass < 3; pass++) + FOR_EACH_VEC_ELT (bndargs, i, pbdata) + { + /* Pass 0 => regs only. */ + if (pass == 0 + && (!pbdata->parm_data.entry_parm + || GET_CODE (pbdata->parm_data.entry_parm) != REG)) + continue; + /* Pass 1 => slots only. */ + else if (pass == 1 + && (!pbdata->parm_data.entry_parm + || GET_CODE (pbdata->parm_data.entry_parm) == REG)) + continue; + /* Pass 2 => BT only. */ + else if (pass == 2 + && pbdata->parm_data.entry_parm) + continue; + + if (!pbdata->parm_data.entry_parm + || GET_CODE (pbdata->parm_data.entry_parm) != REG) + assign_parm_load_bounds (&pbdata->parm_data, pbdata->ptr_parm, + pbdata->ptr_entry, pbdata->bound_no); + + set_decl_incoming_rtl (pbdata->bounds_parm, + pbdata->parm_data.entry_parm, false); + + if (assign_parm_setup_block_p (&pbdata->parm_data)) + assign_parm_setup_block (&all, pbdata->bounds_parm, + &pbdata->parm_data); + else if (pbdata->parm_data.passed_pointer + || use_register_for_decl (pbdata->bounds_parm)) + assign_parm_setup_reg (&all, pbdata->bounds_parm, + &pbdata->parm_data); + else + assign_parm_setup_stack (&all, pbdata->bounds_parm, + &pbdata->parm_data); + + /* Count handled bounds to make sure we miss nothing. */ + handled++; + } + + gcc_assert (handled == bndargs.length ()); + + bndargs.release (); +} + /* Assign RTL expressions to the function's parameters. This may involve copying them into registers and using those registers as the DECL_RTL. */ @@ -3380,7 +3537,11 @@ assign_parms (tree fndecl) struct assign_parm_data_all all; tree parm; vec<tree> fnargs; - unsigned i; + unsigned i, bound_no = 0; + tree last_arg = NULL; + rtx last_arg_entry = NULL; + vec<bounds_parm_data> bndargs = vNULL; + bounds_parm_data bdata; crtl->args.internal_arg_pointer = targetm.calls.internal_arg_pointer (); @@ -3422,9 +3583,6 @@ assign_parms (tree fndecl) } } - if (cfun->stdarg && !DECL_CHAIN (parm)) - assign_parms_setup_varargs (&all, &data, false); - /* Find out where the parameter arrives in this function. */ assign_parm_find_entry_rtl (&all, &data); @@ -3434,7 +3592,15 @@ assign_parms (tree fndecl) assign_parm_find_stack_rtl (parm, &data); assign_parm_adjust_entry_rtl (&data); } - + if (!POINTER_BOUNDS_TYPE_P (data.passed_type)) + { + /* Remember where last non bounds arg was passed in case + we have to load associated bounds for it from Bounds + Table. */ + last_arg = parm; + last_arg_entry = data.entry_parm; + bound_no = 0; + } /* Record permanently how this parm was passed. */ if (data.passed_pointer) { @@ -3446,20 +3612,63 @@ assign_parms (tree fndecl) else set_decl_incoming_rtl (parm, data.entry_parm, false); + /* Boudns should be loaded in the particular order to + have registers allocated correctly. Collect info about + input bounds and load them later. */ + if (POINTER_BOUNDS_TYPE_P (data.passed_type)) + { + /* Expect bounds in instrumented functions only. */ + gcc_assert (chkp_function_instrumented_p (fndecl)); + + bdata.parm_data = data; + bdata.bounds_parm = parm; + bdata.ptr_parm = last_arg; + bdata.ptr_entry = last_arg_entry; + bdata.bound_no = bound_no; + bndargs.safe_push (bdata); + } + else + { + assign_parm_adjust_stack_rtl (&data); + + if (assign_parm_setup_block_p (&data)) + assign_parm_setup_block (&all, parm, &data); + else if (data.passed_pointer || use_register_for_decl (parm)) + assign_parm_setup_reg (&all, parm, &data); + else + assign_parm_setup_stack (&all, parm, &data); + } + + if (cfun->stdarg && !DECL_CHAIN (parm)) + { + int pretend_bytes = 0; + + assign_parms_setup_varargs (&all, &data, false); + + if (chkp_function_instrumented_p (fndecl)) + { + /* We expect this is the last parm. Otherwise it is wrong + to assign bounds right now. */ + gcc_assert (i == (fnargs.length () - 1)); + assign_bounds (bndargs, all); + targetm.calls.setup_incoming_vararg_bounds (all.args_so_far, + data.promoted_mode, + data.passed_type, + &pretend_bytes, + false); + } + } + /* Update info on where next arg arrives in registers. */ targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode, data.passed_type, data.named_arg); - assign_parm_adjust_stack_rtl (&data); - - if (assign_parm_setup_block_p (&data)) - assign_parm_setup_block (&all, parm, &data); - else if (data.passed_pointer || use_register_for_decl (parm)) - assign_parm_setup_reg (&all, parm, &data); - else - assign_parm_setup_stack (&all, parm, &data); + if (POINTER_BOUNDS_TYPE_P (data.passed_type)) + bound_no++; } + assign_bounds (bndargs, all); + if (targetm.calls.split_complex_arg) assign_parms_unsplit_complex (&all, fnargs); @@ -3585,6 +3794,10 @@ assign_parms (tree fndecl) real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result), fndecl, true); + if (chkp_function_instrumented_p (fndecl)) + crtl->return_bnd + = targetm.calls.chkp_function_value_bounds (TREE_TYPE (decl_result), + fndecl, true); REG_FUNCTION_VALUE_P (real_decl_rtl) = 1; /* The delay slot scheduler assumes that crtl->return_rtx holds the hard register containing the return value, not a @@ -4815,6 +5028,14 @@ expand_function_start (tree subr) /* Set DECL_REGISTER flag so that expand_function_end will copy the result to the real return register(s). */ DECL_REGISTER (DECL_RESULT (subr)) = 1; + + if (chkp_function_instrumented_p (current_function_decl)) + { + tree return_type = TREE_TYPE (DECL_RESULT (subr)); + rtx bounds = targetm.calls.chkp_function_value_bounds (return_type, + subr, 1); + SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds); + } } /* Initialize rtx for parameters and local variables. @@ -4918,14 +5139,11 @@ expand_dummy_function_end (void) in_dummy_function = false; } -/* Call DOIT for each hard register used as a return value from - the current function. */ +/* Helper for diddle_return_value. */ void -diddle_return_value (void (*doit) (rtx, void *), void *arg) +diddle_return_value_1 (void (*doit) (rtx, void *), void *arg, rtx outgoing) { - rtx outgoing = crtl->return_rtx; - if (! outgoing) return; @@ -4945,6 +5163,16 @@ diddle_return_value (void (*doit) (rtx, void *), void *arg) } } +/* Call DOIT for each hard register used as a return value from + the current function. */ + +void +diddle_return_value (void (*doit) (rtx, void *), void *arg) +{ + diddle_return_value_1 (doit, arg, crtl->return_rtx); + diddle_return_value_1 (doit, arg, crtl->return_bnd); +} + static void do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED) { diff --git a/gcc/function.h b/gcc/function.h index 08ab761ae76..326e24b201a 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -246,6 +246,9 @@ struct GTY(()) rtl_data { result in a register, current_function_return_rtx will always be the hard register containing the result. */ rtx return_rtx; + /* If nonxero, an RTL expression for the lcoation at which the current + function returns bounds for its result. */ + rtx return_bnd; /* Vector of initial-value pairs. Each pair consists of a pseudo register of approprite mode that stores the initial value a hard diff --git a/gcc/genmodes.c b/gcc/genmodes.c index bb9dbbf508e..30fb4fc7455 100644 --- a/gcc/genmodes.c +++ b/gcc/genmodes.c @@ -336,6 +336,7 @@ complete_mode (struct mode_data *m) break; case MODE_INT: + case MODE_POINTER_BOUNDS: case MODE_FLOAT: case MODE_DECIMAL_FLOAT: case MODE_FRACT: @@ -537,6 +538,19 @@ make_special_mode (enum mode_class cl, const char *name, new_mode (cl, name, file, line); } +#define POINTER_BOUNDS_MODE(N, Y) \ + make_pointer_bounds_mode (#N, Y, __FILE__, __LINE__) + +static void ATTRIBUTE_UNUSED +make_pointer_bounds_mode (const char *name, + unsigned int bytesize, + const char *file, unsigned int line) +{ + struct mode_data *m = new_mode (MODE_POINTER_BOUNDS, name, file, line); + m->bytesize = bytesize; +} + + #define INT_MODE(N, Y) FRACTIONAL_INT_MODE (N, -1U, Y) #define FRACTIONAL_INT_MODE(N, B, Y) \ make_int_mode (#N, B, Y, __FILE__, __LINE__) diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c index 6ae781745d4..33b85d28965 100644 --- a/gcc/gimple-pretty-print.c +++ b/gcc/gimple-pretty-print.c @@ -567,11 +567,12 @@ dump_gimple_assign (pretty_printer *buffer, gimple gs, int spc, int flags) static void dump_gimple_return (pretty_printer *buffer, gimple gs, int spc, int flags) { - tree t; + tree t, t2; t = gimple_return_retval (gs); + t2 = gimple_return_retbnd (gs); if (flags & TDF_RAW) - dump_gimple_fmt (buffer, spc, flags, "%G <%T>", gs, t); + dump_gimple_fmt (buffer, spc, flags, "%G <%T %T>", gs, t, t2); else { pp_string (buffer, "return"); @@ -580,6 +581,11 @@ dump_gimple_return (pretty_printer *buffer, gimple gs, int spc, int flags) pp_space (buffer); dump_generic_node (buffer, t, spc, flags, false); } + if (t2) + { + pp_string (buffer, ", "); + dump_generic_node (buffer, t2, spc, flags, false); + } pp_semicolon (buffer); } } diff --git a/gcc/gimple.c b/gcc/gimple.c index b86f85c70ac..62f172b5a68 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -191,7 +191,7 @@ gimple_build_with_ops_stat (enum gimple_code code, unsigned subcode, gimple gimple_build_return (tree retval) { - gimple s = gimple_build_with_ops (GIMPLE_RETURN, ERROR_MARK, 1); + gimple s = gimple_build_with_ops (GIMPLE_RETURN, ERROR_MARK, 2); if (retval) gimple_return_set_retval (s, retval); return s; @@ -378,6 +378,7 @@ gimple_build_call_from_tree (tree t) gimple_call_set_va_arg_pack (call, CALL_EXPR_VA_ARG_PACK (t)); gimple_call_set_nothrow (call, TREE_NOTHROW (t)); gimple_set_no_warning (call, TREE_NO_WARNING (t)); + gimple_call_set_with_bounds (call, CALL_WITH_BOUNDS_P (t)); return call; } diff --git a/gcc/gimple.h b/gcc/gimple.h index 7f1240f7538..c7aaa815b81 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -91,6 +91,7 @@ enum gf_mask { GF_CALL_ALLOCA_FOR_VAR = 1 << 5, GF_CALL_INTERNAL = 1 << 6, GF_CALL_CTRL_ALTERING = 1 << 7, + GF_CALL_WITH_BOUNDS = 1 << 8, GF_OMP_PARALLEL_COMBINED = 1 << 0, GF_OMP_FOR_KIND_MASK = 7 << 0, GF_OMP_FOR_KIND_FOR = 0, @@ -2453,6 +2454,31 @@ gimple_call_internal_p (const_gimple gs) } +/* Return true if call GS is marked as instrumented by + Pointer Bounds Checker. */ + +static inline bool +gimple_call_with_bounds_p (const_gimple gs) +{ + GIMPLE_CHECK (gs, GIMPLE_CALL); + return (gs->subcode & GF_CALL_WITH_BOUNDS) != 0; +} + + +/* If INSTRUMENTED_P is true, marm statement GS as instrumented by + Pointer Bounds Checker. */ + +static inline void +gimple_call_set_with_bounds (gimple gs, bool with_bounds) +{ + GIMPLE_CHECK (gs, GIMPLE_CALL); + if (with_bounds) + gs->subcode |= GF_CALL_WITH_BOUNDS; + else + gs->subcode &= ~GF_CALL_WITH_BOUNDS; +} + + /* Return the target of internal call GS. */ static inline enum internal_fn @@ -5555,6 +5581,26 @@ gimple_return_set_retval (gimple gs, tree retval) } +/* Return the return bounds for GIMPLE_RETURN GS. */ + +static inline tree +gimple_return_retbnd (const_gimple gs) +{ + GIMPLE_CHECK (gs, GIMPLE_RETURN); + return gimple_op (gs, 1); +} + + +/* Set RETVAL to be the return bounds for GIMPLE_RETURN GS. */ + +static inline void +gimple_return_set_retbnd (gimple gs, tree retval) +{ + GIMPLE_CHECK (gs, GIMPLE_RETURN); + gimple_set_op (gs, 1, retval); +} + + /* Returns true when the gimple statement STMT is any of the OpenMP types. */ #define CASE_GIMPLE_OMP \ diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 2dc505a35eb..f5afc0da292 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -3862,10 +3862,19 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, individual element initialization. Also don't do this for small all-zero initializers (which aren't big enough to merit clearing), and don't try to make bitwise copies of - TREE_ADDRESSABLE types. */ + TREE_ADDRESSABLE types. + + We cannot apply such transformation when compiling chkp static + initializer because creation of initializer image in the memory + will require static initialization of bounds for it. It should + result in another gimplification of similar initializer and we + may fall into infinite loop. */ if (valid_const_initializer && !(cleared || num_nonzero_elements == 0) - && !TREE_ADDRESSABLE (type)) + && !TREE_ADDRESSABLE (type) + && (!current_function_decl + || !lookup_attribute ("chkp ctor", + DECL_ATTRIBUTES (current_function_decl)))) { HOST_WIDE_INT size = int_size_in_bytes (type); unsigned int align; diff --git a/gcc/ipa-chkp.c b/gcc/ipa-chkp.c new file mode 100644 index 00000000000..f910583907d --- /dev/null +++ b/gcc/ipa-chkp.c @@ -0,0 +1,647 @@ +/* Pointer Bounds Checker IPA passes. + 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 "stor-layout.h" +#include "tree.h" +#include "tree-pass.h" +#include "stringpool.h" +#include "bitmap.h" +#include "gimple-expr.h" +#include "tm.h" +#include "hard-reg-set.h" +#include "function.h" +#include "is-a.h" +#include "tree-ssa-alias.h" +#include "predict.h" +#include "basic-block.h" +#include "gimple.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include "tree-chkp.h" +#include "ipa-chkp.h" +#include <string> + +/* Pointer Bounds Checker has two IPA passes to support code instrumentation. + + In instrumented code each pointer is provided with bounds. For input + pointer parameters it means we also have bounds passed. For calls it + means we have additional bounds arguments for pointer arguments. + + To have all IPA optimizations working correctly we have to express + dataflow between passed and received bounds explicitly via additional + entries in function declaration arguments list and in function type. + Since we may have both instrumented and not instrumented code at the + same time, we cannot replace all original functions with their + instrumented variants. Therefore we create clones (versions) instead. + + Instrumentation clones creation is a separate IPA pass which is a part + of early local passes. Clones are created after SSA is built (because + instrumentation pass works on SSA) and before any transformations + which may change pointer flow and therefore lead to incorrect code + instrumentation (possibly causing false bounds check failures). + + Instrumentation clones have pointer bounds arguments added right after + pointer arguments. Clones have assembler name of the original + function with suffix added. New assembler name is in transparent + alias chain with the original name. Thus we expect all calls to the + original and instrumented functions look similar in assembler. + + During instrumentation versioning pass we create instrumented versions + of all function with body and also for all their aliases and thunks. + Clones for functions with no body are created on demand (usually + during call instrumentation). + + Original and instrumented function nodes are connected with IPA + reference IPA_REF_CHKP. It is mostly done to have reachability + analysis working correctly. We may have no references to the + instrumented function in the code but it still should be counted + as reachable if the original function is reachable. + + When original function bodies are not needed anymore we release + them and transform functions into a special kind of thunks. Each + thunk has a call edge to the instrumented version. These thunks + help to keep externally visible instrumented functions visible + when linker resolution files are used. Linker has no info about + connection between original and instrumented function and + therefore we may wrongly decide (due to difference in assembler + names) that instrumented function version is local and can be + removed. */ + +#define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_" + +/* Build a clone of FNDECL with a modified name. */ + +static tree +chkp_build_instrumented_fndecl (tree fndecl) +{ + tree new_decl = copy_node (fndecl); + tree new_name; + std::string s; + + /* called_as_built_in checks DECL_NAME to identify calls to + builtins. We want instrumented calls to builtins to be + recognized by called_as_built_in. Therefore use original + DECL_NAME for cloning with no prefixes. */ + s = IDENTIFIER_POINTER (DECL_NAME (fndecl)); + s += ".chkp"; + DECL_NAME (new_decl) = get_identifier (s.c_str ()); + + /* References to the original and to the instrumented version + should look the same in the output assembly. And we cannot + use the same assembler name for the instrumented version + because it conflicts with decl merging algorithms in LTO. + Achieve the result by using transparent alias name for the + instrumented version. */ + s = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)); + s += ".chkp"; + new_name = get_identifier (s.c_str ()); + IDENTIFIER_TRANSPARENT_ALIAS (new_name) = 1; + TREE_CHAIN (new_name) = DECL_ASSEMBLER_NAME (fndecl); + SET_DECL_ASSEMBLER_NAME (new_decl, new_name); + + /* For functions with body versioning will make a copy of arguments. + For functions with no body we need to do it here. */ + if (!gimple_has_body_p (fndecl)) + DECL_ARGUMENTS (new_decl) = copy_list (DECL_ARGUMENTS (fndecl)); + + /* We are going to modify attributes list and therefore should + make own copy. */ + DECL_ATTRIBUTES (new_decl) = copy_list (DECL_ATTRIBUTES (fndecl)); + + return new_decl; +} + + +/* Fix operands of attribute from ATTRS list named ATTR_NAME. + Integer operands are replaced with values according to + INDEXES map having LEN elements. For operands out of len + we just add DELTA. */ + +static void +chkp_map_attr_arg_indexes (tree attrs, const char *attr_name, + unsigned *indexes, int len, int delta) +{ + tree attr = lookup_attribute (attr_name, attrs); + tree op; + + if (!attr) + return; + + TREE_VALUE (attr) = copy_list (TREE_VALUE (attr)); + for (op = TREE_VALUE (attr); op; op = TREE_CHAIN (op)) + { + int idx; + + if (TREE_CODE (TREE_VALUE (op)) != INTEGER_CST) + continue; + + idx = TREE_INT_CST_LOW (TREE_VALUE (op)); + + /* If idx exceeds indexes length then we just + keep it at the same distance from the last + known arg. */ + if (idx > len) + idx += delta; + else + idx = indexes[idx - 1] + 1; + TREE_VALUE (op) = build_int_cst (TREE_TYPE (TREE_VALUE (op)), idx); + } +} + +/* Make a copy of function type ORIG_TYPE adding pointer + bounds as additional arguments. */ + +tree +chkp_copy_function_type_adding_bounds (tree orig_type) +{ + tree type; + tree arg_type, attrs, t; + unsigned len = list_length (TYPE_ARG_TYPES (orig_type)); + unsigned *indexes = XALLOCAVEC (unsigned, len); + unsigned idx = 0, new_idx = 0; + + for (arg_type = TYPE_ARG_TYPES (orig_type); + arg_type; + arg_type = TREE_CHAIN (arg_type)) + if (TREE_VALUE (arg_type) == void_type_node) + continue; + else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) + || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), + TREE_VALUE (arg_type), true) + || chkp_type_has_pointer (TREE_VALUE (arg_type))) + break; + + /* We may use original type if there are no bounds passed. */ + if (!arg_type) + return orig_type; + + type = copy_node (orig_type); + TYPE_ARG_TYPES (type) = copy_list (TYPE_ARG_TYPES (type)); + + for (arg_type = TYPE_ARG_TYPES (type); + arg_type; + arg_type = TREE_CHAIN (arg_type)) + { + indexes[idx++] = new_idx++; + + /* pass_by_reference returns 1 for void type, + so check for it first. */ + if (TREE_VALUE (arg_type) == void_type_node) + continue; + else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) + || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), + TREE_VALUE (arg_type), true)) + { + tree new_type = build_tree_list (NULL_TREE, + pointer_bounds_type_node); + TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); + TREE_CHAIN (arg_type) = new_type; + + arg_type = TREE_CHAIN (arg_type); + new_idx++; + } + else if (chkp_type_has_pointer (TREE_VALUE (arg_type))) + { + bitmap slots = BITMAP_ALLOC (NULL); + bitmap_iterator bi; + unsigned bnd_no; + + chkp_find_bound_slots (TREE_VALUE (arg_type), slots); + + EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) + { + tree new_type = build_tree_list (NULL_TREE, + pointer_bounds_type_node); + TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); + TREE_CHAIN (arg_type) = new_type; + + arg_type = TREE_CHAIN (arg_type); + new_idx++; + } + BITMAP_FREE (slots); + } + } + + /* If function type has attribute with arg indexes then + we have to copy it fixing attribute ops. Map for + fixing is in indexes array. */ + attrs = TYPE_ATTRIBUTES (type); + if (lookup_attribute ("nonnull", attrs) + || lookup_attribute ("format", attrs) + || lookup_attribute ("format_arg", attrs)) + { + int delta = new_idx - len; + attrs = copy_list (TYPE_ATTRIBUTES (type)); + chkp_map_attr_arg_indexes (attrs, "nonnull", indexes, len, delta); + chkp_map_attr_arg_indexes (attrs, "format", indexes, len, delta); + chkp_map_attr_arg_indexes (attrs, "format_arg", indexes, len, delta); + TYPE_ATTRIBUTES (type) = attrs; + } + + t = TYPE_MAIN_VARIANT (orig_type); + if (orig_type != t) + { + TYPE_MAIN_VARIANT (type) = t; + TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t); + TYPE_NEXT_VARIANT (t) = type; + } + else + { + TYPE_MAIN_VARIANT (type) = type; + TYPE_NEXT_VARIANT (type) = NULL; + } + + + return type; +} + +/* For given function FNDECL add bounds arguments to arguments + list. */ + +static void +chkp_add_bounds_params_to_function (tree fndecl) +{ + tree arg; + + for (arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg)) + if (BOUNDED_P (arg)) + { + std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; + tree new_arg; + + if (DECL_NAME (arg)) + new_name += IDENTIFIER_POINTER (DECL_NAME (arg)); + else + { + char uid[25]; + snprintf (uid, 25, "D.%u", DECL_UID (arg)); + new_name += uid; + } + + new_arg = build_decl (DECL_SOURCE_LOCATION (arg), PARM_DECL, + get_identifier (new_name.c_str ()), + pointer_bounds_type_node); + DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; + DECL_CONTEXT (new_arg) = DECL_CONTEXT (arg); + DECL_ARTIFICIAL (new_arg) = 1; + DECL_CHAIN (new_arg) = DECL_CHAIN (arg); + DECL_CHAIN (arg) = new_arg; + + arg = DECL_CHAIN (arg); + + } + else if (chkp_type_has_pointer (TREE_TYPE (arg))) + { + tree orig_arg = arg; + bitmap slots = BITMAP_ALLOC (NULL); + bitmap_iterator bi; + unsigned bnd_no; + + chkp_find_bound_slots (TREE_TYPE (arg), slots); + + EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) + { + std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; + tree new_arg; + char offs[25]; + + if (DECL_NAME (orig_arg)) + new_name += IDENTIFIER_POINTER (DECL_NAME (orig_arg)); + else + { + snprintf (offs, 25, "D.%u", DECL_UID (arg)); + new_name += offs; + } + snprintf (offs, 25, "__%u", bnd_no * POINTER_SIZE / BITS_PER_UNIT); + + new_arg = build_decl (DECL_SOURCE_LOCATION (orig_arg), + PARM_DECL, + get_identifier (new_name.c_str ()), + pointer_bounds_type_node); + DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; + DECL_CONTEXT (new_arg) = DECL_CONTEXT (orig_arg); + DECL_ARTIFICIAL (new_arg) = 1; + DECL_CHAIN (new_arg) = DECL_CHAIN (arg); + DECL_CHAIN (arg) = new_arg; + + arg = DECL_CHAIN (arg); + } + BITMAP_FREE (slots); + } + + TREE_TYPE (fndecl) = + chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl)); +} + +/* Return clone created for instrumentation of NODE or NULL. */ + +cgraph_node * +chkp_maybe_create_clone (tree fndecl) +{ + cgraph_node *node = cgraph_node::get_create (fndecl); + cgraph_node *clone = node->instrumented_version; + + gcc_assert (!node->instrumentation_clone); + + if (!clone) + { + tree new_decl = chkp_build_instrumented_fndecl (fndecl); + struct cgraph_edge *e; + struct ipa_ref *ref; + int i; + + clone = node->create_version_clone (new_decl, vNULL, NULL); + clone->externally_visible = node->externally_visible; + clone->local = node->local; + clone->address_taken = node->address_taken; + clone->thunk = node->thunk; + clone->alias = node->alias; + clone->weakref = node->weakref; + clone->cpp_implicit_alias = node->cpp_implicit_alias; + clone->instrumented_version = node; + clone->orig_decl = fndecl; + clone->instrumentation_clone = true; + node->instrumented_version = clone; + + if (gimple_has_body_p (fndecl)) + { + /* If function will not be instrumented, then it's instrumented + version is a thunk for the original. */ + if (lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)) + || (flag_chkp_instrument_marked_only + && !lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl)))) + { + clone->thunk.thunk_p = true; + clone->thunk.add_pointer_bounds_args = true; + clone->create_edge (node, NULL, 0, CGRAPH_FREQ_BASE); + } + else + { + tree_function_versioning (fndecl, new_decl, NULL, false, + NULL, false, NULL, NULL); + clone->lowered = true; + } + } + + /* New params are inserted after versioning because it + actually copies args list from the original decl. */ + chkp_add_bounds_params_to_function (new_decl); + + /* Clones have the same comdat group as originals. */ + if (node->same_comdat_group + || DECL_ONE_ONLY (node->decl)) + clone->add_to_same_comdat_group (node); + + if (gimple_has_body_p (fndecl)) + symtab->call_cgraph_insertion_hooks (clone); + + /* Clone all aliases. */ + for (i = 0; node->iterate_referring (i, ref); i++) + if (ref->use == IPA_REF_ALIAS) + { + struct cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring); + struct cgraph_node *chkp_alias + = chkp_maybe_create_clone (alias->decl); + chkp_alias->create_reference (clone, IPA_REF_ALIAS, NULL); + } + + /* Clone all thunks. */ + for (e = node->callers; e; e = e->next_caller) + if (e->caller->thunk.thunk_p) + { + struct cgraph_node *thunk + = chkp_maybe_create_clone (e->caller->decl); + /* Redirect thunk clone edge to the node clone. */ + thunk->callees->redirect_callee (clone); + } + + /* For aliases and thunks we should make sure target is cloned + to have proper references and edges. */ + if (node->thunk.thunk_p) + chkp_maybe_create_clone (node->callees->callee->decl); + else if (node->alias) + { + struct cgraph_node *target; + + ref = node->ref_list.first_reference (); + if (ref) + chkp_maybe_create_clone (ref->referred->decl); + + if (node->alias_target) + { + if (TREE_CODE (node->alias_target) == FUNCTION_DECL) + { + target = chkp_maybe_create_clone (node->alias_target); + clone->alias_target = target->decl; + } + else + clone->alias_target = node->alias_target; + } + } + + /* Add IPA reference. It's main role is to keep instrumented + version reachable while original node is reachable. */ + ref = node->create_reference (clone, IPA_REF_CHKP, NULL); + } + + return clone; +} + +/* Create clone for all functions to be instrumented. */ + +static unsigned int +chkp_versioning (void) +{ + struct cgraph_node *node; + + bitmap_obstack_initialize (NULL); + + FOR_EACH_DEFINED_FUNCTION (node) + { + if (!node->instrumentation_clone + && !node->instrumented_version + && !node->alias + && !node->thunk.thunk_p + && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node->decl)) + && (!flag_chkp_instrument_marked_only + || lookup_attribute ("bnd_instrument", + DECL_ATTRIBUTES (node->decl))) + /* No builtins instrumentation for now. */ + && DECL_BUILT_IN_CLASS (node->decl) == NOT_BUILT_IN) + chkp_maybe_create_clone (node->decl); + } + + /* Mark all aliases and thunks of functions with no instrumented + version as legacy function. */ + FOR_EACH_DEFINED_FUNCTION (node) + { + if (!node->instrumentation_clone + && !node->instrumented_version + && (node->alias || node->thunk.thunk_p) + && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node->decl))) + DECL_ATTRIBUTES (node->decl) + = tree_cons (get_identifier ("bnd_legacy"), NULL, + DECL_ATTRIBUTES (node->decl)); + } + + bitmap_obstack_release (NULL); + + return 0; +} + +/* In this pass we remove bodies of functions having + instrumented version. Functions with removed bodies + become a special kind of thunks to provide a connection + between calls to the original version and instrumented + function. */ + +static unsigned int +chkp_produce_thunks (void) +{ + struct cgraph_node *node; + + FOR_EACH_DEFINED_FUNCTION (node) + { + if (!node->instrumentation_clone + && node->instrumented_version + && gimple_has_body_p (node->decl) + && gimple_has_body_p (node->instrumented_version->decl)) + { + node->release_body (); + node->remove_callees (); + node->remove_all_references (); + + node->thunk.thunk_p = true; + node->thunk.add_pointer_bounds_args = true; + node->create_edge (node->instrumented_version, NULL, + 0, CGRAPH_FREQ_BASE); + node->create_reference (node->instrumented_version, + IPA_REF_CHKP, NULL); + } + } + + /* Mark instrumentation clones created for aliases and thunks + as insttrumented so they could be removed as unreachable + now. */ + FOR_EACH_DEFINED_FUNCTION (node) + { + if (node->instrumentation_clone + && (node->alias || node->thunk.thunk_p) + && !chkp_function_instrumented_p (node->decl)) + chkp_function_mark_instrumented (node->decl); + } + + symtab->remove_unreachable_nodes (true, dump_file); + + return 0; +} + +const pass_data pass_data_ipa_chkp_versioning = +{ + SIMPLE_IPA_PASS, /* type */ + "chkp_versioning", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ +}; + +const pass_data pass_data_ipa_chkp_produce_thunks = +{ + SIMPLE_IPA_PASS, /* type */ + "chkp_cleanup", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ +}; + +class pass_ipa_chkp_versioning : public simple_ipa_opt_pass +{ +public: + pass_ipa_chkp_versioning (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_chkp_versioning, ctxt) + {} + + /* opt_pass methods: */ + virtual opt_pass * clone () + { + return new pass_ipa_chkp_versioning (m_ctxt); + } + + virtual bool gate (function *) + { + return flag_check_pointer_bounds; + } + + virtual unsigned int execute (function *) + { + return chkp_versioning (); + } + +}; // class pass_ipa_chkp_versioning + +class pass_ipa_chkp_produce_thunks : public simple_ipa_opt_pass +{ +public: + pass_ipa_chkp_produce_thunks (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_chkp_produce_thunks, ctxt) + {} + + /* opt_pass methods: */ + virtual opt_pass * clone () + { + return new pass_ipa_chkp_produce_thunks (m_ctxt); + } + + virtual bool gate (function *) + { + return flag_check_pointer_bounds; + } + + virtual unsigned int execute (function *) + { + return chkp_produce_thunks (); + } + +}; // class pass_chkp_produce_thunks + +simple_ipa_opt_pass * +make_pass_ipa_chkp_versioning (gcc::context *ctxt) +{ + return new pass_ipa_chkp_versioning (ctxt); +} + +simple_ipa_opt_pass * +make_pass_ipa_chkp_produce_thunks (gcc::context *ctxt) +{ + return new pass_ipa_chkp_produce_thunks (ctxt); +} diff --git a/gcc/ipa-chkp.h b/gcc/ipa-chkp.h new file mode 100644 index 00000000000..d4ad113893a --- /dev/null +++ b/gcc/ipa-chkp.h @@ -0,0 +1,26 @@ +/* Declaration of interface functions of Pointer Bounds Checker. + Copyright (C) 2014 Free Software Foundation, Inc. + +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/>. */ + +#ifndef GCC_IPA_CHKP_H +#define GCC_IPA_CHKP_H + +extern tree chkp_copy_function_type_adding_bounds (tree orig_type); +extern cgraph_node *chkp_maybe_create_clone (tree fndecl); + +#endif /* GCC_IPA_CHKP_H */ diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 3331dca7f8b..47f9f5cea09 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -716,7 +716,7 @@ initialize_node_lattices (struct cgraph_node *node) int i; gcc_checking_assert (node->has_gimple_body_p ()); - if (!node->local.local) + if (!cgraph_local_p (node)) { /* When cloning is allowed, we can assume that externally visible functions are not called. We will compensate this by cloning @@ -1464,6 +1464,24 @@ propagate_constants_accross_call (struct cgraph_edge *cs) if (parms_count == 0) return false; + /* No propagation through instrumentation thunks is available yet. + It should be possible with proper mapping of call args and + instrumented callee params in the propagation loop below. But + this case mostly occurs when legacy code calls instrumented code + and it is not a primary target for optimizations. + We detect instrumentation thunks in aliases and thunks chain by + checking instrumentation_clone flag for chain source and target. + Going through instrumentation thunks we always have it changed + from 0 to 1 and all other nodes do not change it. */ + if (!cs->callee->instrumentation_clone + && callee->instrumentation_clone) + { + for (i = 0; i < parms_count; i++) + ret |= set_all_contains_variable (ipa_get_parm_lattices (callee_info, + i)); + return ret; + } + /* If this call goes through a thunk we must not propagate to the first (0th) parameter. However, we might need to uncover a thunk from below a series of aliases first. */ diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c index a278a6262bd..84cc0ca0ba1 100644 --- a/gcc/ipa-icf.c +++ b/gcc/ipa-icf.c @@ -579,7 +579,8 @@ sem_function::merge (sem_item *alias_item) redirect_callers = (!original_discardable && alias->get_availability () > AVAIL_INTERPOSABLE - && original->get_availability () > AVAIL_INTERPOSABLE); + && original->get_availability () > AVAIL_INTERPOSABLE + && !alias->instrumented_version); } else { @@ -1200,6 +1201,7 @@ sem_variable::merge (sem_item *alias_item) alias->analyzed = false; DECL_INITIAL (alias->decl) = NULL; + alias->need_bounds_init = false; alias->remove_all_references (); varpool_node::create_alias (alias_var->decl, decl); diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index fff386bb739..534b3303193 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -2453,11 +2453,15 @@ early_inliner (function *fun) info that might be cleared out for newly discovered edges. */ for (edge = node->callees; edge; edge = edge->next_callee) { - struct inline_edge_summary *es = inline_edge_summary (edge); - es->call_stmt_size - = estimate_num_insns (edge->call_stmt, &eni_size_weights); - es->call_stmt_time - = estimate_num_insns (edge->call_stmt, &eni_time_weights); + /* We have no summary for new bound store calls yet. */ + if (inline_edge_summary_vec.length () > (unsigned)edge->uid) + { + struct inline_edge_summary *es = inline_edge_summary (edge); + es->call_stmt_size + = estimate_num_insns (edge->call_stmt, &eni_size_weights); + es->call_stmt_time + = estimate_num_insns (edge->call_stmt, &eni_time_weights); + } if (edge->callee->decl && !gimple_check_call_matching_types ( edge->call_stmt, edge->callee->decl, false)) diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 74aefafce81..d6999fa6712 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -1326,6 +1326,7 @@ propagate_pure_const (void) fprintf (dump_file, " global var write\n"); break; case IPA_REF_ADDR: + case IPA_REF_CHKP: break; default: gcc_unreachable (); diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h index 6709933596e..ed75c008599 100644 --- a/gcc/ipa-ref.h +++ b/gcc/ipa-ref.h @@ -32,7 +32,8 @@ enum GTY(()) ipa_ref_use IPA_REF_LOAD, IPA_REF_STORE, IPA_REF_ADDR, - IPA_REF_ALIAS + IPA_REF_ALIAS, + IPA_REF_CHKP }; /* Record of reference in callgraph or varpool. */ @@ -57,7 +58,7 @@ public: gimple stmt; unsigned int lto_stmt_uid; unsigned int referred_index; - ENUM_BITFIELD (ipa_ref_use) use:2; + ENUM_BITFIELD (ipa_ref_use) use:3; unsigned int speculative:1; }; diff --git a/gcc/ipa-split.c b/gcc/ipa-split.c index ac68c87f282..98dbc63e2fb 100644 --- a/gcc/ipa-split.c +++ b/gcc/ipa-split.c @@ -127,6 +127,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-pretty-print.h" #include "ipa-inline.h" #include "cfgloop.h" +#include "tree-chkp.h" /* Per basic block info. */ @@ -168,6 +169,7 @@ struct split_point best_split_point; static bitmap forbidden_dominators; static tree find_retval (basic_block return_bb); +static tree find_retbnd (basic_block return_bb); /* Callback for walk_stmt_load_store_addr_ops. If T is non-SSA automatic variable, check it if it is present in bitmap passed via DATA. */ @@ -413,6 +415,21 @@ dominated_by_forbidden (basic_block bb) return false; } +/* For give split point CURRENT and return block RETURN_BB return 1 + if ssa name VAL is set by split part and 0 otherwise. */ +static bool +split_part_set_ssa_name_p (tree val, struct split_point *current, + basic_block return_bb) +{ + if (TREE_CODE (val) != SSA_NAME) + return false; + + return (!SSA_NAME_IS_DEFAULT_DEF (val) + && (bitmap_bit_p (current->split_bbs, + gimple_bb (SSA_NAME_DEF_STMT (val))->index) + || gimple_bb (SSA_NAME_DEF_STMT (val)) == return_bb)); +} + /* We found an split_point CURRENT. NON_SSA_VARS is bitmap of all non ssa variables used and RETURN_BB is return basic block. See if we can split function here. */ @@ -430,6 +447,7 @@ consider_split (struct split_point *current, bitmap non_ssa_vars, unsigned int i; int incoming_freq = 0; tree retval; + tree retbnd; bool back_edge = false; if (dump_file && (dump_flags & TDF_DETAILS)) @@ -618,10 +636,7 @@ consider_split (struct split_point *current, bitmap non_ssa_vars, = bitmap_bit_p (non_ssa_vars, DECL_UID (SSA_NAME_VAR (retval))); else if (TREE_CODE (retval) == SSA_NAME) current->split_part_set_retval - = (!SSA_NAME_IS_DEFAULT_DEF (retval) - && (bitmap_bit_p (current->split_bbs, - gimple_bb (SSA_NAME_DEF_STMT (retval))->index) - || gimple_bb (SSA_NAME_DEF_STMT (retval)) == return_bb)); + = split_part_set_ssa_name_p (retval, current, return_bb); else if (TREE_CODE (retval) == PARM_DECL) current->split_part_set_retval = false; else if (TREE_CODE (retval) == VAR_DECL @@ -631,6 +646,29 @@ consider_split (struct split_point *current, bitmap non_ssa_vars, else current->split_part_set_retval = true; + /* See if retbnd used by return bb is computed by header or split part. */ + retbnd = find_retbnd (return_bb); + if (retbnd) + { + bool split_part_set_retbnd + = split_part_set_ssa_name_p (retbnd, current, return_bb); + + /* If we have both return value and bounds then keep their definitions + in a single function. We use SSA names to link returned bounds and + value and therefore do not handle cases when result is passed by + reference (which should not be our case anyway since bounds are + returned for pointers only). */ + if ((DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)) + && current->split_part_set_retval) + || split_part_set_retbnd != current->split_part_set_retval) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + " Refused: split point splits return value and bounds\n"); + return; + } + } + /* split_function fixes up at most one PHI non-virtual PHI node in return_bb, for the return value. If there are other PHIs, give up. */ if (return_bb != EXIT_BLOCK_PTR_FOR_FN (cfun)) @@ -753,6 +791,18 @@ find_retval (basic_block return_bb) return NULL; } +/* Given return basic block RETURN_BB, see where return bounds are really + stored. */ +static tree +find_retbnd (basic_block return_bb) +{ + gimple_stmt_iterator bsi; + for (bsi = gsi_last_bb (return_bb); !gsi_end_p (bsi); gsi_prev (&bsi)) + if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN) + return gimple_return_retbnd (gsi_stmt (bsi)); + return NULL; +} + /* Callback for walk_stmt_load_store_addr_ops. If T is non-SSA automatic variable, mark it as used in bitmap passed via DATA. Return true when access to T prevents splitting the function. */ @@ -1123,6 +1173,19 @@ find_split_points (int overall_time, int overall_size) BITMAP_FREE (current.ssa_names_to_pass); } +/* Build and insert initialization of returned bounds RETBND + for returned value RETVAL. Statements are inserted after + a statement pointed by GSI and GSI is modified to point to + the last inserted statement. */ + +static void +insert_bndret_call_after (tree retbnd, tree retval, gimple_stmt_iterator *gsi) +{ + tree fndecl = targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET); + gimple bndret = gimple_build_call (fndecl, 1, retval); + gimple_call_set_lhs (bndret, retbnd); + gsi_insert_after (gsi, bndret, GSI_CONTINUE_LINKING); +} /* Split function at SPLIT_POINT. */ static void @@ -1139,8 +1202,9 @@ split_function (struct split_point *split_point) gimple call; edge e; edge_iterator ei; - tree retval = NULL, real_retval = NULL; + tree retval = NULL, real_retval = NULL, retbnd = NULL; bool split_part_return_p = false; + bool with_bounds = chkp_function_instrumented_p (current_function_decl); gimple last_stmt = NULL; unsigned int i; tree arg, ddef; @@ -1289,6 +1353,12 @@ split_function (struct split_point *split_point) DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN; DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0; } + + /* If the original function is instrumented then it's + part is also instrumented. */ + if (with_bounds) + chkp_function_mark_instrumented (node->decl); + /* If the original function is declared inline, there is no point in issuing a warning for the non-inlinable part. */ DECL_NO_INLINE_WARNING_P (node->decl) = 1; @@ -1323,6 +1393,7 @@ split_function (struct split_point *split_point) args_to_pass[i] = arg; } call = gimple_build_call_vec (node->decl, args_to_pass); + gimple_call_set_with_bounds (call, with_bounds); gimple_set_block (call, DECL_INITIAL (current_function_decl)); args_to_pass.release (); @@ -1429,6 +1500,7 @@ split_function (struct split_point *split_point) if (return_bb != EXIT_BLOCK_PTR_FOR_FN (cfun)) { real_retval = retval = find_retval (return_bb); + retbnd = find_retbnd (return_bb); if (real_retval && split_point->split_part_set_retval) { @@ -1473,6 +1545,21 @@ split_function (struct split_point *split_point) } update_stmt (gsi_stmt (bsi)); } + + /* Replace retbnd with new one. */ + if (retbnd) + { + gimple_stmt_iterator bsi; + for (bsi = gsi_last_bb (return_bb); !gsi_end_p (bsi); + gsi_prev (&bsi)) + if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN) + { + retbnd = copy_ssa_name (retbnd, call); + gimple_return_set_retbnd (gsi_stmt (bsi), retbnd); + update_stmt (gsi_stmt (bsi)); + break; + } + } } if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))) { @@ -1494,6 +1581,9 @@ split_function (struct split_point *split_point) gsi_insert_after (&gsi, cpy, GSI_NEW_STMT); retval = tem; } + /* Build bndret call to obtain returned bounds. */ + if (retbnd) + insert_bndret_call_after (retbnd, retval, &gsi); gimple_call_set_lhs (call, retval); update_stmt (call); } @@ -1512,6 +1602,10 @@ split_function (struct split_point *split_point) { retval = DECL_RESULT (current_function_decl); + if (chkp_function_instrumented_p (current_function_decl) + && BOUNDED_P (retval)) + retbnd = create_tmp_reg (pointer_bounds_type_node, NULL); + /* We use temporary register to hold value when aggregate_value_p is false. Similarly for DECL_BY_REFERENCE we must avoid extra copy. */ @@ -1535,6 +1629,9 @@ split_function (struct split_point *split_point) gimple_call_set_lhs (call, retval); } gsi_insert_after (&gsi, call, GSI_NEW_STMT); + /* Build bndret call to obtain returned bounds. */ + if (retbnd) + insert_bndret_call_after (retbnd, retval, &gsi); ret = gimple_build_return (retval); gsi_insert_after (&gsi, ret, GSI_NEW_STMT); } diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c index b0823fff8c9..210777d9b2d 100644 --- a/gcc/ipa-visibility.c +++ b/gcc/ipa-visibility.c @@ -270,6 +270,10 @@ cgraph_externally_visible_p (struct cgraph_node *node, if (MAIN_NAME_P (DECL_NAME (node->decl))) return true; + if (node->instrumentation_clone + && MAIN_NAME_P (DECL_NAME (node->orig_decl))) + return true; + return false; } @@ -554,6 +558,7 @@ function_and_variable_visibility (bool whole_program) } if (node->thunk.thunk_p + && !node->thunk.add_pointer_bounds_args && TREE_PUBLIC (node->decl)) { struct cgraph_node *decl_node = node; diff --git a/gcc/ipa.c b/gcc/ipa.c index 8562102ea80..a6086d808b0 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -226,7 +226,13 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets, if (inline_summary_vec) inline_update_overall_summary (node); else if (edge->call_stmt) - edge->redirect_call_stmt_to_callee (); + { + edge->redirect_call_stmt_to_callee (); + + /* Call to __builtin_unreachable shouldn't be instrumented. */ + if (!targets.length ()) + gimple_call_set_with_bounds (edge->call_stmt, false); + } } } } @@ -507,6 +513,12 @@ symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) node->remove_from_same_comdat_group (); node->remove_all_references (); changed = true; + if (node->thunk.thunk_p + && node->thunk.add_pointer_bounds_args) + { + node->thunk.thunk_p = false; + node->thunk.add_pointer_bounds_args = false; + } } } else @@ -556,7 +568,8 @@ symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) changed = true; } /* Keep body if it may be useful for constant folding. */ - if ((init = ctor_for_folding (vnode->decl)) == error_mark_node) + if ((init = ctor_for_folding (vnode->decl)) == error_mark_node + && !POINTER_BOUNDS_P (vnode->decl)) vnode->remove_initializer (); else DECL_INITIAL (vnode->decl) = init; @@ -581,7 +594,10 @@ symbol_table::remove_unreachable_nodes (bool before_inlining_p, FILE *file) && !node->used_from_other_partition) { if (!node->call_for_symbol_thunks_and_aliases - (has_addr_references_p, NULL, true)) + (has_addr_references_p, NULL, true) + && (!node->instrumentation_clone + || !node->instrumented_version + || !node->instrumented_version->address_taken)) { if (file) fprintf (file, " %s", node->name ()); @@ -644,6 +660,8 @@ process_references (varpool_node *vnode, process_references (dyn_cast<varpool_node *> (ref->referring), written, address_taken, read, explicit_refs); break; + case IPA_REF_CHKP: + gcc_unreachable (); } } @@ -782,9 +800,11 @@ make_pass_ipa_free_inline_summary (gcc::context *ctxt) } /* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor) or 'D' (for a destructor). BODY - is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the - initialization priority for this constructor or destructor. + be one of 'I' (for a constructor), 'D' (for a destructor), 'P' + (for chp static vars constructor) or 'B' (for chkp static bounds + constructor). BODY is a STATEMENT_LIST containing GENERIC + statements. PRIORITY is the initialization priority for this + constructor or destructor. FINAL specify whether the externally visible name for collect2 should be produced. */ @@ -843,6 +863,20 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final) DECL_STATIC_CONSTRUCTOR (decl) = 1; decl_init_priority_insert (decl, priority); break; + case 'P': + DECL_STATIC_CONSTRUCTOR (decl) = 1; + DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("chkp ctor"), + NULL, + NULL_TREE); + decl_init_priority_insert (decl, priority); + break; + case 'B': + DECL_STATIC_CONSTRUCTOR (decl) = 1; + DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("bnd_legacy"), + NULL, + NULL_TREE); + decl_init_priority_insert (decl, priority); + break; case 'D': DECL_STATIC_DESTRUCTOR (decl) = 1; decl_fini_priority_insert (decl, priority); @@ -860,9 +894,11 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final) } /* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor) or 'D' (for a destructor). BODY - is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the - initialization priority for this constructor or destructor. */ + be one of 'I' (for a constructor), 'D' (for a destructor), 'P' + (for chkp static vars constructor) or 'B' (for chkp static bounds + constructor). BODY is a STATEMENT_LIST containing GENERIC + statements. PRIORITY is the initialization priority for this + constructor or destructor. */ void cgraph_build_static_cdtor (char which, tree body, int priority) diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c index 28ec341f565..da1f0e44682 100644 --- a/gcc/lto-cgraph.c +++ b/gcc/lto-cgraph.c @@ -552,6 +552,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1); bp_pack_enum (&bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN, node->resolution); + bp_pack_value (&bp, node->instrumentation_clone, 1); streamer_write_bitpack (&bp); streamer_write_data_stream (ob->main_stream, section, strlen (section) + 1); @@ -560,7 +561,8 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, streamer_write_uhwi_stream (ob->main_stream, 1 + (node->thunk.this_adjusting != 0) * 2 - + (node->thunk.virtual_offset_p != 0) * 4); + + (node->thunk.virtual_offset_p != 0) * 4 + + (node->thunk.add_pointer_bounds_args != 0) * 8); streamer_write_uhwi_stream (ob->main_stream, node->thunk.fixed_offset); streamer_write_uhwi_stream (ob->main_stream, node->thunk.virtual_value); } @@ -569,6 +571,9 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, streamer_write_hwi_stream (ob->main_stream, node->get_init_priority ()); if (DECL_STATIC_DESTRUCTOR (node->decl)) streamer_write_hwi_stream (ob->main_stream, node->get_fini_priority ()); + + if (node->instrumentation_clone) + lto_output_fn_decl_index (ob->decl_state, ob->main_stream, node->orig_decl); } /* Output the varpool NODE to OB. @@ -623,6 +628,7 @@ lto_output_varpool_node (struct lto_simple_output_block *ob, varpool_node *node, } bp_pack_value (&bp, node->tls_model, 3); bp_pack_value (&bp, node->used_by_single_function, 1); + bp_pack_value (&bp, node->need_bounds_init, 1); streamer_write_bitpack (&bp); group = node->get_comdat_group (); @@ -667,7 +673,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref, struct cgraph_node *node; bp = bitpack_create (ob->main_stream); - bp_pack_value (&bp, ref->use, 2); + bp_pack_value (&bp, ref->use, 3); bp_pack_value (&bp, ref->speculative, 1); streamer_write_bitpack (&bp); nref = lto_symtab_encoder_lookup (encoder, ref->referred); @@ -875,7 +881,8 @@ compute_ltrans_boundary (lto_symtab_encoder_t in_encoder) { if (!lto_symtab_encoder_encode_initializer_p (encoder, vnode) - && vnode->ctor_useable_for_folding_p ()) + && (vnode->ctor_useable_for_folding_p () + || POINTER_BOUNDS_P (vnode->decl))) { lto_set_symtab_encoder_encode_initializer (encoder, vnode); create_references (encoder, vnode); @@ -1093,6 +1100,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->thunk.thunk_p = bp_unpack_value (bp, 1); node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN); + node->instrumentation_clone = bp_unpack_value (bp, 1); gcc_assert (flag_ltrans || (!node->in_other_partition && !node->used_from_other_partition)); @@ -1215,6 +1223,7 @@ input_node (struct lto_file_decl_data *file_data, node->thunk.this_adjusting = (type & 2); node->thunk.virtual_value = virtual_value; node->thunk.virtual_offset_p = (type & 4); + node->thunk.add_pointer_bounds_args = (type & 8); } if (node->alias && !node->analyzed && node->weakref) node->alias_target = get_alias_symbol (node->decl); @@ -1223,6 +1232,14 @@ input_node (struct lto_file_decl_data *file_data, node->set_init_priority (streamer_read_hwi (ib)); if (DECL_STATIC_DESTRUCTOR (node->decl)) node->set_fini_priority (streamer_read_hwi (ib)); + + if (node->instrumentation_clone) + { + decl_index = streamer_read_uhwi (ib); + fn_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index); + node->orig_decl = fn_decl; + } + return node; } @@ -1282,6 +1299,7 @@ input_varpool_node (struct lto_file_decl_data *file_data, node->alias_target = get_alias_symbol (node->decl); node->tls_model = (enum tls_model)bp_unpack_value (&bp, 3); node->used_by_single_function = (enum tls_model)bp_unpack_value (&bp, 1); + node->need_bounds_init = bp_unpack_value (&bp, 1); group = read_identifier (ib); if (group) { @@ -1319,7 +1337,7 @@ input_ref (struct lto_input_block *ib, struct ipa_ref *ref; bp = streamer_read_bitpack (ib); - use = (enum ipa_ref_use) bp_unpack_value (&bp, 2); + use = (enum ipa_ref_use) bp_unpack_value (&bp, 3); speculative = (enum ipa_ref_use) bp_unpack_value (&bp, 1); node = nodes[streamer_read_hwi (ib)]; ref = referring_node->create_reference (node, use); @@ -1462,6 +1480,22 @@ input_cgraph_1 (struct lto_file_decl_data *file_data, = dyn_cast<cgraph_node *> (nodes[ref]); else cnode->global.inlined_to = NULL; + + /* Compute instrumented_version. */ + if (cnode->instrumentation_clone) + { + gcc_assert (cnode->orig_decl); + + cnode->instrumented_version = cgraph_node::get (cnode->orig_decl); + if (cnode->instrumented_version) + cnode->instrumented_version->instrumented_version = cnode; + + /* Restore decl names reference. */ + if (IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (cnode->decl)) + && !TREE_CHAIN (DECL_ASSEMBLER_NAME (cnode->decl))) + TREE_CHAIN (DECL_ASSEMBLER_NAME (cnode->decl)) + = DECL_ASSEMBLER_NAME (cnode->orig_decl); + } } ref = (int) (intptr_t) node->same_comdat_group; diff --git a/gcc/lto/lto-partition.c b/gcc/lto/lto-partition.c index c38931b3f95..0e1a95bf918 100644 --- a/gcc/lto/lto-partition.c +++ b/gcc/lto/lto-partition.c @@ -108,8 +108,9 @@ add_references_to_partition (ltrans_partition part, symtab_node *node) Recursively look into the initializers of the constant variable and add references, too. */ else if (is_a <varpool_node *> (ref->referred) - && dyn_cast <varpool_node *> (ref->referred) - ->ctor_useable_for_folding_p () + && (dyn_cast <varpool_node *> (ref->referred) + ->ctor_useable_for_folding_p () + || POINTER_BOUNDS_P (ref->referred->decl)) && !lto_symtab_encoder_in_partition_p (part->encoder, ref->referred)) { if (!part->initializers_visited) @@ -176,6 +177,11 @@ add_symbol_to_partition_1 (ltrans_partition part, symtab_node *node) for (e = cnode->callers; e; e = e->next_caller) if (e->caller->thunk.thunk_p) add_symbol_to_partition_1 (part, e->caller); + + /* Instrumented version is actually the same function. + Therefore put it into the same partition. */ + if (cnode->instrumented_version) + add_symbol_to_partition_1 (part, cnode->instrumented_version); } add_references_to_partition (part, node); @@ -782,6 +788,7 @@ privatize_symbol_name (symtab_node *node) { tree decl = node->decl; const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + cgraph_node *cnode; /* Our renaming machinery do not handle more than one change of assembler name. We should not need more than one anyway. */ @@ -812,6 +819,18 @@ privatize_symbol_name (symtab_node *node) lto_record_renamed_decl (node->lto_file_data, name, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + /* We could change name which is a target of transparent alias + chain of instrumented function name. Fix alias chain if so .*/ + if ((cnode = dyn_cast <cgraph_node *> (node)) + && !cnode->instrumentation_clone + && cnode->instrumented_version + && cnode->instrumented_version->orig_decl == decl) + { + tree iname = DECL_ASSEMBLER_NAME (cnode->instrumented_version->decl); + + gcc_assert (IDENTIFIER_TRANSPARENT_ALIAS (iname)); + TREE_CHAIN (iname) = DECL_ASSEMBLER_NAME (decl); + } if (symtab->dump_file) fprintf (symtab->dump_file, "Privatizing symbol name: %s -> %s\n", diff --git a/gcc/machmode.h b/gcc/machmode.h index 940b953f8d7..52a4e7c5224 100644 --- a/gcc/machmode.h +++ b/gcc/machmode.h @@ -174,6 +174,9 @@ extern const unsigned char mode_class[NUM_MACHINE_MODES]; || CLASS == MODE_ACCUM \ || CLASS == MODE_UACCUM) +#define POINTER_BOUNDS_MODE_P(MODE) \ + (GET_MODE_CLASS (MODE) == MODE_POINTER_BOUNDS) + /* Get the size in bytes and bits of an object of mode MODE. */ extern CONST_MODE_SIZE unsigned char mode_size[NUM_MACHINE_MODES]; diff --git a/gcc/mode-classes.def b/gcc/mode-classes.def index 9c6a8bbc0c2..b645484724c 100644 --- a/gcc/mode-classes.def +++ b/gcc/mode-classes.def @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see DEF_MODE_CLASS (MODE_CC), /* condition code in a register */ \ DEF_MODE_CLASS (MODE_INT), /* integer */ \ DEF_MODE_CLASS (MODE_PARTIAL_INT), /* integer with padding bits */ \ + DEF_MODE_CLASS (MODE_POINTER_BOUNDS), /* bounds */ \ DEF_MODE_CLASS (MODE_FRACT), /* signed fractional number */ \ DEF_MODE_CLASS (MODE_UFRACT), /* unsigned fractional number */ \ DEF_MODE_CLASS (MODE_ACCUM), /* signed accumulator */ \ diff --git a/gcc/params.def b/gcc/params.def index beff7e63212..73f5643031e 100644 --- a/gcc/params.def +++ b/gcc/params.def @@ -1107,6 +1107,12 @@ DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS, "Maximum number of nested calls to search for control dependencies " "during uninitialized variable analysis", 1000, 1, 0) + +DEFPARAM (PARAM_CHKP_MAX_CTOR_SIZE, + "chkp-max-ctor-size", + "Maximum number of statements to be included into a single static " + "constructor generated by Pointer Bounds Checker", + 5000, 100, 0) /* Local variables: diff --git a/gcc/passes.c b/gcc/passes.c index 9ec0ebfc629..5e91a79414a 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -140,7 +140,9 @@ opt_pass::opt_pass (const pass_data &data, context *ctxt) void pass_manager::execute_early_local_passes () { - execute_pass_list (cfun, pass_early_local_passes_1->sub); + execute_pass_list (cfun, pass_build_ssa_passes_1->sub); + execute_pass_list (cfun, pass_chkp_instrumentation_passes_1->sub); + execute_pass_list (cfun, pass_local_optimization_passes_1->sub); } unsigned int @@ -332,7 +334,7 @@ finish_optimization_passes (void) } static unsigned int -execute_all_early_local_passes (void) +execute_build_ssa_passes (void) { /* Once this pass (and its sub-passes) are complete, all functions will be in SSA form. Technically this state change is happening @@ -347,10 +349,10 @@ execute_all_early_local_passes (void) namespace { -const pass_data pass_data_early_local_passes = +const pass_data pass_data_build_ssa_passes = { SIMPLE_IPA_PASS, /* type */ - "early_local_cleanups", /* name */ + "build_ssa_passes", /* name */ OPTGROUP_NONE, /* optinfo_flags */ TV_EARLY_LOCAL, /* tv_id */ 0, /* properties_required */ @@ -362,11 +364,11 @@ const pass_data pass_data_early_local_passes = 0, /* todo_flags_finish */ }; -class pass_early_local_passes : public simple_ipa_opt_pass +class pass_build_ssa_passes : public simple_ipa_opt_pass { public: - pass_early_local_passes (gcc::context *ctxt) - : simple_ipa_opt_pass (pass_data_early_local_passes, ctxt) + pass_build_ssa_passes (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_build_ssa_passes, ctxt) {} /* opt_pass methods: */ @@ -378,17 +380,87 @@ public: virtual unsigned int execute (function *) { - return execute_all_early_local_passes (); + return execute_build_ssa_passes (); } -}; // class pass_early_local_passes +}; // class pass_build_ssa_passes + +const pass_data pass_data_chkp_instrumentation_passes = +{ + SIMPLE_IPA_PASS, /* type */ + "chkp_passes", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_chkp_instrumentation_passes : public simple_ipa_opt_pass +{ +public: + pass_chkp_instrumentation_passes (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_chkp_instrumentation_passes, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + /* Don't bother doing anything if the program has errors. */ + return (!seen_error () && !in_lto_p); + } + +}; // class pass_chkp_instrumentation_passes + +const pass_data pass_data_local_optimization_passes = +{ + SIMPLE_IPA_PASS, /* type */ + "opt_local_passes", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_local_optimization_passes : public simple_ipa_opt_pass +{ +public: + pass_local_optimization_passes (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_local_optimization_passes, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + /* Don't bother doing anything if the program has errors. */ + return (!seen_error () && !in_lto_p); + } + +}; // class pass_local_optimization_passes } // anon namespace simple_ipa_opt_pass * -make_pass_early_local_passes (gcc::context *ctxt) +make_pass_build_ssa_passes (gcc::context *ctxt) +{ + return new pass_build_ssa_passes (ctxt); +} + +simple_ipa_opt_pass * +make_pass_chkp_instrumentation_passes (gcc::context *ctxt) +{ + return new pass_chkp_instrumentation_passes (ctxt); +} + +simple_ipa_opt_pass * +make_pass_local_optimization_passes (gcc::context *ctxt) { - return new pass_early_local_passes (ctxt); + return new pass_local_optimization_passes (ctxt); } namespace { diff --git a/gcc/passes.def b/gcc/passes.def index 6f6a4931bc5..2305d670663 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -49,14 +49,27 @@ along with GCC; see the file COPYING3. If not see INSERT_PASSES_AFTER (all_small_ipa_passes) NEXT_PASS (pass_ipa_free_lang_data); NEXT_PASS (pass_ipa_function_and_variable_visibility); - NEXT_PASS (pass_early_local_passes); - PUSH_INSERT_PASSES_WITHIN (pass_early_local_passes) + NEXT_PASS (pass_ipa_chkp_versioning); + NEXT_PASS (pass_build_ssa_passes); + PUSH_INSERT_PASSES_WITHIN (pass_build_ssa_passes) NEXT_PASS (pass_fixup_cfg); NEXT_PASS (pass_init_datastructures); - NEXT_PASS (pass_build_ssa); NEXT_PASS (pass_ubsan); NEXT_PASS (pass_early_warn_uninitialized); + POP_INSERT_PASSES () + + NEXT_PASS (pass_chkp_instrumentation_passes); + PUSH_INSERT_PASSES_WITHIN (pass_chkp_instrumentation_passes) + NEXT_PASS (pass_fixup_cfg); + NEXT_PASS (pass_chkp); + NEXT_PASS (pass_rebuild_cgraph_edges); + POP_INSERT_PASSES () + NEXT_PASS (pass_ipa_chkp_produce_thunks); + + NEXT_PASS (pass_local_optimization_passes); + PUSH_INSERT_PASSES_WITHIN (pass_local_optimization_passes) + NEXT_PASS (pass_fixup_cfg); NEXT_PASS (pass_rebuild_cgraph_edges); NEXT_PASS (pass_inline_parameters); NEXT_PASS (pass_early_inline); @@ -78,13 +91,13 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_early_ipa_sra); NEXT_PASS (pass_tail_recursion); NEXT_PASS (pass_convert_switch); - NEXT_PASS (pass_cleanup_eh); - NEXT_PASS (pass_profile); - NEXT_PASS (pass_local_pure_const); + NEXT_PASS (pass_cleanup_eh); + NEXT_PASS (pass_profile); + NEXT_PASS (pass_local_pure_const); /* Split functions creates parts that are not run through early optimizations again. It is thus good idea to do this - late. */ - NEXT_PASS (pass_split_functions); + late. */ + NEXT_PASS (pass_split_functions); POP_INSERT_PASSES () NEXT_PASS (pass_release_ssa_names); NEXT_PASS (pass_rebuild_cgraph_edges); @@ -154,6 +167,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_fre); NEXT_PASS (pass_merge_phi); NEXT_PASS (pass_vrp); + NEXT_PASS (pass_chkp_opt); NEXT_PASS (pass_dce); NEXT_PASS (pass_call_cdce); NEXT_PASS (pass_cselim); diff --git a/gcc/rtl-chkp.c b/gcc/rtl-chkp.c new file mode 100644 index 00000000000..2d56ba29f4e --- /dev/null +++ b/gcc/rtl-chkp.c @@ -0,0 +1,307 @@ +/* RTL manipulation functions exported by Pointer Bounds Checker. + 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 "expr.h" +#include "target.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "is-a.h" +#include "predict.h" +#include "basic-block.h" +#include "tree.h" +#include "gimple-expr.h" +#include "gimple.h" +#include "bitmap.h" +#include "rtl-chkp.h" +#include "tree-chkp.h" +#include "hash-map.h" + +static hash_map<tree, rtx> *chkp_rtx_bounds_map; + +/* Get bounds rtx associated with NODE via + chkp_set_rtl_bounds call. */ +rtx +chkp_get_rtl_bounds (tree node) +{ + rtx *slot; + + if (!chkp_rtx_bounds_map) + return NULL_RTX; + + slot = chkp_rtx_bounds_map->get (node); + return slot ? *slot : NULL_RTX; +} + +/* Associate bounds rtx VAL with NODE. */ +void +chkp_set_rtl_bounds (tree node, rtx val) +{ + if (!chkp_rtx_bounds_map) + chkp_rtx_bounds_map = new hash_map<tree, rtx>; + + chkp_rtx_bounds_map->put (node, val); +} + +/* Reset all bounds stored via chkp_set_rtl_bounds. */ +void +chkp_reset_rtl_bounds () +{ + if (!chkp_rtx_bounds_map) + return; + + delete chkp_rtx_bounds_map; + chkp_rtx_bounds_map = NULL; +} + +/* Split SLOT identifying slot for function value or + argument into two parts SLOT_VAL and SLOT_BND. + First is the slot for regular value and the other one is + for bounds. */ +void +chkp_split_slot (rtx slot, rtx *slot_val, rtx *slot_bnd) +{ + int i; + int val_num = 0; + int bnd_num = 0; + rtx *val_tmps; + rtx *bnd_tmps; + + *slot_bnd = 0; + + if (!slot + || GET_CODE (slot) != PARALLEL) + { + *slot_val = slot; + return; + } + + val_tmps = XALLOCAVEC (rtx, XVECLEN (slot, 0)); + bnd_tmps = XALLOCAVEC (rtx, XVECLEN (slot, 0)); + + for (i = 0; i < XVECLEN (slot, 0); i++) + { + rtx elem = XVECEXP (slot, 0, i); + rtx reg = GET_CODE (elem) == EXPR_LIST ? XEXP (elem, 0) : elem; + + if (!reg) + continue; + + if (POINTER_BOUNDS_MODE_P (GET_MODE (reg)) || CONST_INT_P (reg)) + bnd_tmps[bnd_num++] = elem; + else + val_tmps[val_num++] = elem; + } + + gcc_assert (val_num); + + if (!bnd_num) + { + *slot_val = slot; + return; + } + + if ((GET_CODE (val_tmps[0]) == EXPR_LIST) || (val_num > 1)) + *slot_val = gen_rtx_PARALLEL (GET_MODE (slot), + gen_rtvec_v (val_num, val_tmps)); + else + *slot_val = val_tmps[0]; + + if ((GET_CODE (bnd_tmps[0]) == EXPR_LIST) || (bnd_num > 1)) + *slot_bnd = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec_v (bnd_num, bnd_tmps)); + else + *slot_bnd = bnd_tmps[0]; +} + +/* Join previously splitted to VAL and BND rtx for function + value or argument and return it. */ +rtx +chkp_join_splitted_slot (rtx val, rtx bnd) +{ + rtx res; + int i, n = 0; + + if (!bnd) + return val; + + if (GET_CODE (val) == PARALLEL) + n += XVECLEN (val, 0); + else + n++; + + if (GET_CODE (bnd) == PARALLEL) + n += XVECLEN (bnd, 0); + else + n++; + + res = gen_rtx_PARALLEL (GET_MODE (val), rtvec_alloc (n)); + + n = 0; + + if (GET_CODE (val) == PARALLEL) + for (i = 0; i < XVECLEN (val, 0); i++) + XVECEXP (res, 0, n++) = XVECEXP (val, 0, i); + else + XVECEXP (res, 0, n++) = val; + + if (GET_CODE (bnd) == PARALLEL) + for (i = 0; i < XVECLEN (bnd, 0); i++) + XVECEXP (res, 0, n++) = XVECEXP (bnd, 0, i); + else + XVECEXP (res, 0, n++) = bnd; + + return res; +} + +/* If PAR is PARALLEL holding registers then transform + it into PARALLEL holding EXPR_LISTs of those regs + and zero constant (similar to how function value + on multiple registers looks like). */ +void +chkp_put_regs_to_expr_list (rtx par) +{ + int n; + + if (GET_CODE (par) != PARALLEL + || GET_CODE (XVECEXP (par, 0, 0)) == EXPR_LIST) + return; + + for (n = 0; n < XVECLEN (par, 0); n++) + XVECEXP (par, 0, n) = gen_rtx_EXPR_LIST (VOIDmode, + XVECEXP (par, 0, n), + const0_rtx); +} + +/* Search rtx PAR describing function return value for an + item related to value at offset OFFS and return it. + Return NULL if item was not found. */ +rtx +chkp_get_value_with_offs (rtx par, rtx offs) +{ + int n; + + gcc_assert (GET_CODE (par) == PARALLEL); + + for (n = 0; n < XVECLEN (par, 0); n++) + { + rtx par_offs = XEXP (XVECEXP (par, 0, n), 1); + if (INTVAL (offs) == INTVAL (par_offs)) + return XEXP (XVECEXP (par, 0, n), 0); + } + + return NULL; +} + +/* Emit instructions to store BOUNDS for pointer VALUE + stored in MEM. + Function is used by expand to pass bounds for args + passed on stack. */ +void +chkp_emit_bounds_store (rtx bounds, rtx value, rtx mem) +{ + gcc_assert (MEM_P (mem)); + + if (REG_P (bounds) || CONST_INT_P (bounds)) + { + rtx ptr; + + if (REG_P (value)) + ptr = value; + else + { + rtx slot = adjust_address (value, Pmode, 0); + ptr = gen_reg_rtx (Pmode); + emit_move_insn (ptr, slot); + } + + if (CONST_INT_P (bounds)) + bounds = targetm.calls.load_bounds_for_arg (value, ptr, bounds); + + targetm.calls.store_bounds_for_arg (ptr, mem, + bounds, NULL); + } + else + { + int i; + + gcc_assert (GET_CODE (bounds) == PARALLEL); + gcc_assert (GET_CODE (value) == PARALLEL || MEM_P (value) || REG_P (value)); + + for (i = 0; i < XVECLEN (bounds, 0); i++) + { + rtx reg = XEXP (XVECEXP (bounds, 0, i), 0); + rtx offs = XEXP (XVECEXP (bounds, 0, i), 1); + rtx slot = adjust_address (mem, Pmode, INTVAL (offs)); + rtx ptr; + + if (GET_CODE (value) == PARALLEL) + ptr = chkp_get_value_with_offs (value, offs); + else if (MEM_P (value)) + { + rtx tmp = adjust_address (value, Pmode, INTVAL (offs)); + ptr = gen_reg_rtx (Pmode); + emit_move_insn (ptr, tmp); + } + else + ptr = gen_rtx_SUBREG (Pmode, value, INTVAL (offs)); + + targetm.calls.store_bounds_for_arg (ptr, slot, reg, NULL); + } + } +} + +/* Emit code to copy bounds for structure VALUE of type TYPE + copied to SLOT. */ +void +chkp_copy_bounds_for_stack_parm (rtx slot, rtx value, tree type) +{ + bitmap have_bound; + bitmap_iterator bi; + unsigned i; + rtx tmp = NULL, bnd; + + gcc_assert (TYPE_SIZE (type)); + gcc_assert (MEM_P (value)); + gcc_assert (MEM_P (slot)); + gcc_assert (RECORD_OR_UNION_TYPE_P (type)); + + bitmap_obstack_initialize (NULL); + have_bound = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (type, have_bound); + + EXECUTE_IF_SET_IN_BITMAP (have_bound, 0, i, bi) + { + rtx ptr = adjust_address (value, Pmode, i * POINTER_SIZE / 8); + rtx to = adjust_address (slot, Pmode, i * POINTER_SIZE / 8); + + if (!tmp) + tmp = gen_reg_rtx (Pmode); + + emit_move_insn (tmp, ptr); + bnd = targetm.calls.load_bounds_for_arg (ptr, tmp, NULL); + targetm.calls.store_bounds_for_arg (tmp, to, bnd, NULL); + } + + BITMAP_FREE (have_bound); + bitmap_obstack_release (NULL); +} diff --git a/gcc/rtl-chkp.h b/gcc/rtl-chkp.h new file mode 100644 index 00000000000..5b77db90460 --- /dev/null +++ b/gcc/rtl-chkp.h @@ -0,0 +1,38 @@ +/* Declaration of interface functions of Pointer Bounds Checker. + Copyright (C) 2014 Free Software Foundation, Inc. + +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/>. */ + +#ifndef GCC_RTL_CHKP_H +#define GCC_RTL_CHKP_H + +#define DECL_BOUNDS_RTL(NODE) (chkp_get_rtl_bounds (DECL_WRTL_CHECK (NODE))) + +#define SET_DECL_BOUNDS_RTL(NODE, VAL) \ + (chkp_set_rtl_bounds (DECL_WRTL_CHECK (NODE), VAL)) + +extern rtx chkp_get_rtl_bounds (tree node); +extern void chkp_set_rtl_bounds (tree node, rtx val); +extern void chkp_reset_rtl_bounds (); +extern void chkp_split_slot (rtx slot, rtx *slot_val, rtx *slot_bnd); +extern rtx chkp_join_splitted_slot (rtx val, rtx bnd); +extern rtx chkp_get_value_with_offs (rtx par, rtx offs); +extern void chkp_copy_bounds_for_stack_parm (rtx slot, rtx value, tree type); +extern void chkp_emit_bounds_store (rtx bounds, rtx value, rtx mem); +extern void chkp_put_regs_to_expr_list (rtx par); + +#endif /* GCC_RTL_CHKP_H */ diff --git a/gcc/rtl.h b/gcc/rtl.h index 09d20e2b544..c697d553e95 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -297,7 +297,8 @@ struct GTY((desc("0"), tag("0"), In a CODE_LABEL, part of the two-bit alternate entry field. 1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c. 1 in a VALUE is SP_BASED_VALUE_P in cselib.c. - 1 in a SUBREG generated by LRA for reload insns. */ + 1 in a SUBREG generated by LRA for reload insns. + 1 in a CALL for calls instrumented by Pointer Bounds Checker. */ unsigned int jump : 1; /* In a CODE_LABEL, part of the two-bit alternate entry field. 1 in a MEM if it cannot trap. @@ -2206,6 +2207,10 @@ do { \ #define LRA_SUBREG_P(RTX) \ (RTL_FLAG_CHECK1 ("LRA_SUBREG_P", (RTX), SUBREG)->jump) +/* True if call is instrumented by Pointer Bounds Checker. */ +#define CALL_EXPR_WITH_BOUNDS_P(RTX) \ + (RTL_FLAG_CHECK1 ("CALL_EXPR_WITH_BOUNDS_P", (RTX), CALL)->jump) + /* Access various components of an ASM_OPERANDS rtx. */ #define ASM_OPERANDS_TEMPLATE(RTX) XCSTR (RTX, 0, ASM_OPERANDS) diff --git a/gcc/stor-layout.c b/gcc/stor-layout.c index 6a3c21210b2..431b20715c3 100644 --- a/gcc/stor-layout.c +++ b/gcc/stor-layout.c @@ -409,6 +409,7 @@ int_mode_for_mode (machine_mode mode) case MODE_VECTOR_ACCUM: case MODE_VECTOR_UFRACT: case MODE_VECTOR_UACCUM: + case MODE_POINTER_BOUNDS: mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0); break; @@ -2228,6 +2229,11 @@ layout_type (tree type) SET_TYPE_MODE (type, VOIDmode); break; + case POINTER_BOUNDS_TYPE: + TYPE_SIZE (type) = bitsize_int (GET_MODE_BITSIZE (TYPE_MODE (type))); + TYPE_SIZE_UNIT (type) = size_int (GET_MODE_SIZE (TYPE_MODE (type))); + break; + case OFFSET_TYPE: TYPE_SIZE (type) = bitsize_int (POINTER_SIZE); TYPE_SIZE_UNIT (type) = size_int (POINTER_SIZE_UNITS); diff --git a/gcc/symtab.c b/gcc/symtab.c index 44bb3a6a929..29839e676f0 100644 --- a/gcc/symtab.c +++ b/gcc/symtab.c @@ -54,7 +54,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-utils.h" #include "calls.h" -static const char *ipa_ref_use_name[] = {"read","write","addr","alias"}; +static const char *ipa_ref_use_name[] = {"read","write","addr","alias","chkp"}; const char * const ld_plugin_symbol_resolution_names[]= { diff --git a/gcc/target.def b/gcc/target.def index 112f78eef56..d460516ffe6 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2066,6 +2066,107 @@ built-in function.", (tree exp, rtx target, rtx subtarget, machine_mode mode, int ignore), default_expand_builtin) +DEFHOOK +(builtin_chkp_function, + "This hook allows target to redefine built-in functions used by\n\ +Pointer Bounds Checker for code instrumentation. Hook should return\n\ +fndecl of function implementing generic builtin whose code is\n\ +passed in @var{fcode}. Currently following built-in functions are\n\ +obtained using this hook:\n\ +@deftypefn {Built-in Function} __bounds_type __chkp_bndmk (const void *@var{lb}, size_t @var{size})\n\ +Function code - BUILT_IN_CHKP_BNDMK. This built-in function is used\n\ +by Pointer Bounds Checker to create bound values. @var{lb} holds low\n\ +bound of the resulting bounds. @var{size} holds size of created bounds.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} void __chkp_bndstx (const void *@var{ptr}, __bounds_type @var{b}, const void **@var{loc})\n\ +Function code - @code{BUILT_IN_CHKP_BNDSTX}. This built-in function is used\n\ +by Pointer Bounds Checker to store bounds @var{b} for pointer @var{ptr}\n\ +when @var{ptr} is stored by address @var{loc}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} __bounds_type __chkp_bndldx (const void **@var{loc}, const void *@var{ptr})\n\ +Function code - @code{BUILT_IN_CHKP_BNDLDX}. This built-in function is used\n\ +by Pointer Bounds Checker to get bounds of pointer @var{ptr} loaded by\n\ +address @var{loc}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} void __chkp_bndcl (const void *@var{ptr}, __bounds_type @var{b})\n\ +Function code - @code{BUILT_IN_CHKP_BNDCL}. This built-in function is used\n\ +by Pointer Bounds Checker to perform check for pointer @var{ptr} against\n\ +lower bound of bounds @var{b}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} void __chkp_bndcu (const void *@var{ptr}, __bounds_type @var{b})\n\ +Function code - @code{BUILT_IN_CHKP_BNDCU}. This built-in function is used\n\ +by Pointer Bounds Checker to perform check for pointer @var{ptr} against\n\ +upper bound of bounds @var{b}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} __bounds_type __chkp_bndret (void *@var{ptr})\n\ +Function code - @code{BUILT_IN_CHKP_BNDRET}. This built-in function is used\n\ +by Pointer Bounds Checker to obtain bounds returned by a call statement.\n\ +@var{ptr} passed to built-in is @code{SSA_NAME} returned by the call.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} __bounds_type __chkp_intersect (__bounds_type @var{b1}, __bounds_type @var{b2})\n\ +Function code - @code{BUILT_IN_CHKP_INTERSECT}. This built-in function\n\ +returns intersection of bounds @var{b1} and @var{b2}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} __bounds_type __chkp_narrow (const void *@var{ptr}, __bounds_type @var{b}, size_t @var{s})\n\ +Function code - @code{BUILT_IN_CHKP_NARROW}. This built-in function\n\ +returns intersection of bounds @var{b} and\n\ +[@var{ptr}, @var{ptr} + @var{s} - @code{1}].\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} size_t __chkp_sizeof (const void *@var{ptr})\n\ +Function code - @code{BUILT_IN_CHKP_SIZEOF}. This built-in function\n\ +returns size of object referenced by @var{ptr}. @var{ptr} is always\n\ +@code{ADDR_EXPR} of @code{VAR_DECL}. This built-in is used by\n\ +Pointer Bounds Checker when bounds of object cannot be computed statically\n\ +(e.g. object has incomplete type).\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} const void *__chkp_extract_lower (__bounds_type @var{b})\n\ +Function code - @code{BUILT_IN_CHKP_EXTRACT_LOWER}. This built-in function\n\ +returns lower bound of bounds @var{b}.\n\ +@end deftypefn\n\ +\n\ +@deftypefn {Built-in Function} const void *__chkp_extract_upper (__bounds_type @var{b})\n\ +Function code - @code{BUILT_IN_CHKP_EXTRACT_UPPER}. This built-in function\n\ +returns upper bound of bounds @var{b}.\n\ +@end deftypefn", + tree, (unsigned fcode), + default_builtin_chkp_function) + +DEFHOOK +(chkp_bound_type, + "Return type to be used for bounds", + tree, (void), + default_chkp_bound_type) + +DEFHOOK +(chkp_bound_mode, + "Return mode to be used for bounds.", + enum machine_mode, (void), + default_chkp_bound_mode) + +DEFHOOK +(chkp_make_bounds_constant, + "Return constant used to statically initialize constant bounds\n\ +with specified lower bound @var{lb} and upper bounds @var{ub}.", + tree, (HOST_WIDE_INT lb, HOST_WIDE_INT ub), + default_chkp_make_bounds_constant) + +DEFHOOK +(chkp_initialize_bounds, + "Generate a list of statements @var{stmts} to initialize pointer\n\ +bounds variable @var{var} with bounds @var{lb} and @var{ub}. Return\n\ +the number of generated statements.", + int, (tree var, tree lb, tree ub, tree *stmts), + default_chkp_initialize_bounds) + /* Select a replacement for a target-specific builtin. This is done *before* regular type checking, and so allows the target to implement a crude form of function overloading. The result is a @@ -3844,6 +3945,54 @@ not generate any instructions in this case.", default_setup_incoming_varargs) DEFHOOK +(load_bounds_for_arg, + "This hook is used by expand pass to emit insn to load bounds of\n\ +@var{arg} passed in @var{slot}. Expand pass uses this hook in case\n\ +bounds of @var{arg} are not passed in register. If @var{slot} is a\n\ +memory, then bounds are loaded as for regular pointer loaded from\n\ +memory. If @var{slot} is not a memory then @var{slot_no} is an integer\n\ +constant holding number of the target dependent special slot which\n\ +should be used to obtain bounds. Hook returns RTX holding loaded bounds.", + rtx, (rtx slot, rtx arg, rtx slot_no), + default_load_bounds_for_arg) + +DEFHOOK +(store_bounds_for_arg, + "This hook is used by expand pass to emit insns to store @var{bounds} of\n\ +@var{arg} passed in @var{slot}. Expand pass uses this hook in case\n\ +@var{bounds} of @var{arg} are not passed in register. If @var{slot} is a\n\ +memory, then @var{bounds} are stored as for regular pointer stored in\n\ +memory. If @var{slot} is not a memory then @var{slot_no} is an integer\n\ +constant holding number of the target dependent special slot which\n\ +should be used to store @var{bounds}.", + void, (rtx arg, rtx slot, rtx bounds, rtx slot_no), + default_store_bounds_for_arg) + +DEFHOOK +(load_returned_bounds, + "This hook is used by expand pass to emit insn to load bounds\n\ +returned by function call in @var{slot}. Hook returns RTX holding\n\ +loaded bounds.", + rtx, (rtx slot), + default_load_returned_bounds) + +DEFHOOK +(store_returned_bounds, + "This hook is used by expand pass to emit insn to store @var{bounds}\n\ +returned by function call into @var{slot}.", + void, (rtx slot, rtx bounds), + default_store_returned_bounds) + +DEFHOOK +(setup_incoming_vararg_bounds, + "Use it to store bounds for anonymous register arguments stored\n\ +into the stack. Arguments meaning is similar to\n\ +@code{TARGET_SETUP_INCOMING_VARARGS}.", + void, (cumulative_args_t args_so_far, enum machine_mode mode, tree type, + int *pretend_args_size, int second_time), + default_setup_incoming_vararg_bounds) + +DEFHOOK (strict_argument_naming, "Define this hook to return @code{true} if the location where a function\n\ argument is passed depends on whether or not it is a named argument.\n\ @@ -3986,6 +4135,12 @@ The return value is usually either a @code{reg} RTX for the hard\n\ register in which to pass the argument, or zero to pass the argument\n\ on the stack.\n\ \n\ +The return value can be a @code{const_int} which means argument is\n\ +passed in a target specific slot with specified number. Target hooks\n\ +should be used to store or load argument in such case. See\n\ +@code{TARGET_STORE_BOUNDS_FOR_ARG} and @code{TARGET_LOAD_BOUNDS_FOR_ARG}\n\ +for more information.\n\ +\n\ The value of the expression can also be a @code{parallel} RTX@. This is\n\ used when an argument is passed in multiple locations. The mode of the\n\ @code{parallel} should be the mode of the entire argument. The\n\ @@ -4120,6 +4275,15 @@ aggregate data types, because these are returned in another way. See\n\ rtx, (const_tree ret_type, const_tree fn_decl_or_type, bool outgoing), default_function_value) +/* Return the rtx for bounds of returned pointer. */ +DEFHOOK +(chkp_function_value_bounds, + "Define this to return an RTX representing the place where a function\n\ +returns bounds for returned pointers. Arguments meaning is similar to\n\ +@code{TARGET_FUNCTION_VALUE}.", + rtx, (const_tree ret_type, const_tree fn_decl_or_type, bool outgoing), + default_chkp_function_value_bounds) + /* Return the rtx for the result of a libcall of mode MODE, calling the function FN_NAME. */ DEFHOOK diff --git a/gcc/targhooks.c b/gcc/targhooks.c index d1d3119a9f0..316001687ce 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1700,6 +1700,36 @@ default_member_type_forces_blk (const_tree, machine_mode) return false; } +rtx +default_load_bounds_for_arg (rtx addr ATTRIBUTE_UNUSED, + rtx ptr ATTRIBUTE_UNUSED, + rtx bnd ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +void +default_store_bounds_for_arg (rtx val ATTRIBUTE_UNUSED, + rtx addr ATTRIBUTE_UNUSED, + rtx bounds ATTRIBUTE_UNUSED, + rtx to ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +rtx +default_load_returned_bounds (rtx slot ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +void +default_store_returned_bounds (rtx slot ATTRIBUTE_UNUSED, + rtx bounds ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + /* Default version of canonicalize_comparison. */ void @@ -1824,6 +1854,62 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, return build_va_arg_indirect_ref (addr); } +tree +default_chkp_bound_type (void) +{ + tree res = make_node (POINTER_BOUNDS_TYPE); + TYPE_PRECISION (res) = TYPE_PRECISION (size_type_node) * 2; + TYPE_NAME (res) = get_identifier ("__bounds_type"); + SET_TYPE_MODE (res, targetm.chkp_bound_mode ()); + layout_type (res); + return res; +} + +enum machine_mode +default_chkp_bound_mode (void) +{ + return VOIDmode; +} + +tree +default_builtin_chkp_function (unsigned int fcode ATTRIBUTE_UNUSED) +{ + return NULL_TREE; +} + +rtx +default_chkp_function_value_bounds (const_tree ret_type ATTRIBUTE_UNUSED, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +tree +default_chkp_make_bounds_constant (HOST_WIDE_INT lb ATTRIBUTE_UNUSED, + HOST_WIDE_INT ub ATTRIBUTE_UNUSED) +{ + return NULL_TREE; +} + +int +default_chkp_initialize_bounds (tree var ATTRIBUTE_UNUSED, + tree lb ATTRIBUTE_UNUSED, + tree ub ATTRIBUTE_UNUSED, + tree *stmts ATTRIBUTE_UNUSED) +{ + return 0; +} + +void +default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + int *pretend_arg_size ATTRIBUTE_UNUSED, + int second_time ATTRIBUTE_UNUSED) +{ +} + /* An implementation of TARGET_CAN_USE_DOLOOP_P for targets that do not support nested low-overhead loops. */ diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 4bbf49253b9..faadd23c887 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -221,4 +221,20 @@ extern bool can_use_doloop_if_innermost (const widest_int &, const widest_int &, unsigned int, bool); +extern rtx default_load_bounds_for_arg (rtx, rtx, rtx); +extern void default_store_bounds_for_arg (rtx, rtx, rtx, rtx); +extern rtx default_load_returned_bounds (rtx); +extern void default_store_returned_bounds (rtx,rtx); +extern tree default_chkp_bound_type (void); +extern enum machine_mode default_chkp_bound_mode (void); +extern tree default_builtin_chkp_function (unsigned int); +extern rtx default_chkp_function_value_bounds (const_tree, const_tree, bool); +extern tree default_chkp_make_bounds_constant (HOST_WIDE_INT lb, HOST_WIDE_INT ub); +extern int default_chkp_initialize_bounds (tree var, tree lb, tree ub, + tree *stmts); +extern void default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + int *pretend_arg_size ATTRIBUTE_UNUSED, + int second_time ATTRIBUTE_UNUSED); #endif /* GCC_TARGHOOKS_H */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6e956b403a3..e25b7b1c9e0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,17 @@ +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. + 2014-11-05 Alex Velenko <Alex.Velenko@arm.com> * gcc.dg/asr-div1.c: New testcase. diff --git a/gcc/testsuite/gcc.dg/pr37858.c b/gcc/testsuite/gcc.dg/pr37858.c index f606d4a5d1a..577b661503a 100644 --- a/gcc/testsuite/gcc.dg/pr37858.c +++ b/gcc/testsuite/gcc.dg/pr37858.c @@ -1,7 +1,7 @@ /* PR middle-end/37858 */ /* ??? With -dv removed, this test is a bit silly. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-ipa-early_local_cleanups" } */ +/* { dg-options "-O2 -fdump-ipa-build_ssa_passes" } */ int main (void) @@ -9,4 +9,4 @@ main (void) return 0; } -/* { dg-final { cleanup-ipa-dump "early_local_cleanups" } } */ +/* { dg-final { cleanup-ipa-dump "build_ssa_passes" } } */ diff --git a/gcc/testsuite/gcc.target/i386/chkp-builtins-1.c b/gcc/testsuite/gcc.target/i386/chkp-builtins-1.c new file mode 100644 index 00000000000..bcc1198b5b1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-builtins-1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -fdump-tree-chkp" } */ +/* { dg-final { scan-tree-dump-not "bnd_init_ptr_bounds" "chkp" } } */ + +void * +chkp_test (void *p) +{ + return __builtin___bnd_init_ptr_bounds (p); +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-builtins-2.c b/gcc/testsuite/gcc.target/i386/chkp-builtins-2.c new file mode 100644 index 00000000000..1f4a244ee92 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-builtins-2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -fdump-tree-chkp" } */ +/* { dg-final { scan-tree-dump-not "bnd_copy_ptr_bounds" "chkp" } } */ + +void * +chkp_test (void *p, void *q) +{ + return __builtin___bnd_copy_ptr_bounds (p, q); +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-builtins-3.c b/gcc/testsuite/gcc.target/i386/chkp-builtins-3.c new file mode 100644 index 00000000000..ea54ede3c57 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-builtins-3.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -fdump-tree-chkp" } */ +/* { dg-final { scan-tree-dump-not "bnd_set_ptr_bounds" "chkp" } } */ + +void * +chkp_test (void *p) +{ + return __builtin___bnd_set_ptr_bounds (p, 10); +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-builtins-4.c b/gcc/testsuite/gcc.target/i386/chkp-builtins-4.c new file mode 100644 index 00000000000..cee780b2371 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-builtins-4.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -fdump-tree-chkp" } */ +/* { dg-final { scan-tree-dump-not "bnd_null_ptr_bounds" "chkp" } } */ + +void * +chkp_test (void *p) +{ + return __builtin___bnd_null_ptr_bounds (p); +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-const-check-1.c b/gcc/testsuite/gcc.target/i386/chkp-const-check-1.c new file mode 100644 index 00000000000..2cd5af1f626 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-const-check-1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -O2 -fdump-tree-chkpopt" } */ +/* { dg-final { scan-tree-dump-not "bndcl" "chkpopt" } } */ +/* { dg-final { scan-tree-dump-not "bndcu" "chkpopt" } } */ + + +int test (int *p) +{ + p = (int *)__builtin___bnd_set_ptr_bounds (p, sizeof (int)); + return *p; +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-const-check-2.c b/gcc/testsuite/gcc.target/i386/chkp-const-check-2.c new file mode 100644 index 00000000000..98497f958a1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-const-check-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -O2 -Wchkp" } */ + +int test (int *p) +{ + p = (int *)__builtin___bnd_set_ptr_bounds (p, sizeof (int)); + return *(p + 1); /* { dg-warning "memory access check always fail" "" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-lifetime-1.c b/gcc/testsuite/gcc.target/i386/chkp-lifetime-1.c new file mode 100644 index 00000000000..bcecdd032c1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-lifetime-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -O2 -fdump-tree-chkpopt-details" } */ +/* { dg-final { scan-tree-dump "Moving creation of \[^ \]+ down to its use" "chkpopt" } } */ + +extern int arr[]; + +int test (int i) +{ + int res; + if (i >= 0) + res = arr[i]; + else + res = -i; + return res; +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-1.c b/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-1.c new file mode 100644 index 00000000000..db710041f87 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-1.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-not "bndint" "optimized" } } */ + + +struct S +{ + int a; + int b; + int c; +}; + +int test (struct S *ps) +{ + int *pi = &ps->b; + return *pi; +} diff --git a/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-2.c b/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-2.c new file mode 100644 index 00000000000..c97ac277893 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/chkp-remove-bndint-2.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fcheck-pointer-bounds -mmpx -O2 -fdump-tree-optimized -Wchkp" } */ +/* { dg-final { scan-tree-dump-not "bndint" "optimized" } } */ + + +struct S +{ + int a; + int b; + int c; +}; + +int test (struct S *ps) +{ + int *pi = &ps->b; + return *(pi + 1); /* { dg-warning "memory access check always fail" "" } */ +} diff --git a/gcc/toplev.c b/gcc/toplev.c index eb37bfe6395..9c75dd8e39e 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -97,6 +97,7 @@ along with GCC; see the file COPYING3. If not see #include "gcse.h" #include "insn-codes.h" #include "optabs.h" +#include "tree-chkp.h" #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" @@ -597,6 +598,9 @@ compile_file (void) if (flag_sanitize & SANITIZE_THREAD) tsan_finish_file (); + if (flag_check_pointer_bounds) + chkp_finish_file (); + output_shared_constant_pool (); output_object_blocks (); finish_tm_clone_pairs (); @@ -1309,6 +1313,12 @@ process_options (void) "and -ftree-loop-linear)"); #endif + if (flag_check_pointer_bounds) + { + if (targetm.chkp_bound_mode () == VOIDmode) + error ("-fcheck-pointer-bounds is not supported for this target"); + } + /* One region RA really helps to decrease the code size. */ if (flag_ira_region == IRA_REGION_AUTODETECT) flag_ira_region diff --git a/gcc/tree-chkp-opt.c b/gcc/tree-chkp-opt.c new file mode 100644 index 00000000000..383c66fcaa5 --- /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); +} diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c new file mode 100644 index 00000000000..df7d425fe66 --- /dev/null +++ b/gcc/tree-chkp.c @@ -0,0 +1,4252 @@ +/* Pointer Bounds Checker insrumentation 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 "stor-layout.h" +#include "varasm.h" +#include "tree.h" +#include "target.h" +#include "tree-iterator.h" +#include "tree-cfg.h" +#include "langhooks.h" +#include "tree-pass.h" +#include "diagnostic.h" +#include "ggc.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 "print-tree.h" +#include "expr.h" +#include "tree-ssa-propagate.h" +#include "gimple-fold.h" +#include "tree-chkp.h" +#include "gimple-walk.h" +#include "rtl.h" /* For MEM_P, assign_temp. */ +#include "tree-dfa.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include "ipa-chkp.h" +#include "params.h" +#include "ipa-chkp.h" +#include "params.h" + +/* Pointer Bounds Checker instruments code with memory checks to find + out-of-bounds memory accesses. Checks are performed by computing + bounds for each pointer and then comparing address of accessed + memory before pointer dereferencing. + + 1. Function clones. + + See ipa-chkp.c. + + 2. Instrumentation. + + There are few things to instrument: + + a) Memory accesses - add checker calls to check address of accessed memory + against bounds of dereferenced pointer. Obviously safe memory + accesses like static variable access does not have to be instrumented + with checks. + + Example: + + val_2 = *p_1; + + with 4 bytes access is transformed into: + + __builtin___chkp_bndcl (__bound_tmp.1_3, p_1); + D.1_4 = p_1 + 3; + __builtin___chkp_bndcu (__bound_tmp.1_3, D.1_4); + val_2 = *p_1; + + where __bound_tmp.1_3 are bounds computed for pointer p_1, + __builtin___chkp_bndcl is a lower bound check and + __builtin___chkp_bndcu is an upper bound check. + + b) Pointer stores. + + When pointer is stored in memory we need to store its bounds. To + achieve compatibility of instrumented code with regular codes + we have to keep data layout and store bounds in special bound tables + via special checker call. Implementation of bounds table may vary for + different platforms. It has to associate pointer value and its + location (it is required because we may have two equal pointers + with different bounds stored in different places) with bounds. + Another checker builtin allows to get bounds for specified pointer + loaded from specified location. + + Example: + + buf1[i_1] = &buf2; + + is transformed into: + + buf1[i_1] = &buf2; + D.1_2 = &buf1[i_1]; + __builtin___chkp_bndstx (D.1_2, &buf2, __bound_tmp.1_2); + + where __bound_tmp.1_2 are bounds of &buf2. + + c) Static initialization. + + The special case of pointer store is static pointer initialization. + Bounds initialization is performed in a few steps: + - register all static initializations in front-end using + chkp_register_var_initializer + - when file compilation finishes we create functions with special + attribute 'chkp ctor' and put explicit initialization code + (assignments) for all statically initialized pointers. + - when checker constructor is compiled checker pass adds required + bounds initialization for all statically initialized pointers + - since we do not actually need excess pointers initialization + in checker constructor we remove such assignments from them + + d) Calls. + + For each call in the code we add additional arguments to pass + bounds for pointer arguments. We determine type of call arguments + using arguments list from function declaration; if function + declaration is not available we use function type; otherwise + (e.g. for unnamed arguments) we use type of passed value. Function + declaration/type is replaced with the instrumented one. + + Example: + + val_1 = foo (&buf1, &buf2, &buf1, 0); + + is translated into: + + val_1 = foo.chkp (&buf1, __bound_tmp.1_2, &buf2, __bound_tmp.1_3, + &buf1, __bound_tmp.1_2, 0); + + e) Returns. + + If function returns a pointer value we have to return bounds also. + A new operand was added for return statement to hold returned bounds. + + Example: + + return &_buf1; + + is transformed into + + return &_buf1, __bound_tmp.1_1; + + 3. Bounds computation. + + Compiler is fully responsible for computing bounds to be used for each + memory access. The first step for bounds computation is to find the + origin of pointer dereferenced for memory access. Basing on pointer + origin we define a way to compute its bounds. There are just few + possible cases: + + a) Pointer is returned by call. + + In this case we use corresponding checker builtin method to obtain returned + bounds. + + Example: + + buf_1 = malloc (size_2); + foo (buf_1); + + is translated into: + + buf_1 = malloc (size_2); + __bound_tmp.1_3 = __builtin___chkp_bndret (buf_1); + foo (buf_1, __bound_tmp.1_3); + + b) Pointer is an address of an object. + + In this case compiler tries to compute objects size and create corresponding + bounds. If object has incomplete type then special checker builtin is used to + obtain its size at runtime. + + Example: + + foo () + { + <unnamed type> __bound_tmp.3; + static int buf[100]; + + <bb 3>: + __bound_tmp.3_2 = __builtin___chkp_bndmk (&buf, 400); + + <bb 2>: + return &buf, __bound_tmp.3_2; + } + + Example: + + Address of an object 'extern int buf[]' with incomplete type is + returned. + + foo () + { + <unnamed type> __bound_tmp.4; + long unsigned int __size_tmp.3; + + <bb 3>: + __size_tmp.3_4 = __builtin_ia32_sizeof (buf); + __bound_tmp.4_3 = __builtin_ia32_bndmk (&buf, __size_tmp.3_4); + + <bb 2>: + return &buf, __bound_tmp.4_3; + } + + c) Pointer is the result of object narrowing. + + It happens when we use pointer to an object to compute pointer to a part + of an object. E.g. we take pointer to a field of a structure. In this + case we perform bounds intersection using bounds of original object and + bounds of object's part (which are computed basing on its type). + + There may be some debatable questions about when narrowing should occur + and when it should not. To avoid false bound violations in correct + programs we do not perform narrowing when address of an array element is + obtained (it has address of the whole array) and when address of the first + structure field is obtained (because it is guaranteed to be equal to + address of the whole structure and it is legal to cast it back to structure). + + Default narrowing behavior may be changed using compiler flags. + + Example: + + In this example address of the second structure field is returned. + + foo (struct A * p, __bounds_type __bounds_of_p) + { + <unnamed type> __bound_tmp.3; + int * _2; + int * _5; + + <bb 2>: + _5 = &p_1(D)->second_field; + __bound_tmp.3_6 = __builtin___chkp_bndmk (_5, 4); + __bound_tmp.3_8 = __builtin___chkp_intersect (__bound_tmp.3_6, + __bounds_of_p_3(D)); + _2 = &p_1(D)->second_field; + return _2, __bound_tmp.3_8; + } + + Example: + + In this example address of the first field of array element is returned. + + foo (struct A * p, __bounds_type __bounds_of_p, int i) + { + long unsigned int _3; + long unsigned int _4; + struct A * _6; + int * _7; + + <bb 2>: + _3 = (long unsigned int) i_1(D); + _4 = _3 * 8; + _6 = p_5(D) + _4; + _7 = &_6->first_field; + return _7, __bounds_of_p_2(D); + } + + + d) Pointer is the result of pointer arithmetic or type cast. + + In this case bounds of the base pointer are used. In case of binary + operation producing a pointer we are analyzing data flow further + looking for operand's bounds. One operand is considered as a base + if it has some valid bounds. If we fall into a case when none of + operands (or both of them) has valid bounds, a default bounds value + is used. + + Trying to find out bounds for binary operations we may fall into + cyclic dependencies for pointers. To avoid infinite recursion all + walked phi nodes instantly obtain corresponding bounds but created + bounds are marked as incomplete. It helps us to stop DF walk during + bounds search. + + When we reach pointer source, some args of incomplete bounds phi obtain + valid bounds and those values are propagated further through phi nodes. + If no valid bounds were found for phi node then we mark its result as + invalid bounds. Process stops when all incomplete bounds become either + valid or invalid and we are able to choose a pointer base. + + e) Pointer is loaded from the memory. + + In this case we just need to load bounds from the bounds table. + + Example: + + foo () + { + <unnamed type> __bound_tmp.3; + static int * buf; + int * _2; + + <bb 2>: + _2 = buf; + __bound_tmp.3_4 = __builtin___chkp_bndldx (&buf, _2); + return _2, __bound_tmp.3_4; + } + +*/ + +typedef void (*assign_handler)(tree, tree, void *); + +static tree chkp_get_zero_bounds (); +static tree chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter); +static tree chkp_find_bounds_loaded (tree ptr, tree ptr_src, + gimple_stmt_iterator *iter); +static void chkp_parse_array_and_component_ref (tree node, tree *ptr, + tree *elt, bool *safe, + bool *bitfield, + tree *bounds, + gimple_stmt_iterator *iter, + bool innermost_bounds); + +#define chkp_bndldx_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDLDX)) +#define chkp_bndstx_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDSTX)) +#define chkp_checkl_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL)) +#define chkp_checku_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU)) +#define chkp_bndmk_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK)) +#define chkp_ret_bnd_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET)) +#define chkp_intersect_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT)) +#define chkp_narrow_bounds_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_NARROW)) +#define chkp_sizeof_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_SIZEOF)) +#define chkp_extract_lower_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_LOWER)) +#define chkp_extract_upper_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER)) + +static GTY (()) tree chkp_uintptr_type; + +static GTY (()) tree chkp_zero_bounds_var; +static GTY (()) tree chkp_none_bounds_var; + +static GTY (()) basic_block entry_block; +static GTY (()) tree zero_bounds; +static GTY (()) tree none_bounds; +static GTY (()) tree incomplete_bounds; +static GTY (()) tree tmp_var; +static GTY (()) tree size_tmp_var; +static GTY (()) bitmap chkp_abnormal_copies; + +struct hash_set<tree> *chkp_invalid_bounds; +struct hash_set<tree> *chkp_completed_bounds_set; +struct hash_map<tree, tree> *chkp_reg_bounds; +struct hash_map<tree, tree> *chkp_bound_vars; +struct hash_map<tree, tree> *chkp_reg_addr_bounds; +struct hash_map<tree, tree> *chkp_incomplete_bounds_map; +struct hash_map<tree, tree> *chkp_bounds_map; +struct hash_map<tree, tree> *chkp_static_var_bounds; + +static bool in_chkp_pass; + +#define CHKP_BOUND_TMP_NAME "__bound_tmp" +#define CHKP_SIZE_TMP_NAME "__size_tmp" +#define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_" +#define CHKP_STRING_BOUNDS_PREFIX "__chkp_string_bounds_" +#define CHKP_VAR_BOUNDS_PREFIX "__chkp_var_bounds_" +#define CHKP_ZERO_BOUNDS_VAR_NAME "__chkp_zero_bounds" +#define CHKP_NONE_BOUNDS_VAR_NAME "__chkp_none_bounds" + +/* Static checker constructors may become very large and their + compilation with optimization may take too much time. + Therefore we put a limit to number of statements in one + constructor. Tests with 100 000 statically initialized + pointers showed following compilation times on Sandy Bridge + server (used -O2): + limit 100 => ~18 sec. + limit 300 => ~22 sec. + limit 1000 => ~30 sec. + limit 3000 => ~49 sec. + limit 5000 => ~55 sec. + limit 10000 => ~76 sec. + limit 100000 => ~532 sec. */ +#define MAX_STMTS_IN_STATIC_CHKP_CTOR (PARAM_VALUE (PARAM_CHKP_MAX_CTOR_SIZE)) + +struct chkp_ctor_stmt_list +{ + tree stmts; + int avail; +}; + +/* Return 1 if function FNDECL is instrumented by Pointer + Bounds Checker. */ +bool +chkp_function_instrumented_p (tree fndecl) +{ + return fndecl + && lookup_attribute ("chkp instrumented", DECL_ATTRIBUTES (fndecl)); +} + +/* Mark function FNDECL as instrumented. */ +void +chkp_function_mark_instrumented (tree fndecl) +{ + if (chkp_function_instrumented_p (fndecl)) + return; + + DECL_ATTRIBUTES (fndecl) + = tree_cons (get_identifier ("chkp instrumented"), NULL, + DECL_ATTRIBUTES (fndecl)); +} + +/* Return true when STMT is builtin call to instrumentation function + corresponding to CODE. */ + +bool +chkp_gimple_call_builtin_p (gimple call, + enum built_in_function code) +{ + tree fndecl; + if (is_gimple_call (call) + && (fndecl = targetm.builtin_chkp_function (code)) + && gimple_call_fndecl (call) == fndecl) + return true; + return false; +} + +/* Emit code to store zero bounds for PTR located at MEM. */ +void +chkp_expand_bounds_reset_for_mem (tree mem, tree ptr) +{ + tree zero_bnd, bnd, addr, bndstx; + + if (flag_chkp_use_static_const_bounds) + zero_bnd = chkp_get_zero_bounds_var (); + else + zero_bnd = chkp_build_make_bounds_call (integer_zero_node, + integer_zero_node); + bnd = make_tree (pointer_bounds_type_node, + assign_temp (pointer_bounds_type_node, 0, 1)); + addr = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (mem)), mem); + bndstx = chkp_build_bndstx_call (addr, ptr, bnd); + + expand_assignment (bnd, zero_bnd, false); + expand_normal (bndstx); +} + +/* Mark statement S to not be instrumented. */ +static void +chkp_mark_stmt (gimple s) +{ + gimple_set_plf (s, GF_PLF_1, true); +} + +/* Mark statement S to be instrumented. */ +static void +chkp_unmark_stmt (gimple s) +{ + gimple_set_plf (s, GF_PLF_1, false); +} + +/* Return 1 if statement S should not be instrumented. */ +static bool +chkp_marked_stmt_p (gimple s) +{ + return gimple_plf (s, GF_PLF_1); +} + +/* Get var to be used for bound temps. */ +static tree +chkp_get_tmp_var (void) +{ + if (!tmp_var) + tmp_var = create_tmp_reg (pointer_bounds_type_node, CHKP_BOUND_TMP_NAME); + + return tmp_var; +} + +/* Get SSA_NAME to be used as temp. */ +static tree +chkp_get_tmp_reg (gimple stmt) +{ + if (in_chkp_pass) + return make_ssa_name (chkp_get_tmp_var (), stmt); + + return make_temp_ssa_name (pointer_bounds_type_node, stmt, + CHKP_BOUND_TMP_NAME); +} + +/* Get var to be used for size temps. */ +static tree +chkp_get_size_tmp_var (void) +{ + if (!size_tmp_var) + size_tmp_var = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME); + + return size_tmp_var; +} + +/* Register bounds BND for address of OBJ. */ +static void +chkp_register_addr_bounds (tree obj, tree bnd) +{ + if (bnd == incomplete_bounds) + return; + + chkp_reg_addr_bounds->put (obj, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered bound "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for address of "); + print_generic_expr (dump_file, obj, 0); + fprintf (dump_file, "\n"); + } +} + +/* Return bounds registered for address of OBJ. */ +static tree +chkp_get_registered_addr_bounds (tree obj) +{ + tree *slot = chkp_reg_addr_bounds->get (obj); + return slot ? *slot : NULL_TREE; +} + +/* Mark BOUNDS as completed. */ +static void +chkp_mark_completed_bounds (tree bounds) +{ + chkp_completed_bounds_set->add (bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Marked bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " as completed\n"); + } +} + +/* Return 1 if BOUNDS were marked as completed and 0 otherwise. */ +static bool +chkp_completed_bounds (tree bounds) +{ + return chkp_completed_bounds_set->contains (bounds); +} + +/* Clear comleted bound marks. */ +static void +chkp_erase_completed_bounds (void) +{ + delete chkp_completed_bounds_set; + chkp_completed_bounds_set = new hash_set<tree>; +} + +/* Mark BOUNDS associated with PTR as incomplete. */ +static void +chkp_register_incomplete_bounds (tree bounds, tree ptr) +{ + chkp_incomplete_bounds_map->put (bounds, ptr); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered incomplete bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " for "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } +} + +/* Return 1 if BOUNDS are incomplete and 0 otherwise. */ +static bool +chkp_incomplete_bounds (tree bounds) +{ + if (bounds == incomplete_bounds) + return true; + + if (chkp_completed_bounds (bounds)) + return false; + + return chkp_incomplete_bounds_map->get (bounds) != NULL; +} + +/* Clear incomleted bound marks. */ +static void +chkp_erase_incomplete_bounds (void) +{ + delete chkp_incomplete_bounds_map; + chkp_incomplete_bounds_map = new hash_map<tree, tree>; +} + +/* Build and return bndmk call which creates bounds for structure + pointed by PTR. Structure should have complete type. */ +tree +chkp_make_bounds_for_struct_addr (tree ptr) +{ + tree type = TREE_TYPE (ptr); + tree size; + + gcc_assert (POINTER_TYPE_P (type)); + + size = TYPE_SIZE (TREE_TYPE (type)); + + gcc_assert (size); + + return build_call_nary (pointer_bounds_type_node, + build_fold_addr_expr (chkp_bndmk_fndecl), + 2, ptr, size); +} + +/* Traversal function for chkp_may_finish_incomplete_bounds. + Set RES to 0 if at least one argument of phi statement + defining bounds (passed in KEY arg) is unknown. + Traversal stops when first unknown phi argument is found. */ +bool +chkp_may_complete_phi_bounds (tree const &bounds, tree *slot ATTRIBUTE_UNUSED, + bool *res) +{ + gimple phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + + phi = SSA_NAME_DEF_STMT (bounds); + + gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI); + + for (i = 0; i < gimple_phi_num_args (phi); i++) + { + tree phi_arg = gimple_phi_arg_def (phi, i); + if (!phi_arg) + { + *res = false; + /* Do not need to traverse further. */ + return false; + } + } + + return true; +} + +/* Return 1 if all phi nodes created for bounds have their + arguments computed. */ +static bool +chkp_may_finish_incomplete_bounds (void) +{ + bool res = true; + + chkp_incomplete_bounds_map + ->traverse<bool *, chkp_may_complete_phi_bounds> (&res); + + return res; +} + +/* Helper function for chkp_finish_incomplete_bounds. + Recompute args for bounds phi node. */ +bool +chkp_recompute_phi_bounds (tree const &bounds, tree *slot, + void *res ATTRIBUTE_UNUSED) +{ + tree ptr = *slot; + gimple bounds_phi; + gimple ptr_phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + gcc_assert (TREE_CODE (ptr) == SSA_NAME); + + bounds_phi = SSA_NAME_DEF_STMT (bounds); + ptr_phi = SSA_NAME_DEF_STMT (ptr); + + gcc_assert (bounds_phi && gimple_code (bounds_phi) == GIMPLE_PHI); + gcc_assert (ptr_phi && gimple_code (ptr_phi) == GIMPLE_PHI); + + for (i = 0; i < gimple_phi_num_args (bounds_phi); i++) + { + tree ptr_arg = gimple_phi_arg_def (ptr_phi, i); + tree bound_arg = chkp_find_bounds (ptr_arg, NULL); + + add_phi_arg (bounds_phi, bound_arg, + gimple_phi_arg_edge (ptr_phi, i), + UNKNOWN_LOCATION); + } + + return true; +} + +/* Mark BOUNDS as invalid. */ +static void +chkp_mark_invalid_bounds (tree bounds) +{ + chkp_invalid_bounds->add (bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Marked bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " as invalid\n"); + } +} + +/* Return 1 if BOUNDS were marked as invalid and 0 otherwise. */ +static bool +chkp_valid_bounds (tree bounds) +{ + if (bounds == zero_bounds || bounds == none_bounds) + return false; + + return !chkp_invalid_bounds->contains (bounds); +} + +/* Helper function for chkp_finish_incomplete_bounds. + Check all arguments of phi nodes trying to find + valid completed bounds. If there is at least one + such arg then bounds produced by phi node are marked + as valid completed bounds and all phi args are + recomputed. */ +bool +chkp_find_valid_phi_bounds (tree const &bounds, tree *slot, bool *res) +{ + gimple phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + + if (chkp_completed_bounds (bounds)) + return true; + + phi = SSA_NAME_DEF_STMT (bounds); + + gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI); + + for (i = 0; i < gimple_phi_num_args (phi); i++) + { + tree phi_arg = gimple_phi_arg_def (phi, i); + + gcc_assert (phi_arg); + + if (chkp_valid_bounds (phi_arg) && !chkp_incomplete_bounds (phi_arg)) + { + *res = true; + chkp_mark_completed_bounds (bounds); + chkp_recompute_phi_bounds (bounds, slot, NULL); + return true; + } + } + + return true; +} + +/* Helper function for chkp_finish_incomplete_bounds. + Marks all incompleted bounds as invalid. */ +bool +chkp_mark_invalid_bounds_walker (tree const &bounds, + tree *slot ATTRIBUTE_UNUSED, + void *res ATTRIBUTE_UNUSED) +{ + if (!chkp_completed_bounds (bounds)) + { + chkp_mark_invalid_bounds (bounds); + chkp_mark_completed_bounds (bounds); + } + return true; +} + +/* When all bound phi nodes have all their args computed + we have enough info to find valid bounds. We iterate + through all incompleted bounds searching for valid + bounds. Found valid bounds are marked as completed + and all remaining incompleted bounds are recomputed. + Process continues until no new valid bounds may be + found. All remained incompleted bounds are marked as + invalid (i.e. have no valid source of bounds). */ +static void +chkp_finish_incomplete_bounds (void) +{ + bool found_valid; + + while (found_valid) + { + found_valid = false; + + chkp_incomplete_bounds_map-> + traverse<bool *, chkp_find_valid_phi_bounds> (&found_valid); + + if (found_valid) + chkp_incomplete_bounds_map-> + traverse<void *, chkp_recompute_phi_bounds> (NULL); + } + + chkp_incomplete_bounds_map-> + traverse<void *, chkp_mark_invalid_bounds_walker> (NULL); + chkp_incomplete_bounds_map-> + traverse<void *, chkp_recompute_phi_bounds> (NULL); + + chkp_erase_completed_bounds (); + chkp_erase_incomplete_bounds (); +} + +/* Return 1 if type TYPE is a pointer type or a + structure having a pointer type as one of its fields. + Otherwise return 0. */ +bool +chkp_type_has_pointer (const_tree type) +{ + bool res = false; + + if (BOUNDED_TYPE_P (type)) + res = true; + else if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + res = res || chkp_type_has_pointer (TREE_TYPE (field)); + } + else if (TREE_CODE (type) == ARRAY_TYPE) + res = chkp_type_has_pointer (TREE_TYPE (type)); + + return res; +} + +unsigned +chkp_type_bounds_count (const_tree type) +{ + unsigned res = 0; + + if (!type) + res = 0; + else if (BOUNDED_TYPE_P (type)) + res = 1; + else if (RECORD_OR_UNION_TYPE_P (type)) + { + bitmap have_bound; + + bitmap_obstack_initialize (NULL); + have_bound = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (type, have_bound); + res = bitmap_count_bits (have_bound); + BITMAP_FREE (have_bound); + bitmap_obstack_release (NULL); + } + + return res; +} + +/* Get bounds associated with NODE via + chkp_set_bounds call. */ +tree +chkp_get_bounds (tree node) +{ + tree *slot; + + if (!chkp_bounds_map) + return NULL_TREE; + + slot = chkp_bounds_map->get (node); + return slot ? *slot : NULL_TREE; +} + +/* Associate bounds VAL with NODE. */ +void +chkp_set_bounds (tree node, tree val) +{ + if (!chkp_bounds_map) + chkp_bounds_map = new hash_map<tree, tree>; + + chkp_bounds_map->put (node, val); +} + +/* Check if statically initialized variable VAR require + static bounds initialization. If VAR is added into + bounds initlization list then 1 is returned. Otherwise + return 0. */ +extern bool +chkp_register_var_initializer (tree var) +{ + if (!flag_check_pointer_bounds + || DECL_INITIAL (var) == error_mark_node) + return false; + + gcc_assert (TREE_CODE (var) == VAR_DECL); + gcc_assert (DECL_INITIAL (var)); + + if (TREE_STATIC (var) + && chkp_type_has_pointer (TREE_TYPE (var))) + { + varpool_node::get_create (var)->need_bounds_init = 1; + return true; + } + + return false; +} + +/* Helper function for chkp_finish_file. + + Add new modification statement (RHS is assigned to LHS) + into list of static initializer statementes (passed in ARG). + If statements list becomes too big, emit checker constructor + and start the new one. */ +static void +chkp_add_modification_to_stmt_list (tree lhs, + tree rhs, + void *arg) +{ + struct chkp_ctor_stmt_list *stmts = (struct chkp_ctor_stmt_list *)arg; + tree modify; + + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) + rhs = build1 (CONVERT_EXPR, TREE_TYPE (lhs), rhs); + + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); + append_to_statement_list (modify, &stmts->stmts); + + stmts->avail--; +} + +/* Build and return ADDR_EXPR for specified object OBJ. */ +static tree +chkp_build_addr_expr (tree obj) +{ + return TREE_CODE (obj) == TARGET_MEM_REF + ? tree_mem_ref_addr (ptr_type_node, obj) + : build_fold_addr_expr (obj); +} + +/* Helper function for chkp_finish_file. + Initialize bound variable BND_VAR with bounds of variable + VAR to statements list STMTS. If statements list becomes + too big, emit checker constructor and start the new one. */ +static void +chkp_output_static_bounds (tree bnd_var, tree var, + struct chkp_ctor_stmt_list *stmts) +{ + tree lb, ub, size; + + if (TREE_CODE (var) == STRING_CST) + { + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + size = build_int_cst (size_type_node, TREE_STRING_LENGTH (var) - 1); + } + else if (DECL_SIZE (var) + && !chkp_variable_size_type (TREE_TYPE (var))) + { + /* Compute bounds using statically known size. */ + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + size = size_binop (MINUS_EXPR, DECL_SIZE_UNIT (var), size_one_node); + } + else + { + /* Compute bounds using dynamic size. */ + tree call; + + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + call = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_sizeof_fndecl)), + chkp_sizeof_fndecl); + size = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_sizeof_fndecl)), + call, 1, var); + + if (flag_chkp_zero_dynamic_size_as_infinite) + { + tree max_size, cond; + + max_size = build2 (MINUS_EXPR, size_type_node, size_zero_node, lb); + cond = build2 (NE_EXPR, boolean_type_node, size, size_zero_node); + size = build3 (COND_EXPR, size_type_node, cond, size, max_size); + } + + size = size_binop (MINUS_EXPR, size, size_one_node); + } + + ub = size_binop (PLUS_EXPR, lb, size); + stmts->avail -= targetm.chkp_initialize_bounds (bnd_var, lb, ub, + &stmts->stmts); + if (stmts->avail <= 0) + { + cgraph_build_static_cdtor ('B', stmts->stmts, + MAX_RESERVED_INIT_PRIORITY + 2); + stmts->avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts->stmts = NULL; + } +} + +/* Return entry block to be used for checker initilization code. + Create new block if required. */ +static basic_block +chkp_get_entry_block (void) +{ + if (!entry_block) + entry_block = split_block (ENTRY_BLOCK_PTR_FOR_FN (cfun), NULL)->dest; + + return entry_block; +} + +/* Return a bounds var to be used for pointer var PTR_VAR. */ +static tree +chkp_get_bounds_var (tree ptr_var) +{ + tree bnd_var; + tree *slot; + + slot = chkp_bound_vars->get (ptr_var); + if (slot) + bnd_var = *slot; + else + { + bnd_var = create_tmp_reg (pointer_bounds_type_node, + CHKP_BOUND_TMP_NAME); + chkp_bound_vars->put (ptr_var, bnd_var); + } + + return bnd_var; +} + + + +/* Register bounds BND for object PTR in global bounds table. + A copy of bounds may be created for abnormal ssa names. + Returns bounds to use for PTR. */ +static tree +chkp_maybe_copy_and_register_bounds (tree ptr, tree bnd) +{ + bool abnormal_ptr; + + if (!chkp_reg_bounds) + return bnd; + + /* Do nothing if bounds are incomplete_bounds + because it means bounds will be recomputed. */ + if (bnd == incomplete_bounds) + return bnd; + + abnormal_ptr = (TREE_CODE (ptr) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr) + && gimple_code (SSA_NAME_DEF_STMT (ptr)) != GIMPLE_PHI); + + /* A single bounds value may be reused multiple times for + different pointer values. It may cause coalescing issues + for abnormal SSA names. To avoid it we create a bounds + copy in case it is computed for abnormal SSA name. + + We also cannot reuse such created copies for other pointers */ + if (abnormal_ptr + || bitmap_bit_p (chkp_abnormal_copies, SSA_NAME_VERSION (bnd))) + { + tree bnd_var = NULL_TREE; + + if (abnormal_ptr) + { + if (SSA_NAME_VAR (ptr)) + bnd_var = chkp_get_bounds_var (SSA_NAME_VAR (ptr)); + } + else + bnd_var = chkp_get_tmp_var (); + + /* For abnormal copies we may just find original + bounds and use them. */ + if (!abnormal_ptr && !SSA_NAME_IS_DEFAULT_DEF (bnd)) + { + gimple bnd_def = SSA_NAME_DEF_STMT (bnd); + gcc_checking_assert (gimple_code (bnd_def) == GIMPLE_ASSIGN); + bnd = gimple_assign_rhs1 (bnd_def); + } + /* For undefined values we usually use none bounds + value but in case of abnormal edge it may cause + coalescing failures. Use default definition of + bounds variable instead to avoid it. */ + else if (SSA_NAME_IS_DEFAULT_DEF (ptr) + && TREE_CODE (SSA_NAME_VAR (ptr)) != PARM_DECL) + { + bnd = get_or_create_ssa_default_def (cfun, bnd_var); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Using default def bounds "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for abnormal default def SSA name "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + } + else + { + tree copy; + gimple def = SSA_NAME_DEF_STMT (ptr); + gimple assign; + gimple_stmt_iterator gsi; + + if (bnd_var) + copy = make_ssa_name (bnd_var, gimple_build_nop ()); + else + copy = make_temp_ssa_name (pointer_bounds_type_node, + gimple_build_nop (), + CHKP_BOUND_TMP_NAME); + assign = gimple_build_assign (copy, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Creating a copy of bounds "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for abnormal SSA name "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + + if (gimple_code (def) == GIMPLE_NOP) + { + gsi = gsi_last_bb (chkp_get_entry_block ()); + if (!gsi_end_p (gsi) && is_ctrl_stmt (gsi_stmt (gsi))) + gsi_insert_before (&gsi, assign, GSI_CONTINUE_LINKING); + else + gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING); + } + else + { + gimple bnd_def = SSA_NAME_DEF_STMT (bnd); + /* Sometimes (e.g. when we load a pointer from a + memory) bounds are produced later than a pointer. + We need to insert bounds copy appropriately. */ + if (gimple_code (bnd_def) != GIMPLE_NOP + && stmt_dominates_stmt_p (def, bnd_def)) + gsi = gsi_for_stmt (bnd_def); + else + gsi = gsi_for_stmt (def); + gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING); + } + + bnd = copy; + } + + if (abnormal_ptr) + bitmap_set_bit (chkp_abnormal_copies, SSA_NAME_VERSION (bnd)); + } + + chkp_reg_bounds->put (ptr, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered bound "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for pointer "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + + return bnd; +} + +/* Get bounds registered for object PTR in global bounds table. */ +static tree +chkp_get_registered_bounds (tree ptr) +{ + tree *slot; + + if (!chkp_reg_bounds) + return NULL_TREE; + + slot = chkp_reg_bounds->get (ptr); + return slot ? *slot : NULL_TREE; +} + +/* Add bound retvals to return statement pointed by GSI. */ + +static void +chkp_add_bounds_to_ret_stmt (gimple_stmt_iterator *gsi) +{ + gimple ret = gsi_stmt (*gsi); + tree retval = gimple_return_retval (ret); + tree ret_decl = DECL_RESULT (cfun->decl); + tree bounds; + + if (!retval) + return; + + if (BOUNDED_P (ret_decl)) + { + bounds = chkp_find_bounds (retval, gsi); + bounds = chkp_maybe_copy_and_register_bounds (ret_decl, bounds); + gimple_return_set_retbnd (ret, bounds); + } + + update_stmt (ret); +} + +/* Force OP to be suitable for using as an argument for call. + New statements (if any) go to SEQ. */ +static tree +chkp_force_gimple_call_op (tree op, gimple_seq *seq) +{ + gimple_seq stmts; + gimple_stmt_iterator si; + + op = force_gimple_operand (unshare_expr (op), &stmts, true, NULL_TREE); + + for (si = gsi_start (stmts); !gsi_end_p (si); gsi_next (&si)) + chkp_mark_stmt (gsi_stmt (si)); + + gimple_seq_add_seq (seq, stmts); + + return op; +} + +/* Generate lower bound check for memory access by ADDR. + Check is inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +static void +chkp_check_lower (tree addr, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + gimple_seq seq; + gimple check; + tree node; + + if (bounds == chkp_get_zero_bounds ()) + return; + + if (dirflag == integer_zero_node + && !flag_chkp_check_read) + return; + + if (dirflag == integer_one_node + && !flag_chkp_check_write) + return; + + seq = NULL; + + node = chkp_force_gimple_call_op (addr, &seq); + + check = gimple_build_call (chkp_checkl_fndecl, 2, node, bounds); + chkp_mark_stmt (check); + gimple_call_set_with_bounds (check, true); + gimple_set_location (check, location); + gimple_seq_add_stmt (&seq, check); + + gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + gimple before = gsi_stmt (iter); + fprintf (dump_file, "Generated lower bound check for statement "); + print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " "); + print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Generate upper bound check for memory access by ADDR. + Check is inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +static void +chkp_check_upper (tree addr, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + gimple_seq seq; + gimple check; + tree node; + + if (bounds == chkp_get_zero_bounds ()) + return; + + if (dirflag == integer_zero_node + && !flag_chkp_check_read) + return; + + if (dirflag == integer_one_node + && !flag_chkp_check_write) + return; + + seq = NULL; + + node = chkp_force_gimple_call_op (addr, &seq); + + check = gimple_build_call (chkp_checku_fndecl, 2, node, bounds); + chkp_mark_stmt (check); + gimple_call_set_with_bounds (check, true); + gimple_set_location (check, location); + gimple_seq_add_stmt (&seq, check); + + gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + gimple before = gsi_stmt (iter); + fprintf (dump_file, "Generated upper bound check for statement "); + print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " "); + print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Generate lower and upper bound checks for memory access + to memory slot [FIRST, LAST] againsr BOUNDS. Checks + are inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +void +chkp_check_mem_access (tree first, tree last, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + chkp_check_lower (first, bounds, iter, location, dirflag); + chkp_check_upper (last, bounds, iter, location, dirflag); +} + +/* Replace call to _bnd_chk_* pointed by GSI with + bndcu and bndcl calls. DIRFLAG determines whether + check is for read or write. */ + +void +chkp_replace_address_check_builtin (gimple_stmt_iterator *gsi, + tree dirflag) +{ + gimple_stmt_iterator call_iter = *gsi; + gimple call = gsi_stmt (*gsi); + tree fndecl = gimple_call_fndecl (call); + tree addr = gimple_call_arg (call, 0); + tree bounds = chkp_find_bounds (addr, gsi); + + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS) + chkp_check_lower (addr, bounds, *gsi, gimple_location (call), dirflag); + + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS) + chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag); + + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS) + { + tree size = gimple_call_arg (call, 1); + addr = fold_build_pointer_plus (addr, size); + addr = fold_build_pointer_plus_hwi (addr, -1); + chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag); + } + + gsi_remove (&call_iter, true); +} + +/* Replace call to _bnd_get_ptr_* pointed by GSI with + corresponding bounds extract call. */ + +void +chkp_replace_extract_builtin (gimple_stmt_iterator *gsi) +{ + gimple call = gsi_stmt (*gsi); + tree fndecl = gimple_call_fndecl (call); + tree addr = gimple_call_arg (call, 0); + tree bounds = chkp_find_bounds (addr, gsi); + gimple extract; + + if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND) + fndecl = chkp_extract_lower_fndecl; + else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND) + fndecl = chkp_extract_upper_fndecl; + else + gcc_unreachable (); + + extract = gimple_build_call (fndecl, 1, bounds); + gimple_call_set_lhs (extract, gimple_call_lhs (call)); + chkp_mark_stmt (extract); + + gsi_replace (gsi, extract, false); +} + +/* Return COMPONENT_REF accessing FIELD in OBJ. */ +static tree +chkp_build_component_ref (tree obj, tree field) +{ + tree res; + + /* If object is TMR then we do not use component_ref but + add offset instead. We need it to be able to get addr + of the reasult later. */ + if (TREE_CODE (obj) == TARGET_MEM_REF) + { + tree offs = TMR_OFFSET (obj); + offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs), + offs, DECL_FIELD_OFFSET (field)); + + gcc_assert (offs); + + res = copy_node (obj); + TREE_TYPE (res) = TREE_TYPE (field); + TMR_OFFSET (res) = offs; + } + else + res = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL_TREE); + + return res; +} + +/* Return ARRAY_REF for array ARR and index IDX with + specified element type ETYPE and element size ESIZE. */ +static tree +chkp_build_array_ref (tree arr, tree etype, tree esize, + unsigned HOST_WIDE_INT idx) +{ + tree index = build_int_cst (size_type_node, idx); + tree res; + + /* If object is TMR then we do not use array_ref but + add offset instead. We need it to be able to get addr + of the reasult later. */ + if (TREE_CODE (arr) == TARGET_MEM_REF) + { + tree offs = TMR_OFFSET (arr); + + esize = fold_binary_to_constant (MULT_EXPR, TREE_TYPE (esize), + esize, index); + gcc_assert(esize); + + offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs), + offs, esize); + gcc_assert (offs); + + res = copy_node (arr); + TREE_TYPE (res) = etype; + TMR_OFFSET (res) = offs; + } + else + res = build4 (ARRAY_REF, etype, arr, index, NULL_TREE, NULL_TREE); + + return res; +} + +/* Helper function for chkp_add_bounds_to_call_stmt. + Fill ALL_BOUNDS output array with created bounds. + + OFFS is used for recursive calls and holds basic + offset of TYPE in outer structure in bits. + + ITER points a position where bounds are searched. + + ALL_BOUNDS[i] is filled with elem bounds if there + is a field in TYPE which has pointer type and offset + equal to i * POINTER_SIZE in bits. */ +static void +chkp_find_bounds_for_elem (tree elem, tree *all_bounds, + HOST_WIDE_INT offs, + gimple_stmt_iterator *iter) +{ + tree type = TREE_TYPE (elem); + + if (BOUNDED_TYPE_P (type)) + { + if (!all_bounds[offs / POINTER_SIZE]) + { + tree temp = make_temp_ssa_name (type, gimple_build_nop (), ""); + gimple assign = gimple_build_assign (temp, elem); + gimple_stmt_iterator gsi; + + gsi_insert_before (iter, assign, GSI_SAME_STMT); + gsi = gsi_for_stmt (assign); + + all_bounds[offs / POINTER_SIZE] = chkp_find_bounds (temp, &gsi); + } + } + else if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + { + tree base = unshare_expr (elem); + tree field_ref = chkp_build_component_ref (base, field); + HOST_WIDE_INT field_offs + = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field)); + if (DECL_FIELD_OFFSET (field)) + field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8; + + chkp_find_bounds_for_elem (field_ref, all_bounds, + offs + field_offs, iter); + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree etype = TREE_TYPE (type); + HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype)); + unsigned HOST_WIDE_INT cur; + + if (!maxval || integer_minus_onep (maxval)) + return; + + for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++) + { + tree base = unshare_expr (elem); + tree arr_elem = chkp_build_array_ref (base, etype, + TYPE_SIZE (etype), + cur); + chkp_find_bounds_for_elem (arr_elem, all_bounds, offs + cur * esize, + iter); + } + } +} + +/* Fill HAVE_BOUND output bitmap with information about + bounds requred for object of type TYPE. + + OFFS is used for recursive calls and holds basic + offset of TYPE in outer structure in bits. + + HAVE_BOUND[i] is set to 1 if there is a field + in TYPE which has pointer type and offset + equal to i * POINTER_SIZE - OFFS in bits. */ +void +chkp_find_bound_slots_1 (const_tree type, bitmap have_bound, + HOST_WIDE_INT offs) +{ + if (BOUNDED_TYPE_P (type)) + bitmap_set_bit (have_bound, offs / POINTER_SIZE); + else if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + { + HOST_WIDE_INT field_offs + = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field)); + if (DECL_FIELD_OFFSET (field)) + field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8; + chkp_find_bound_slots_1 (TREE_TYPE (field), have_bound, + offs + field_offs); + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree etype = TREE_TYPE (type); + HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype)); + unsigned HOST_WIDE_INT cur; + + if (!maxval || integer_minus_onep (maxval)) + return; + + for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++) + chkp_find_bound_slots_1 (etype, have_bound, offs + cur * esize); + } +} + +/* Fill bitmap RES with information about bounds for + type TYPE. See chkp_find_bound_slots_1 for more + details. */ +void +chkp_find_bound_slots (const_tree type, bitmap res) +{ + bitmap_clear (res); + chkp_find_bound_slots_1 (type, res, 0); +} + +/* Add bound arguments to call statement pointed by GSI. + Also performs a replacement of user checker builtins calls + with internal ones. */ + +static void +chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi) +{ + gimple call = gsi_stmt (*gsi); + unsigned arg_no = 0; + tree fndecl = gimple_call_fndecl (call); + tree fntype; + tree first_formal_arg; + tree arg; + bool use_fntype = false; + tree op; + ssa_op_iter iter; + gimple new_call; + + /* Do nothing for internal functions. */ + if (gimple_call_internal_p (call)) + return; + + fntype = TREE_TYPE (TREE_TYPE (gimple_call_fn (call))); + + /* Do nothing if back-end builtin is called. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return; + + /* Do nothing for some middle-end builtins. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE) + return; + + /* Donothing for calls to legacy functions. */ + if (fndecl + && lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl))) + return; + + /* Ignore CHKP_INIT_PTR_BOUNDS, CHKP_NULL_PTR_BOUNDS + and CHKP_COPY_PTR_BOUNDS. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS)) + return; + + /* Check user builtins are replaced with checks. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)) + { + chkp_replace_address_check_builtin (gsi, integer_minus_one_node); + return; + } + + /* Check user builtins are replaced with bound extract. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND)) + { + chkp_replace_extract_builtin (gsi); + return; + } + + /* BUILT_IN_CHKP_NARROW_PTR_BOUNDS call is replaced with + target narrow bounds call. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NARROW_PTR_BOUNDS) + { + tree arg = gimple_call_arg (call, 1); + tree bounds = chkp_find_bounds (arg, gsi); + + gimple_call_set_fndecl (call, chkp_narrow_bounds_fndecl); + gimple_call_set_arg (call, 1, bounds); + update_stmt (call); + + return; + } + + /* BUILT_IN_CHKP_STORE_PTR_BOUNDS call is replaced with + bndstx call. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_STORE_PTR_BOUNDS) + { + tree addr = gimple_call_arg (call, 0); + tree ptr = gimple_call_arg (call, 1); + tree bounds = chkp_find_bounds (ptr, gsi); + gimple_stmt_iterator iter = gsi_for_stmt (call); + + chkp_build_bndstx (addr, ptr, bounds, gsi); + gsi_remove (&iter, true); + + return; + } + + if (!flag_chkp_instrument_calls) + return; + + /* Avoid instrumented builtin functions for now. Due to IPA + it also means we have to avoid instrumentation of indirect + calls. */ + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN) + return; + + /* If function decl is available then use it for + formal arguments list. Otherwise use function type. */ + if (fndecl && DECL_ARGUMENTS (fndecl)) + first_formal_arg = DECL_ARGUMENTS (fndecl); + else + { + first_formal_arg = TYPE_ARG_TYPES (fntype); + use_fntype = true; + } + + /* Fill vector of new call args. */ + vec<tree> new_args = vNULL; + new_args.create (gimple_call_num_args (call)); + arg = first_formal_arg; + for (arg_no = 0; arg_no < gimple_call_num_args (call); arg_no++) + { + tree call_arg = gimple_call_arg (call, arg_no); + tree type; + + /* Get arg type using formal argument description + or actual argument type. */ + if (arg) + if (use_fntype) + if (TREE_VALUE (arg) != void_type_node) + { + type = TREE_VALUE (arg); + arg = TREE_CHAIN (arg); + } + else + type = TREE_TYPE (call_arg); + else + { + type = TREE_TYPE (arg); + arg = TREE_CHAIN (arg); + } + else + type = TREE_TYPE (call_arg); + + new_args.safe_push (call_arg); + + if (BOUNDED_TYPE_P (type) + || pass_by_reference (NULL, TYPE_MODE (type), type, true)) + new_args.safe_push (chkp_find_bounds (call_arg, gsi)); + else if (chkp_type_has_pointer (type)) + { + HOST_WIDE_INT max_bounds + = TREE_INT_CST_LOW (TYPE_SIZE (type)) / POINTER_SIZE; + tree *all_bounds = (tree *)xmalloc (sizeof (tree) * max_bounds); + HOST_WIDE_INT bnd_no; + + memset (all_bounds, 0, sizeof (tree) * max_bounds); + + chkp_find_bounds_for_elem (call_arg, all_bounds, 0, gsi); + + for (bnd_no = 0; bnd_no < max_bounds; bnd_no++) + if (all_bounds[bnd_no]) + new_args.safe_push (all_bounds[bnd_no]); + + free (all_bounds); + } + } + + if (new_args.length () == gimple_call_num_args (call)) + new_call = call; + else + { + new_call = gimple_build_call_vec (gimple_op (call, 1), new_args); + gimple_call_set_lhs (new_call, gimple_call_lhs (call)); + gimple_call_copy_flags (new_call, call); + } + new_args.release (); + + /* If we call built-in function and pass no bounds then + we do not need to change anything. */ + if (new_call == call + && fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))) + return; + + /* For direct calls fndecl is replaced with instrumented version. */ + if (fndecl) + { + tree new_decl = chkp_maybe_create_clone (fndecl)->decl; + gimple_call_set_fndecl (new_call, new_decl); + gimple_call_set_fntype (new_call, TREE_TYPE (new_decl)); + } + /* For indirect call we should fix function pointer type if + pass some bounds. */ + else if (new_call != call) + { + tree type = gimple_call_fntype (call); + type = chkp_copy_function_type_adding_bounds (type); + gimple_call_set_fntype (new_call, type); + } + + /* replace old call statement with the new one. */ + if (call != new_call) + { + FOR_EACH_SSA_TREE_OPERAND (op, call, iter, SSA_OP_ALL_DEFS) + { + SSA_NAME_DEF_STMT (op) = new_call; + } + gsi_replace (gsi, new_call, true); + } + else + update_stmt (new_call); + + gimple_call_set_with_bounds (new_call, true); +} + +/* Return constant static bounds var with specified LB and UB + if such var exists in varpool. Return NULL otherwise. */ +static tree +chkp_find_const_bounds_var (HOST_WIDE_INT lb, + HOST_WIDE_INT ub) +{ + tree val = targetm.chkp_make_bounds_constant (lb, ub); + struct varpool_node *node; + + /* We expect bounds constant is represented as a complex value + of two pointer sized integers. */ + gcc_assert (TREE_CODE (val) == COMPLEX_CST); + + FOR_EACH_VARIABLE (node) + if (POINTER_BOUNDS_P (node->decl) + && TREE_READONLY (node->decl) + && DECL_INITIAL (node->decl) + && TREE_CODE (DECL_INITIAL (node->decl)) == COMPLEX_CST + && tree_int_cst_equal (TREE_REALPART (DECL_INITIAL (node->decl)), + TREE_REALPART (val)) + && tree_int_cst_equal (TREE_IMAGPART (DECL_INITIAL (node->decl)), + TREE_IMAGPART (val))) + return node->decl; + + return NULL; +} + +/* Return constant static bounds var with specified bounds LB and UB. + If such var does not exists then new var is created with specified NAME. */ +static tree +chkp_make_static_const_bounds (HOST_WIDE_INT lb, + HOST_WIDE_INT ub, + const char *name) +{ + tree var; + + /* With LTO we may have constant bounds already in varpool. + Try to find it. */ + var = chkp_find_const_bounds_var (lb, ub); + + if (var) + return var; + + var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (name), pointer_bounds_type_node); + + TREE_PUBLIC (var) = 1; + TREE_USED (var) = 1; + TREE_READONLY (var) = 1; + TREE_STATIC (var) = 1; + TREE_ADDRESSABLE (var) = 0; + DECL_ARTIFICIAL (var) = 1; + DECL_READ_P (var) = 1; + /* We may use this symbol during ctors generation in chkp_finish_file + when all symbols are emitted. Force output to avoid undefined + symbols in ctors. */ + if (!in_lto_p) + { + DECL_INITIAL (var) = targetm.chkp_make_bounds_constant (lb, ub); + DECL_COMDAT (var) = 1; + varpool_node::get_create (var)->set_comdat_group (DECL_ASSEMBLER_NAME (var)); + varpool_node::get_create (var)->force_output = 1; + } + else + DECL_EXTERNAL (var) = 1; + varpool_node::finalize_decl (var); + + return var; +} + +/* Generate code to make bounds with specified lower bound LB and SIZE. + if AFTER is 1 then code is inserted after position pointed by ITER + otherwise code is inserted before position pointed by ITER. + If ITER is NULL then code is added to entry block. */ +static tree +chkp_make_bounds (tree lb, tree size, gimple_stmt_iterator *iter, bool after) +{ + gimple_seq seq; + gimple_stmt_iterator gsi; + gimple stmt; + tree bounds; + + if (iter) + gsi = *iter; + else + gsi = gsi_start_bb (chkp_get_entry_block ()); + + seq = NULL; + + lb = chkp_force_gimple_call_op (lb, &seq); + size = chkp_force_gimple_call_op (size, &seq); + + stmt = gimple_build_call (chkp_bndmk_fndecl, 2, lb, size); + chkp_mark_stmt (stmt); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + if (iter && after) + gsi_insert_seq_after (&gsi, seq, GSI_SAME_STMT); + else + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Made bounds: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + if (iter) + { + fprintf (dump_file, " inserted before statement: "); + print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, TDF_VOPS|TDF_MEMSYMS); + } + else + fprintf (dump_file, " at function entry\n"); + } + + /* update_stmt (stmt); */ + + return bounds; +} + +/* Return var holding zero bounds. */ +tree +chkp_get_zero_bounds_var (void) +{ + if (!chkp_zero_bounds_var) + { + tree id = get_identifier (CHKP_ZERO_BOUNDS_VAR_NAME); + symtab_node *node = symtab_node::get_for_asmname (id); + if (node) + chkp_zero_bounds_var = node->decl; + } + + if (!chkp_zero_bounds_var) + chkp_zero_bounds_var + = chkp_make_static_const_bounds (0, -1, + CHKP_ZERO_BOUNDS_VAR_NAME); + return chkp_zero_bounds_var; +} + +/* Return var holding none bounds. */ +tree +chkp_get_none_bounds_var (void) +{ + if (!chkp_none_bounds_var) + { + tree id = get_identifier (CHKP_NONE_BOUNDS_VAR_NAME); + symtab_node *node = symtab_node::get_for_asmname (id); + if (node) + chkp_none_bounds_var = node->decl; + } + + if (!chkp_none_bounds_var) + chkp_none_bounds_var + = chkp_make_static_const_bounds (-1, 0, + CHKP_NONE_BOUNDS_VAR_NAME); + return chkp_none_bounds_var; +} + +/* Return SSA_NAME used to represent zero bounds. */ +static tree +chkp_get_zero_bounds (void) +{ + if (zero_bounds) + return zero_bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Creating zero bounds..."); + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + zero_bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (zero_bounds, chkp_get_zero_bounds_var ()); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + zero_bounds = chkp_make_bounds (integer_zero_node, + integer_zero_node, + NULL, + false); + + return zero_bounds; +} + +/* Return SSA_NAME used to represent none bounds. */ +static tree +chkp_get_none_bounds (void) +{ + if (none_bounds) + return none_bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Creating none bounds..."); + + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + none_bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (none_bounds, chkp_get_none_bounds_var ()); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + none_bounds = chkp_make_bounds (integer_minus_one_node, + build_int_cst (size_type_node, 2), + NULL, + false); + + return none_bounds; +} + +/* Return bounds to be used as a result of operation which + should not create poiunter (e.g. MULT_EXPR). */ +static tree +chkp_get_invalid_op_bounds (void) +{ + return chkp_get_zero_bounds (); +} + +/* Return bounds to be used for loads of non-pointer values. */ +static tree +chkp_get_nonpointer_load_bounds (void) +{ + return chkp_get_zero_bounds (); +} + +/* Build bounds returned by CALL. */ +static tree +chkp_build_returned_bound (gimple call) +{ + gimple_stmt_iterator gsi; + tree bounds; + gimple stmt; + tree fndecl = gimple_call_fndecl (call); + + /* To avoid fixing alloca expands in targets we handle + it separately. */ + if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN)) + { + tree size = gimple_call_arg (call, 0); + tree lb = gimple_call_lhs (call); + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_make_bounds (lb, size, &iter, true); + } + /* We know bounds returned by set_bounds builtin call. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS) + { + tree lb = gimple_call_arg (call, 0); + tree size = gimple_call_arg (call, 1); + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_make_bounds (lb, size, &iter, true); + } + /* Detect bounds initialization calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS) + bounds = chkp_get_zero_bounds (); + /* Detect bounds nullification calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS) + bounds = chkp_get_none_bounds (); + /* Detect bounds copy calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS) + { + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_find_bounds (gimple_call_arg (call, 1), &iter); + } + /* Do not use retbnd when returned bounds are equal to some + of passed bounds. */ + else if ((gimple_call_return_flags (call) & ERF_RETURNS_ARG) + || gimple_call_builtin_p (call, BUILT_IN_STRCHR)) + { + gimple_stmt_iterator iter = gsi_for_stmt (call); + unsigned int retarg = 0, argno; + if (gimple_call_return_flags (call) & ERF_RETURNS_ARG) + retarg = gimple_call_return_flags (call) & ERF_RETURN_ARG_MASK; + if (gimple_call_with_bounds_p (call)) + { + for (argno = 0; argno < gimple_call_num_args (call); argno++) + if (!POINTER_BOUNDS_P (gimple_call_arg (call, argno))) + { + if (retarg) + retarg--; + else + break; + } + } + else + argno = retarg; + + bounds = chkp_find_bounds (gimple_call_arg (call, argno), &iter); + } + else + { + gcc_assert (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME); + + /* In general case build checker builtin call to + obtain returned bounds. */ + stmt = gimple_build_call (chkp_ret_bnd_fndecl, 1, + gimple_call_lhs (call)); + chkp_mark_stmt (stmt); + + gsi = gsi_for_stmt (call); + gsi_insert_after (&gsi, stmt, GSI_SAME_STMT); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + update_stmt (stmt); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Built returned bounds ("); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, ") for call: "); + print_gimple_stmt (dump_file, call, 0, TDF_VOPS|TDF_MEMSYMS); + } + + bounds = chkp_maybe_copy_and_register_bounds (gimple_call_lhs (call), bounds); + + return bounds; +} + +/* Return bounds used as returned by call + which produced SSA name VAL. */ +gimple +chkp_retbnd_call_by_val (tree val) +{ + if (TREE_CODE (val) != SSA_NAME) + return NULL; + + gcc_assert (gimple_code (SSA_NAME_DEF_STMT (val)) == GIMPLE_CALL); + + imm_use_iterator use_iter; + use_operand_p use_p; + FOR_EACH_IMM_USE_FAST (use_p, use_iter, val) + if (gimple_code (USE_STMT (use_p)) == GIMPLE_CALL + && gimple_call_fndecl (USE_STMT (use_p)) == chkp_ret_bnd_fndecl) + return USE_STMT (use_p); + + return NULL; +} + +/* Check the next parameter for the given PARM is bounds + and return it's default SSA_NAME (create if required). */ +static tree +chkp_get_next_bounds_parm (tree parm) +{ + tree bounds = TREE_CHAIN (parm); + gcc_assert (POINTER_BOUNDS_P (bounds)); + bounds = ssa_default_def (cfun, bounds); + if (!bounds) + { + bounds = make_ssa_name (TREE_CHAIN (parm), gimple_build_nop ()); + set_ssa_default_def (cfun, TREE_CHAIN (parm), bounds); + } + return bounds; +} + +/* Return bounds to be used for input argument PARM. */ +static tree +chkp_get_bound_for_parm (tree parm) +{ + tree decl = SSA_NAME_VAR (parm); + tree bounds; + + gcc_assert (TREE_CODE (decl) == PARM_DECL); + + bounds = chkp_get_registered_bounds (parm); + + if (!bounds) + bounds = chkp_get_registered_bounds (decl); + + if (!bounds) + { + tree orig_decl = cgraph_node::get (cfun->decl)->orig_decl; + + /* For static chain param we return zero bounds + because currently we do not check dereferences + of this pointer. */ + if (cfun->static_chain_decl == decl) + bounds = chkp_get_zero_bounds (); + /* If non instrumented runtime is used then it may be useful + to use zero bounds for input arguments of main + function. */ + else if (flag_chkp_zero_input_bounds_for_main + && strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (orig_decl)), + "main") == 0) + bounds = chkp_get_zero_bounds (); + else if (BOUNDED_P (parm)) + { + bounds = chkp_get_next_bounds_parm (decl); + bounds = chkp_maybe_copy_and_register_bounds (decl, bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Built arg bounds ("); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, ") for arg: "); + print_node (dump_file, "", decl, 0); + } + } + else + bounds = chkp_get_zero_bounds (); + } + + if (!chkp_get_registered_bounds (parm)) + bounds = chkp_maybe_copy_and_register_bounds (parm, bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Using bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " for parm "); + print_generic_expr (dump_file, parm, 0); + fprintf (dump_file, " of type "); + print_generic_expr (dump_file, TREE_TYPE (parm), 0); + fprintf (dump_file, ".\n"); + } + + return bounds; +} + +/* Build and return CALL_EXPR for bndstx builtin with specified + arguments. */ +tree +chkp_build_bndldx_call (tree addr, tree ptr) +{ + tree fn = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndldx_fndecl)), + chkp_bndldx_fndecl); + tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndldx_fndecl)), + fn, 2, addr, ptr); + CALL_WITH_BOUNDS_P (call) = true; + return call; +} + +/* Insert code to load bounds for PTR located by ADDR. + Code is inserted after position pointed by GSI. + Loaded bounds are returned. */ +static tree +chkp_build_bndldx (tree addr, tree ptr, gimple_stmt_iterator *gsi) +{ + gimple_seq seq; + gimple stmt; + tree bounds; + + seq = NULL; + + addr = chkp_force_gimple_call_op (addr, &seq); + ptr = chkp_force_gimple_call_op (ptr, &seq); + + stmt = gimple_build_call (chkp_bndldx_fndecl, 2, addr, ptr); + chkp_mark_stmt (stmt); + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generated bndldx for pointer "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, ": "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + } + + return bounds; +} + +/* Build and return CALL_EXPR for bndstx builtin with specified + arguments. */ +tree +chkp_build_bndstx_call (tree addr, tree ptr, tree bounds) +{ + tree fn = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndstx_fndecl)), + chkp_bndstx_fndecl); + tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndstx_fndecl)), + fn, 3, ptr, bounds, addr); + CALL_WITH_BOUNDS_P (call) = true; + return call; +} + +/* Insert code to store BOUNDS for PTR stored by ADDR. + New statements are inserted after position pointed + by GSI. */ +void +chkp_build_bndstx (tree addr, tree ptr, tree bounds, + gimple_stmt_iterator *gsi) +{ + gimple_seq seq; + gimple stmt; + + seq = NULL; + + addr = chkp_force_gimple_call_op (addr, &seq); + ptr = chkp_force_gimple_call_op (ptr, &seq); + + stmt = gimple_build_call (chkp_bndstx_fndecl, 3, ptr, bounds, addr); + chkp_mark_stmt (stmt); + gimple_call_set_with_bounds (stmt, true); + + gimple_seq_add_stmt (&seq, stmt); + + gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generated bndstx for pointer store "); + print_gimple_stmt (dump_file, gsi_stmt (*gsi), 0, TDF_VOPS|TDF_MEMSYMS); + print_gimple_stmt (dump_file, stmt, 2, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Compute bounds for pointer NODE which was assigned in + assignment statement ASSIGN. Return computed bounds. */ +static tree +chkp_compute_bounds_for_assignment (tree node, gimple assign) +{ + enum tree_code rhs_code = gimple_assign_rhs_code (assign); + tree rhs1 = gimple_assign_rhs1 (assign); + tree bounds = NULL_TREE; + gimple_stmt_iterator iter = gsi_for_stmt (assign); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Computing bounds for assignment: "); + print_gimple_stmt (dump_file, assign, 0, TDF_VOPS|TDF_MEMSYMS); + } + + switch (rhs_code) + { + case MEM_REF: + case TARGET_MEM_REF: + case COMPONENT_REF: + case ARRAY_REF: + /* We need to load bounds from the bounds table. */ + bounds = chkp_find_bounds_loaded (node, rhs1, &iter); + break; + + case VAR_DECL: + case SSA_NAME: + case ADDR_EXPR: + case POINTER_PLUS_EXPR: + case NOP_EXPR: + case CONVERT_EXPR: + case INTEGER_CST: + /* Bounds are just propagated from RHS. */ + bounds = chkp_find_bounds (rhs1, &iter); + break; + + case VIEW_CONVERT_EXPR: + /* Bounds are just propagated from RHS. */ + bounds = chkp_find_bounds (TREE_OPERAND (rhs1, 0), &iter); + break; + + case PARM_DECL: + if (BOUNDED_P (rhs1)) + { + /* We need to load bounds from the bounds table. */ + bounds = chkp_build_bndldx (chkp_build_addr_expr (rhs1), + node, &iter); + TREE_ADDRESSABLE (rhs1) = 1; + } + else + bounds = chkp_get_nonpointer_load_bounds (); + break; + + case MINUS_EXPR: + case PLUS_EXPR: + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (assign); + tree bnd1 = chkp_find_bounds (rhs1, &iter); + tree bnd2 = chkp_find_bounds (rhs2, &iter); + + /* First we try to check types of operands. If it + does not help then look at bound values. + + If some bounds are incomplete and other are + not proven to be valid (i.e. also incomplete + or invalid because value is not pointer) then + resulting value is incomplete and will be + recomputed later in chkp_finish_incomplete_bounds. */ + if (BOUNDED_P (rhs1) + && !BOUNDED_P (rhs2)) + bounds = bnd1; + else if (BOUNDED_P (rhs2) + && !BOUNDED_P (rhs1) + && rhs_code != MINUS_EXPR) + bounds = bnd2; + else if (chkp_incomplete_bounds (bnd1)) + if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR + && !chkp_incomplete_bounds (bnd2)) + bounds = bnd2; + else + bounds = incomplete_bounds; + else if (chkp_incomplete_bounds (bnd2)) + if (chkp_valid_bounds (bnd1) + && !chkp_incomplete_bounds (bnd1)) + bounds = bnd1; + else + bounds = incomplete_bounds; + else if (!chkp_valid_bounds (bnd1)) + if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR) + bounds = bnd2; + else if (bnd2 == chkp_get_zero_bounds ()) + bounds = bnd2; + else + bounds = bnd1; + else if (!chkp_valid_bounds (bnd2)) + bounds = bnd1; + else + /* Seems both operands may have valid bounds + (e.g. pointer minus pointer). In such case + use default invalid op bounds. */ + bounds = chkp_get_invalid_op_bounds (); + } + break; + + case BIT_NOT_EXPR: + case NEGATE_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + case EQ_EXPR: + case NE_EXPR: + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case MULT_EXPR: + case RDIV_EXPR: + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + case CEIL_DIV_EXPR: + case ROUND_DIV_EXPR: + case TRUNC_MOD_EXPR: + case FLOOR_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + case EXACT_DIV_EXPR: + case FIX_TRUNC_EXPR: + case FLOAT_EXPR: + case REALPART_EXPR: + case IMAGPART_EXPR: + /* No valid bounds may be produced by these exprs. */ + bounds = chkp_get_invalid_op_bounds (); + break; + + case COND_EXPR: + { + tree val1 = gimple_assign_rhs2 (assign); + tree val2 = gimple_assign_rhs3 (assign); + tree bnd1 = chkp_find_bounds (val1, &iter); + tree bnd2 = chkp_find_bounds (val2, &iter); + gimple stmt; + + if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2)) + bounds = incomplete_bounds; + else if (bnd1 == bnd2) + bounds = bnd1; + else + { + rhs1 = unshare_expr (rhs1); + + bounds = chkp_get_tmp_reg (assign); + stmt = gimple_build_assign_with_ops (COND_EXPR, bounds, + rhs1, bnd1, bnd2); + gsi_insert_after (&iter, stmt, GSI_SAME_STMT); + + if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2)) + chkp_mark_invalid_bounds (bounds); + } + } + break; + + case MAX_EXPR: + case MIN_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (assign); + tree bnd1 = chkp_find_bounds (rhs1, &iter); + tree bnd2 = chkp_find_bounds (rhs2, &iter); + + if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2)) + bounds = incomplete_bounds; + else if (bnd1 == bnd2) + bounds = bnd1; + else + { + gimple stmt; + tree cond = build2 (rhs_code == MAX_EXPR ? GT_EXPR : LT_EXPR, + boolean_type_node, rhs1, rhs2); + bounds = chkp_get_tmp_reg (assign); + stmt = gimple_build_assign_with_ops (COND_EXPR, bounds, + cond, bnd1, bnd2); + + gsi_insert_after (&iter, stmt, GSI_SAME_STMT); + + if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2)) + chkp_mark_invalid_bounds (bounds); + } + } + break; + + default: + bounds = chkp_get_zero_bounds (); + warning (0, "pointer bounds were lost due to unexpected expression %s", + get_tree_code_name (rhs_code)); + } + + gcc_assert (bounds); + + if (node) + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + + return bounds; +} + +/* Compute bounds for ssa name NODE defined by DEF_STMT pointed by ITER. + + There are just few statement codes allowed: NOP (for default ssa names), + ASSIGN, CALL, PHI, ASM. + + Return computed bounds. */ +static tree +chkp_get_bounds_by_definition (tree node, gimple def_stmt, + gimple_stmt_iterator *iter) +{ + tree var, bounds; + enum gimple_code code = gimple_code (def_stmt); + gimple stmt; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Searching for bounds for node: "); + print_generic_expr (dump_file, node, 0); + + fprintf (dump_file, " using its definition: "); + print_gimple_stmt (dump_file, def_stmt, 0, TDF_VOPS|TDF_MEMSYMS); + } + + switch (code) + { + case GIMPLE_NOP: + var = SSA_NAME_VAR (node); + switch (TREE_CODE (var)) + { + case PARM_DECL: + bounds = chkp_get_bound_for_parm (node); + break; + + case VAR_DECL: + /* For uninitialized pointers use none bounds. */ + bounds = chkp_get_none_bounds (); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + break; + + case RESULT_DECL: + { + tree base_type; + + gcc_assert (TREE_CODE (TREE_TYPE (node)) == REFERENCE_TYPE); + + base_type = TREE_TYPE (TREE_TYPE (node)); + + gcc_assert (TYPE_SIZE (base_type) + && TREE_CODE (TYPE_SIZE (base_type)) == INTEGER_CST + && tree_to_uhwi (TYPE_SIZE (base_type)) != 0); + + bounds = chkp_make_bounds (node, TYPE_SIZE_UNIT (base_type), + NULL, false); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + } + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Unexpected var with no definition\n"); + print_generic_expr (dump_file, var, 0); + } + internal_error ("chkp_get_bounds_by_definition: Unexpected var of type %s", + get_tree_code_name (TREE_CODE (var))); + } + break; + + case GIMPLE_ASSIGN: + bounds = chkp_compute_bounds_for_assignment (node, def_stmt); + break; + + case GIMPLE_CALL: + bounds = chkp_build_returned_bound (def_stmt); + break; + + case GIMPLE_PHI: + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (node)) + if (SSA_NAME_VAR (node)) + var = chkp_get_bounds_var (SSA_NAME_VAR (node)); + else + var = make_temp_ssa_name (pointer_bounds_type_node, + gimple_build_nop (), + CHKP_BOUND_TMP_NAME); + else + var = chkp_get_tmp_var (); + stmt = create_phi_node (var, gimple_bb (def_stmt)); + bounds = gimple_phi_result (stmt); + *iter = gsi_for_stmt (stmt); + + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + + /* Created bounds do not have all phi args computed and + therefore we do not know if there is a valid source + of bounds for that node. Therefore we mark bounds + as incomplete and then recompute them when all phi + args are computed. */ + chkp_register_incomplete_bounds (bounds, node); + break; + + case GIMPLE_ASM: + bounds = chkp_get_zero_bounds (); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + break; + + default: + internal_error ("chkp_get_bounds_by_definition: Unexpected GIMPLE code %s", + gimple_code_name[code]); + } + + return bounds; +} + +/* Return CALL_EXPR for bndmk with specified LOWER_BOUND and SIZE. */ +tree +chkp_build_make_bounds_call (tree lower_bound, tree size) +{ + tree call = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndmk_fndecl)), + chkp_bndmk_fndecl); + return build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndmk_fndecl)), + call, 2, lower_bound, size); +} + +/* Create static bounds var of specfified OBJ which is + is either VAR_DECL or string constant. */ +static tree +chkp_make_static_bounds (tree obj) +{ + static int string_id = 1; + static int var_id = 1; + tree *slot; + const char *var_name; + char *bnd_var_name; + tree bnd_var; + + /* First check if we already have required var. */ + if (chkp_static_var_bounds) + { + slot = chkp_static_var_bounds->get (obj); + if (slot) + return *slot; + } + + /* Build decl for bounds var. */ + if (TREE_CODE (obj) == VAR_DECL) + { + if (DECL_IGNORED_P (obj)) + { + bnd_var_name = (char *) xmalloc (strlen (CHKP_VAR_BOUNDS_PREFIX) + 10); + sprintf (bnd_var_name, "%s%d", CHKP_VAR_BOUNDS_PREFIX, var_id++); + } + else + { + var_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (obj)); + + /* For hidden symbols we want to skip first '*' char. */ + if (*var_name == '*') + var_name++; + + bnd_var_name = (char *) xmalloc (strlen (var_name) + + strlen (CHKP_BOUNDS_OF_SYMBOL_PREFIX) + 1); + strcpy (bnd_var_name, CHKP_BOUNDS_OF_SYMBOL_PREFIX); + strcat (bnd_var_name, var_name); + } + + bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (bnd_var_name), + pointer_bounds_type_node); + + /* Address of the obj will be used as lower bound. */ + TREE_ADDRESSABLE (obj) = 1; + } + else + { + bnd_var_name = (char *) xmalloc (strlen (CHKP_STRING_BOUNDS_PREFIX) + 10); + sprintf (bnd_var_name, "%s%d", CHKP_STRING_BOUNDS_PREFIX, string_id++); + + bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (bnd_var_name), + pointer_bounds_type_node); + } + + TREE_PUBLIC (bnd_var) = 0; + TREE_USED (bnd_var) = 1; + TREE_READONLY (bnd_var) = 0; + TREE_STATIC (bnd_var) = 1; + TREE_ADDRESSABLE (bnd_var) = 0; + DECL_ARTIFICIAL (bnd_var) = 1; + DECL_COMMON (bnd_var) = 1; + DECL_COMDAT (bnd_var) = 1; + DECL_READ_P (bnd_var) = 1; + DECL_INITIAL (bnd_var) = chkp_build_addr_expr (obj); + /* Force output similar to constant bounds. + See chkp_make_static_const_bounds. */ + varpool_node::get_create (bnd_var)->force_output = 1; + /* Mark symbol as requiring bounds initialization. */ + varpool_node::get_create (bnd_var)->need_bounds_init = 1; + varpool_node::finalize_decl (bnd_var); + + /* Add created var to the map to use it for other references + to obj. */ + if (!chkp_static_var_bounds) + chkp_static_var_bounds = new hash_map<tree, tree>; + + chkp_static_var_bounds->put (obj, bnd_var); + + return bnd_var; +} + +/* When var has incomplete type we cannot get size to + compute its bounds. In such cases we use checker + builtin call which determines object size at runtime. */ +static tree +chkp_generate_extern_var_bounds (tree var) +{ + tree bounds, size_reloc, lb, size, max_size, cond; + gimple_stmt_iterator gsi; + gimple_seq seq = NULL; + gimple stmt; + + /* If instrumentation is not enabled for vars having + incomplete type then just return zero bounds to avoid + checks for this var. */ + if (!flag_chkp_incomplete_type) + return chkp_get_zero_bounds (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generating bounds for extern symbol '"); + print_generic_expr (dump_file, var, 0); + fprintf (dump_file, "'\n"); + } + + stmt = gimple_build_call (chkp_sizeof_fndecl, 1, var); + + size_reloc = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME); + gimple_call_set_lhs (stmt, size_reloc); + + gimple_seq_add_stmt (&seq, stmt); + + lb = chkp_build_addr_expr (var); + size = make_ssa_name (chkp_get_size_tmp_var (), gimple_build_nop ()); + + if (flag_chkp_zero_dynamic_size_as_infinite) + { + /* We should check that size relocation was resolved. + If it was not then use maximum possible size for the var. */ + max_size = build2 (MINUS_EXPR, chkp_uintptr_type, integer_zero_node, + fold_convert (chkp_uintptr_type, lb)); + max_size = chkp_force_gimple_call_op (max_size, &seq); + + cond = build2 (NE_EXPR, boolean_type_node, size_reloc, integer_zero_node); + stmt = gimple_build_assign_with_ops (COND_EXPR, size, + cond, size_reloc, max_size); + gimple_seq_add_stmt (&seq, stmt); + } + else + { + stmt = gimple_build_assign (size, size_reloc); + gimple_seq_add_stmt (&seq, stmt); + } + + gsi = gsi_start_bb (chkp_get_entry_block ()); + gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING); + + bounds = chkp_make_bounds (lb, size, &gsi, true); + + return bounds; +} + +/* Return 1 if TYPE has fields with zero size or fields + marked with chkp_variable_size attribute. */ +bool +chkp_variable_size_type (tree type) +{ + bool res = false; + tree field; + + if (RECORD_OR_UNION_TYPE_P (type)) + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + res = res + || lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field)) + || chkp_variable_size_type (TREE_TYPE (field)); + } + else + res = !TYPE_SIZE (type) + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST + || tree_to_uhwi (TYPE_SIZE (type)) == 0; + + return res; +} + +/* Compute and return bounds for address of DECL which is + one of VAR_DECL, PARM_DECL, RESULT_DECL. */ +static tree +chkp_get_bounds_for_decl_addr (tree decl) +{ + tree bounds; + + gcc_assert (TREE_CODE (decl) == VAR_DECL + || TREE_CODE (decl) == PARM_DECL + || TREE_CODE (decl) == RESULT_DECL); + + bounds = chkp_get_registered_addr_bounds (decl); + + if (bounds) + return bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Building bounds for address of decl "); + print_generic_expr (dump_file, decl, 0); + fprintf (dump_file, "\n"); + } + + /* Use zero bounds if size is unknown and checks for + unknown sizes are restricted. */ + if ((!DECL_SIZE (decl) + || (chkp_variable_size_type (TREE_TYPE (decl)) + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)))) + && !flag_chkp_incomplete_type) + return chkp_get_zero_bounds (); + + if (flag_chkp_use_static_bounds + && TREE_CODE (decl) == VAR_DECL + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)) + && !DECL_THREAD_LOCAL_P (decl)) + { + tree bnd_var = chkp_make_static_bounds (decl); + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (bounds, bnd_var); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else if (!DECL_SIZE (decl) + || (chkp_variable_size_type (TREE_TYPE (decl)) + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)))) + { + gcc_assert (TREE_CODE (decl) == VAR_DECL); + bounds = chkp_generate_extern_var_bounds (decl); + } + else + { + tree lb = chkp_build_addr_expr (decl); + bounds = chkp_make_bounds (lb, DECL_SIZE_UNIT (decl), NULL, false); + } + + return bounds; +} + +/* Compute and return bounds for constant string. */ +static tree +chkp_get_bounds_for_string_cst (tree cst) +{ + tree bounds; + tree lb; + tree size; + + gcc_assert (TREE_CODE (cst) == STRING_CST); + + bounds = chkp_get_registered_bounds (cst); + + if (bounds) + return bounds; + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + tree bnd_var = chkp_make_static_bounds (cst); + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (bounds, bnd_var); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + { + lb = chkp_build_addr_expr (cst); + size = build_int_cst (chkp_uintptr_type, TREE_STRING_LENGTH (cst)); + bounds = chkp_make_bounds (lb, size, NULL, false); + } + + bounds = chkp_maybe_copy_and_register_bounds (cst, bounds); + + return bounds; +} + +/* Generate code to instersect bounds BOUNDS1 and BOUNDS2 and + return the result. if ITER is not NULL then Code is inserted + before position pointed by ITER. Otherwise code is added to + entry block. */ +static tree +chkp_intersect_bounds (tree bounds1, tree bounds2, gimple_stmt_iterator *iter) +{ + if (!bounds1 || bounds1 == chkp_get_zero_bounds ()) + return bounds2 ? bounds2 : bounds1; + else if (!bounds2 || bounds2 == chkp_get_zero_bounds ()) + return bounds1; + else + { + gimple_seq seq; + gimple stmt; + tree bounds; + + seq = NULL; + + stmt = gimple_build_call (chkp_intersect_fndecl, 2, bounds1, bounds2); + chkp_mark_stmt (stmt); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + /* We are probably doing narrowing for constant expression. + In such case iter may be undefined. */ + if (!iter) + { + gimple_stmt_iterator gsi = gsi_last_bb (chkp_get_entry_block ()); + iter = &gsi; + gsi_insert_seq_after (iter, seq, GSI_SAME_STMT); + } + else + gsi_insert_seq_before (iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Bounds intersection: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " inserted before statement: "); + print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, + TDF_VOPS|TDF_MEMSYMS); + } + + return bounds; + } +} + +/* Return 1 if we are allowed to narrow bounds for addressed FIELD + and 0 othersize. */ +static bool +chkp_may_narrow_to_field (tree field) +{ + return DECL_SIZE (field) && TREE_CODE (DECL_SIZE (field)) == INTEGER_CST + && tree_to_uhwi (DECL_SIZE (field)) != 0 + && (!DECL_FIELD_OFFSET (field) + || TREE_CODE (DECL_FIELD_OFFSET (field)) == INTEGER_CST) + && (!DECL_FIELD_BIT_OFFSET (field) + || TREE_CODE (DECL_FIELD_BIT_OFFSET (field)) == INTEGER_CST) + && !lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field)) + && !chkp_variable_size_type (TREE_TYPE (field)); +} + +/* Return 1 if bounds for FIELD should be narrowed to + field's own size. */ +static bool +chkp_narrow_bounds_for_field (tree field) +{ + HOST_WIDE_INT offs; + HOST_WIDE_INT bit_offs; + + if (!chkp_may_narrow_to_field (field)) + return false; + + /* Accesse to compiler generated fields should not cause + bounds narrowing. */ + if (DECL_ARTIFICIAL (field)) + return false; + + offs = tree_to_uhwi (DECL_FIELD_OFFSET (field)); + bit_offs = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)); + + return (flag_chkp_narrow_bounds + && (flag_chkp_first_field_has_own_bounds + || offs + || bit_offs)); +} + +/* Perform narrowing for BOUNDS using bounds computed for field + access COMPONENT. ITER meaning is the same as for + chkp_intersect_bounds. */ +static tree +chkp_narrow_bounds_to_field (tree bounds, tree component, + gimple_stmt_iterator *iter) +{ + tree field = TREE_OPERAND (component, 1); + tree size = DECL_SIZE_UNIT (field); + tree field_ptr = chkp_build_addr_expr (component); + tree field_bounds; + + field_bounds = chkp_make_bounds (field_ptr, size, iter, false); + + return chkp_intersect_bounds (field_bounds, bounds, iter); +} + +/* Parse field or array access NODE. + + PTR ouput parameter holds a pointer to the outermost + object. + + BITFIELD output parameter is set to 1 if bitfield is + accessed and to 0 otherwise. If it is 1 then ELT holds + outer component for accessed bit field. + + SAFE outer parameter is set to 1 if access is safe and + checks are not required. + + BOUNDS outer parameter holds bounds to be used to check + access (may be NULL). + + If INNERMOST_BOUNDS is 1 then try to narrow bounds to the + innermost accessed component. */ +static void +chkp_parse_array_and_component_ref (tree node, tree *ptr, + tree *elt, bool *safe, + bool *bitfield, + tree *bounds, + gimple_stmt_iterator *iter, + bool innermost_bounds) +{ + tree comp_to_narrow = NULL_TREE; + tree last_comp = NULL_TREE; + bool array_ref_found = false; + tree *nodes; + tree var; + int len; + int i; + + /* Compute tree height for expression. */ + var = node; + len = 1; + while (TREE_CODE (var) == COMPONENT_REF + || TREE_CODE (var) == ARRAY_REF + || TREE_CODE (var) == VIEW_CONVERT_EXPR) + { + var = TREE_OPERAND (var, 0); + len++; + } + + gcc_assert (len > 1); + + /* It is more convenient for us to scan left-to-right, + so walk tree again and put all node to nodes vector + in reversed order. */ + nodes = XALLOCAVEC (tree, len); + nodes[len - 1] = node; + for (i = len - 2; i >= 0; i--) + nodes[i] = TREE_OPERAND (nodes[i + 1], 0); + + if (bounds) + *bounds = NULL; + *safe = true; + *bitfield = (TREE_CODE (node) == COMPONENT_REF + && DECL_BIT_FIELD_TYPE (TREE_OPERAND (node, 1))); + /* To get bitfield address we will need outer elemnt. */ + if (*bitfield) + *elt = nodes[len - 2]; + else + *elt = NULL_TREE; + + /* If we have indirection in expression then compute + outermost structure bounds. Computed bounds may be + narrowed later. */ + if (TREE_CODE (nodes[0]) == MEM_REF || INDIRECT_REF_P (nodes[0])) + { + *safe = false; + *ptr = TREE_OPERAND (nodes[0], 0); + if (bounds) + *bounds = chkp_find_bounds (*ptr, iter); + } + else + { + gcc_assert (TREE_CODE (var) == VAR_DECL + || TREE_CODE (var) == PARM_DECL + || TREE_CODE (var) == RESULT_DECL + || TREE_CODE (var) == STRING_CST + || TREE_CODE (var) == SSA_NAME); + + *ptr = chkp_build_addr_expr (var); + } + + /* In this loop we are trying to find a field access + requiring narrowing. There are two simple rules + for search: + 1. Leftmost array_ref is chosen if any. + 2. Rightmost suitable component_ref is chosen if innermost + bounds are required and no array_ref exists. */ + for (i = 1; i < len; i++) + { + var = nodes[i]; + + if (TREE_CODE (var) == ARRAY_REF) + { + *safe = false; + array_ref_found = true; + if (flag_chkp_narrow_bounds + && !flag_chkp_narrow_to_innermost_arrray + && (!last_comp + || chkp_may_narrow_to_field (TREE_OPERAND (last_comp, 1)))) + { + comp_to_narrow = last_comp; + break; + } + } + else if (TREE_CODE (var) == COMPONENT_REF) + { + tree field = TREE_OPERAND (var, 1); + + if (innermost_bounds + && !array_ref_found + && chkp_narrow_bounds_for_field (field)) + comp_to_narrow = var; + last_comp = var; + + if (flag_chkp_narrow_bounds + && flag_chkp_narrow_to_innermost_arrray + && TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE) + { + if (bounds) + *bounds = chkp_narrow_bounds_to_field (*bounds, var, iter); + comp_to_narrow = NULL; + } + } + else if (TREE_CODE (var) == VIEW_CONVERT_EXPR) + /* Nothing to do for it. */ + ; + else + gcc_unreachable (); + } + + if (comp_to_narrow && DECL_SIZE (TREE_OPERAND (comp_to_narrow, 1)) && bounds) + *bounds = chkp_narrow_bounds_to_field (*bounds, comp_to_narrow, iter); + + if (innermost_bounds && bounds && !*bounds) + *bounds = chkp_find_bounds (*ptr, iter); +} + +/* Compute and return bounds for address of OBJ. */ +static tree +chkp_make_addressed_object_bounds (tree obj, gimple_stmt_iterator *iter) +{ + tree bounds = chkp_get_registered_addr_bounds (obj); + + if (bounds) + return bounds; + + switch (TREE_CODE (obj)) + { + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + bounds = chkp_get_bounds_for_decl_addr (obj); + break; + + case STRING_CST: + bounds = chkp_get_bounds_for_string_cst (obj); + break; + + case ARRAY_REF: + case COMPONENT_REF: + { + tree elt; + tree ptr; + bool safe; + bool bitfield; + + chkp_parse_array_and_component_ref (obj, &ptr, &elt, &safe, + &bitfield, &bounds, iter, true); + + gcc_assert (bounds); + } + break; + + case FUNCTION_DECL: + case LABEL_DECL: + bounds = chkp_get_zero_bounds (); + break; + + case MEM_REF: + bounds = chkp_find_bounds (TREE_OPERAND (obj, 0), iter); + break; + + case REALPART_EXPR: + case IMAGPART_EXPR: + bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (obj, 0), iter); + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "chkp_make_addressed_object_bounds: " + "unexpected object of type %s\n", + get_tree_code_name (TREE_CODE (obj))); + print_node (dump_file, "", obj, 0); + } + internal_error ("chkp_make_addressed_object_bounds: " + "Unexpected tree code %s", + get_tree_code_name (TREE_CODE (obj))); + } + + chkp_register_addr_bounds (obj, bounds); + + return bounds; +} + +/* Compute bounds for pointer PTR loaded from PTR_SRC. Generate statements + to compute bounds if required. Computed bounds should be available at + position pointed by ITER. + + If PTR_SRC is NULL_TREE then pointer definition is identified. + + If PTR_SRC is not NULL_TREE then ITER points to statements which loads + PTR. If PTR is a any memory reference then ITER points to a statement + after which bndldx will be inserterd. In both cases ITER will be updated + to point to the inserted bndldx statement. */ + +static tree +chkp_find_bounds_1 (tree ptr, tree ptr_src, gimple_stmt_iterator *iter) +{ + tree addr = NULL_TREE; + tree bounds = NULL_TREE; + + if (!ptr_src) + ptr_src = ptr; + + bounds = chkp_get_registered_bounds (ptr_src); + + if (bounds) + return bounds; + + switch (TREE_CODE (ptr_src)) + { + case MEM_REF: + case VAR_DECL: + if (BOUNDED_P (ptr_src)) + if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr)) + bounds = chkp_get_zero_bounds (); + else + { + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + } + else + bounds = chkp_get_nonpointer_load_bounds (); + break; + + case ARRAY_REF: + case COMPONENT_REF: + addr = get_base_address (ptr_src); + if (DECL_P (addr) + || TREE_CODE (addr) == MEM_REF + || TREE_CODE (addr) == TARGET_MEM_REF) + { + if (BOUNDED_P (ptr_src)) + if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr)) + bounds = chkp_get_zero_bounds (); + else + { + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + } + else + bounds = chkp_get_nonpointer_load_bounds (); + } + else + { + gcc_assert (TREE_CODE (addr) == SSA_NAME); + bounds = chkp_find_bounds (addr, iter); + } + break; + + case PARM_DECL: + gcc_unreachable (); + bounds = chkp_get_bound_for_parm (ptr_src); + break; + + case TARGET_MEM_REF: + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + break; + + case SSA_NAME: + bounds = chkp_get_registered_bounds (ptr_src); + if (!bounds) + { + gimple def_stmt = SSA_NAME_DEF_STMT (ptr_src); + gimple_stmt_iterator phi_iter; + + bounds = chkp_get_bounds_by_definition (ptr_src, def_stmt, &phi_iter); + + gcc_assert (bounds); + + if (gimple_code (def_stmt) == GIMPLE_PHI) + { + unsigned i; + + for (i = 0; i < gimple_phi_num_args (def_stmt); i++) + { + tree arg = gimple_phi_arg_def (def_stmt, i); + tree arg_bnd; + gimple phi_bnd; + + arg_bnd = chkp_find_bounds (arg, NULL); + + /* chkp_get_bounds_by_definition created new phi + statement and phi_iter points to it. + + Previous call to chkp_find_bounds could create + new basic block and therefore change phi statement + phi_iter points to. */ + phi_bnd = gsi_stmt (phi_iter); + + add_phi_arg (phi_bnd, arg_bnd, + gimple_phi_arg_edge (def_stmt, i), + UNKNOWN_LOCATION); + } + + /* If all bound phi nodes have their arg computed + then we may finish its computation. See + chkp_finish_incomplete_bounds for more details. */ + if (chkp_may_finish_incomplete_bounds ()) + chkp_finish_incomplete_bounds (); + } + + gcc_assert (bounds == chkp_get_registered_bounds (ptr_src) + || chkp_incomplete_bounds (bounds)); + } + break; + + case ADDR_EXPR: + bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (ptr_src, 0), iter); + break; + + case INTEGER_CST: + if (integer_zerop (ptr_src)) + bounds = chkp_get_none_bounds (); + else + bounds = chkp_get_invalid_op_bounds (); + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "chkp_find_bounds: unexpected ptr of type %s\n", + get_tree_code_name (TREE_CODE (ptr_src))); + print_node (dump_file, "", ptr_src, 0); + } + internal_error ("chkp_find_bounds: Unexpected tree code %s", + get_tree_code_name (TREE_CODE (ptr_src))); + } + + if (!bounds) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (stderr, "chkp_find_bounds: cannot find bounds for pointer\n"); + print_node (dump_file, "", ptr_src, 0); + } + internal_error ("chkp_find_bounds: Cannot find bounds for pointer"); + } + + return bounds; +} + +/* Normal case for bounds search without forced narrowing. */ +static tree +chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter) +{ + return chkp_find_bounds_1 (ptr, NULL_TREE, iter); +} + +/* Search bounds for pointer PTR loaded from PTR_SRC + by statement *ITER points to. */ +static tree +chkp_find_bounds_loaded (tree ptr, tree ptr_src, gimple_stmt_iterator *iter) +{ + return chkp_find_bounds_1 (ptr, ptr_src, iter); +} + +/* Helper function which checks type of RHS and finds all pointers in + it. For each found pointer we build it's accesses in LHS and RHS + objects and then call HANDLER for them. Function is used to copy + or initilize bounds for copied object. */ +static void +chkp_walk_pointer_assignments (tree lhs, tree rhs, void *arg, + assign_handler handler) +{ + tree type = TREE_TYPE (lhs); + + /* We have nothing to do with clobbers. */ + if (TREE_CLOBBER_P (rhs)) + return; + + if (BOUNDED_TYPE_P (type)) + handler (lhs, rhs, arg); + else if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + + if (TREE_CODE (rhs) == CONSTRUCTOR) + { + unsigned HOST_WIDE_INT cnt; + tree val; + + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, field, val) + { + if (chkp_type_has_pointer (TREE_TYPE (field))) + { + tree lhs_field = chkp_build_component_ref (lhs, field); + chkp_walk_pointer_assignments (lhs_field, val, arg, handler); + } + } + } + else + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && chkp_type_has_pointer (TREE_TYPE (field))) + { + tree rhs_field = chkp_build_component_ref (rhs, field); + tree lhs_field = chkp_build_component_ref (lhs, field); + chkp_walk_pointer_assignments (lhs_field, rhs_field, arg, handler); + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + unsigned HOST_WIDE_INT cur = 0; + tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree etype = TREE_TYPE (type); + tree esize = TYPE_SIZE (etype); + + if (TREE_CODE (rhs) == CONSTRUCTOR) + { + unsigned HOST_WIDE_INT cnt; + tree purp, val, lhs_elem; + + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, purp, val) + { + if (purp && TREE_CODE (purp) == RANGE_EXPR) + { + tree lo_index = TREE_OPERAND (purp, 0); + tree hi_index = TREE_OPERAND (purp, 1); + + for (cur = (unsigned)tree_to_uhwi (lo_index); + cur <= (unsigned)tree_to_uhwi (hi_index); + cur++) + { + lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur); + chkp_walk_pointer_assignments (lhs_elem, val, arg, handler); + } + } + else + { + if (purp) + { + gcc_assert (TREE_CODE (purp) == INTEGER_CST); + cur = tree_to_uhwi (purp); + } + + lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur++); + + chkp_walk_pointer_assignments (lhs_elem, val, arg, handler); + } + } + } + /* Copy array only when size is known. */ + else if (maxval && !integer_minus_onep (maxval)) + for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++) + { + tree lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur); + tree rhs_elem = chkp_build_array_ref (rhs, etype, esize, cur); + chkp_walk_pointer_assignments (lhs_elem, rhs_elem, arg, handler); + } + } + else + internal_error("chkp_walk_pointer_assignments: unexpected RHS type: %s", + get_tree_code_name (TREE_CODE (type))); +} + +/* Add code to copy bounds for assignment of RHS to LHS. + ARG is an iterator pointing ne code position. */ +static void +chkp_copy_bounds_for_elem (tree lhs, tree rhs, void *arg) +{ + gimple_stmt_iterator *iter = (gimple_stmt_iterator *)arg; + tree bounds = chkp_find_bounds (rhs, iter); + tree addr = chkp_build_addr_expr(lhs); + + chkp_build_bndstx (addr, rhs, bounds, iter); +} + +/* Emit static bound initilizers and size vars. */ +void +chkp_finish_file (void) +{ + struct varpool_node *node; + struct chkp_ctor_stmt_list stmts; + + if (seen_error ()) + return; + + /* Iterate through varpool and generate bounds initialization + constructors for all statically initialized pointers. */ + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + FOR_EACH_VARIABLE (node) + /* Check that var is actually emitted and we need and may initialize + its bounds. */ + if (node->need_bounds_init + && !POINTER_BOUNDS_P (node->decl) + && DECL_RTL (node->decl) + && MEM_P (DECL_RTL (node->decl)) + && TREE_ASM_WRITTEN (node->decl)) + { + chkp_walk_pointer_assignments (node->decl, + DECL_INITIAL (node->decl), + &stmts, + chkp_add_modification_to_stmt_list); + + if (stmts.avail <= 0) + { + cgraph_build_static_cdtor ('P', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 3); + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + } + } + + if (stmts.stmts) + cgraph_build_static_cdtor ('P', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 3); + + /* Iterate through varpool and generate bounds initialization + constructors for all static bounds vars. */ + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + FOR_EACH_VARIABLE (node) + if (node->need_bounds_init + && POINTER_BOUNDS_P (node->decl) + && TREE_ASM_WRITTEN (node->decl)) + { + tree bnd = node->decl; + tree var; + + gcc_assert (DECL_INITIAL (bnd) + && TREE_CODE (DECL_INITIAL (bnd)) == ADDR_EXPR); + + var = TREE_OPERAND (DECL_INITIAL (bnd), 0); + chkp_output_static_bounds (bnd, var, &stmts); + } + + if (stmts.stmts) + cgraph_build_static_cdtor ('B', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 2); + + delete chkp_static_var_bounds; + delete chkp_bounds_map; +} + +/* An instrumentation function which is called for each statement + having memory access we want to instrument. It inserts check + code and bounds copy code. + + ITER points to statement to instrument. + + NODE holds memory access in statement to check. + + LOC holds the location information for statement. + + DIRFLAGS determines whether access is read or write. + + ACCESS_OFFS should be added to address used in NODE + before check. + + ACCESS_SIZE holds size of checked access. + + SAFE indicates if NODE access is safe and should not be + checked. */ +static void +chkp_process_stmt (gimple_stmt_iterator *iter, tree node, + location_t loc, tree dirflag, + tree access_offs, tree access_size, + bool safe) +{ + tree node_type = TREE_TYPE (node); + tree size = access_size ? access_size : TYPE_SIZE_UNIT (node_type); + tree addr_first = NULL_TREE; /* address of the first accessed byte */ + tree addr_last = NULL_TREE; /* address of the last accessed byte */ + tree ptr = NULL_TREE; /* a pointer used for dereference */ + tree bounds = NULL_TREE; + + /* We do not need instrumentation for clobbers. */ + if (dirflag == integer_one_node + && gimple_code (gsi_stmt (*iter)) == GIMPLE_ASSIGN + && TREE_CLOBBER_P (gimple_assign_rhs1 (gsi_stmt (*iter)))) + return; + + switch (TREE_CODE (node)) + { + case ARRAY_REF: + case COMPONENT_REF: + { + bool bitfield; + tree elt; + + if (safe) + { + /* We are not going to generate any checks, so do not + generate bounds as well. */ + addr_first = chkp_build_addr_expr (node); + break; + } + + chkp_parse_array_and_component_ref (node, &ptr, &elt, &safe, + &bitfield, &bounds, iter, false); + + /* Break if there is no dereference and operation is safe. */ + + if (bitfield) + { + tree field = TREE_OPERAND (node, 1); + + if (TREE_CODE (DECL_SIZE_UNIT (field)) == INTEGER_CST) + size = DECL_SIZE_UNIT (field); + + if (elt) + elt = chkp_build_addr_expr (elt); + addr_first = fold_convert_loc (loc, ptr_type_node, elt ? elt : ptr); + addr_first = fold_build_pointer_plus_loc (loc, + addr_first, + byte_position (field)); + } + else + addr_first = chkp_build_addr_expr (node); + } + break; + + case INDIRECT_REF: + ptr = TREE_OPERAND (node, 0); + addr_first = ptr; + break; + + case MEM_REF: + ptr = TREE_OPERAND (node, 0); + addr_first = chkp_build_addr_expr (node); + break; + + case TARGET_MEM_REF: + ptr = TMR_BASE (node); + addr_first = chkp_build_addr_expr (node); + break; + + case ARRAY_RANGE_REF: + printf("ARRAY_RANGE_REF\n"); + debug_gimple_stmt(gsi_stmt(*iter)); + debug_tree(node); + gcc_unreachable (); + break; + + case BIT_FIELD_REF: + { + tree offs, rem, bpu; + + gcc_assert (!access_offs); + gcc_assert (!access_size); + + bpu = fold_convert (size_type_node, bitsize_int (BITS_PER_UNIT)); + offs = fold_convert (size_type_node, TREE_OPERAND (node, 2)); + rem = size_binop_loc (loc, TRUNC_MOD_EXPR, offs, bpu); + offs = size_binop_loc (loc, TRUNC_DIV_EXPR, offs, bpu); + + size = fold_convert (size_type_node, TREE_OPERAND (node, 1)); + size = size_binop_loc (loc, PLUS_EXPR, size, rem); + size = size_binop_loc (loc, CEIL_DIV_EXPR, size, bpu); + size = fold_convert (size_type_node, size); + + chkp_process_stmt (iter, TREE_OPERAND (node, 0), loc, + dirflag, offs, size, safe); + return; + } + break; + + case VAR_DECL: + case RESULT_DECL: + case PARM_DECL: + if (dirflag != integer_one_node + || DECL_REGISTER (node)) + return; + + safe = true; + addr_first = chkp_build_addr_expr (node); + break; + + default: + return; + } + + /* If addr_last was not computed then use (addr_first + size - 1) + expression to compute it. */ + if (!addr_last) + { + addr_last = fold_build_pointer_plus_loc (loc, addr_first, size); + addr_last = fold_build_pointer_plus_hwi_loc (loc, addr_last, -1); + } + + /* Shift both first_addr and last_addr by access_offs if specified. */ + if (access_offs) + { + addr_first = fold_build_pointer_plus_loc (loc, addr_first, access_offs); + addr_last = fold_build_pointer_plus_loc (loc, addr_last, access_offs); + } + + /* Generate bndcl/bndcu checks if memory access is not safe. */ + if (!safe) + { + gimple_stmt_iterator stmt_iter = *iter; + + if (!bounds) + bounds = chkp_find_bounds (ptr, iter); + + chkp_check_mem_access (addr_first, addr_last, bounds, + stmt_iter, loc, dirflag); + } + + /* We need to store bounds in case pointer is stored. */ + if (dirflag == integer_one_node + && chkp_type_has_pointer (node_type) + && flag_chkp_store_bounds) + { + gimple stmt = gsi_stmt (*iter); + tree rhs1 = gimple_assign_rhs1 (stmt); + enum tree_code rhs_code = gimple_assign_rhs_code (stmt); + + if (get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS) + chkp_walk_pointer_assignments (node, rhs1, iter, + chkp_copy_bounds_for_elem); + else + { + bounds = chkp_compute_bounds_for_assignment (NULL_TREE, stmt); + chkp_build_bndstx (addr_first, rhs1, bounds, iter); + } + } +} + +/* Add code to copy bounds for all pointers copied + in ASSIGN created during inline of EDGE. */ +void +chkp_copy_bounds_for_assign (gimple assign, struct cgraph_edge *edge) +{ + tree lhs = gimple_assign_lhs (assign); + tree rhs = gimple_assign_rhs1 (assign); + gimple_stmt_iterator iter = gsi_for_stmt (assign); + + if (!flag_chkp_store_bounds) + return; + + chkp_walk_pointer_assignments (lhs, rhs, &iter, chkp_copy_bounds_for_elem); + + /* We should create edges for all created calls to bndldx and bndstx. */ + while (gsi_stmt (iter) != assign) + { + gimple stmt = gsi_stmt (iter); + if (gimple_code (stmt) == GIMPLE_CALL) + { + tree fndecl = gimple_call_fndecl (stmt); + struct cgraph_node *callee = cgraph_node::get_create (fndecl); + struct cgraph_edge *new_edge; + + gcc_assert (fndecl == chkp_bndstx_fndecl + || fndecl == chkp_bndldx_fndecl + || fndecl == chkp_ret_bnd_fndecl); + + new_edge = edge->caller->create_edge (callee, stmt, edge->count, + edge->frequency); + new_edge->frequency = compute_call_stmt_bb_frequency + (edge->caller->decl, gimple_bb (stmt)); + } + gsi_prev (&iter); + } +} + +/* Some code transformation made during instrumentation pass + may put code into inconsistent state. Here we find and fix + such flaws. */ +void +chkp_fix_cfg () +{ + basic_block bb; + gimple_stmt_iterator i; + + /* We could insert some code right after stmt which ends bb. + We wanted to put this code on fallthru edge but did not + add new edges from the beginning because it may cause new + phi node creation which may be incorrect due to incomplete + bound phi nodes. */ + FOR_ALL_BB_FN (bb, cfun) + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + { + gimple stmt = gsi_stmt (i); + gimple_stmt_iterator next = i; + + gsi_next (&next); + + if (stmt_ends_bb_p (stmt) + && !gsi_end_p (next)) + { + edge fall = find_fallthru_edge (bb->succs); + basic_block dest = NULL; + int flags = 0; + + gcc_assert (fall); + + /* We cannot split abnormal edge. Therefore we + store its params, make it regular and then + rebuild abnormal edge after split. */ + if (fall->flags & EDGE_ABNORMAL) + { + flags = fall->flags & ~EDGE_FALLTHRU; + dest = fall->dest; + + fall->flags &= ~EDGE_COMPLEX; + } + + while (!gsi_end_p (next)) + { + gimple next_stmt = gsi_stmt (next); + gsi_remove (&next, false); + gsi_insert_on_edge (fall, next_stmt); + } + + gsi_commit_edge_inserts (); + + /* Re-create abnormal edge. */ + if (dest) + make_edge (bb, dest, flags); + } + } +} + +/* Walker callback for chkp_replace_function_pointers. Replaces + function pointer in the specified operand with pointer to the + instrumented function version. */ +static tree +chkp_replace_function_pointer (tree *op, int *walk_subtrees, + void *data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*op) == FUNCTION_DECL + && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (*op)) + /* Do not replace builtins for now. */ + && DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN) + { + struct cgraph_node *node = cgraph_node::get_create (*op); + + if (!node->instrumentation_clone) + chkp_maybe_create_clone (*op); + + *op = node->instrumented_version->decl; + *walk_subtrees = 0; + } + + return NULL; +} + +/* This function searches for function pointers in statement + pointed by GSI and replaces them with pointers to instrumented + function versions. */ +static void +chkp_replace_function_pointers (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + /* For calls we want to walk call args only. */ + if (gimple_code (stmt) == GIMPLE_CALL) + { + unsigned i; + for (i = 0; i < gimple_call_num_args (stmt); i++) + walk_tree (gimple_call_arg_ptr (stmt, i), + chkp_replace_function_pointer, NULL, NULL); + } + else + walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL); +} + +/* This function instruments all statements working with memory, + calls and rets. + + It also removes excess statements from static initializers. */ +static void +chkp_instrument_function (void) +{ + basic_block bb, next; + gimple_stmt_iterator i; + enum gimple_rhs_class grhs_class; + bool safe = lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)); + + bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; + do + { + next = bb->next_bb; + for (i = gsi_start_bb (bb); !gsi_end_p (i); ) + { + gimple s = gsi_stmt (i); + + /* Skip statement marked to not be instrumented. */ + if (chkp_marked_stmt_p (s)) + { + gsi_next (&i); + continue; + } + + chkp_replace_function_pointers (&i); + + switch (gimple_code (s)) + { + case GIMPLE_ASSIGN: + chkp_process_stmt (&i, gimple_assign_lhs (s), + gimple_location (s), integer_one_node, + NULL_TREE, NULL_TREE, safe); + chkp_process_stmt (&i, gimple_assign_rhs1 (s), + gimple_location (s), integer_zero_node, + NULL_TREE, NULL_TREE, safe); + grhs_class = get_gimple_rhs_class (gimple_assign_rhs_code (s)); + if (grhs_class == GIMPLE_BINARY_RHS) + chkp_process_stmt (&i, gimple_assign_rhs2 (s), + gimple_location (s), integer_zero_node, + NULL_TREE, NULL_TREE, safe); + break; + + case GIMPLE_RETURN: + if (gimple_return_retval (s) != NULL_TREE) + { + chkp_process_stmt (&i, gimple_return_retval (s), + gimple_location (s), + integer_zero_node, + NULL_TREE, NULL_TREE, safe); + + /* Additionally we need to add bounds + to return statement. */ + chkp_add_bounds_to_ret_stmt (&i); + } + break; + + case GIMPLE_CALL: + chkp_add_bounds_to_call_stmt (&i); + break; + + default: + ; + } + + gsi_next (&i); + + /* We do not need any actual pointer stores in checker + static initializer. */ + if (lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)) + && gimple_code (s) == GIMPLE_ASSIGN + && gimple_store_p (s)) + { + gimple_stmt_iterator del_iter = gsi_for_stmt (s); + gsi_remove (&del_iter, true); + unlink_stmt_vdef (s); + release_defs(s); + } + } + bb = next; + } + while (bb); + + /* Some input params may have bounds and be address taken. In this case + we should store incoming bounds into bounds table. */ + tree arg; + if (flag_chkp_store_bounds) + for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg)) + if (TREE_ADDRESSABLE (arg)) + { + if (BOUNDED_P (arg)) + { + tree bounds = chkp_get_next_bounds_parm (arg); + tree def_ptr = ssa_default_def (cfun, arg); + gimple_stmt_iterator iter + = gsi_start_bb (chkp_get_entry_block ()); + chkp_build_bndstx (chkp_build_addr_expr (arg), + def_ptr ? def_ptr : arg, + bounds, &iter); + + /* Skip bounds arg. */ + arg = TREE_CHAIN (arg); + } + else if (chkp_type_has_pointer (TREE_TYPE (arg))) + { + tree orig_arg = arg; + bitmap slots = BITMAP_ALLOC (NULL); + gimple_stmt_iterator iter + = gsi_start_bb (chkp_get_entry_block ()); + bitmap_iterator bi; + unsigned bnd_no; + + chkp_find_bound_slots (TREE_TYPE (arg), slots); + + EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) + { + tree bounds = chkp_get_next_bounds_parm (arg); + HOST_WIDE_INT offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT; + tree addr = chkp_build_addr_expr (orig_arg); + tree ptr = build2 (MEM_REF, ptr_type_node, addr, + build_int_cst (ptr_type_node, offs)); + chkp_build_bndstx (chkp_build_addr_expr (ptr), ptr, + bounds, &iter); + + arg = DECL_CHAIN (arg); + } + BITMAP_FREE (slots); + } + } +} + +/* Find init/null/copy_ptr_bounds calls and replace them + with assignments. It should allow better code + optimization. */ + +static void +chkp_remove_useless_builtins () +{ + basic_block bb; + gimple_stmt_iterator gsi; + + FOR_EACH_BB_FN (bb, cfun) + { + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree fndecl; + enum built_in_function fcode; + + /* Find builtins returning first arg and replace + them with assignments. */ + if (gimple_code (stmt) == GIMPLE_CALL + && (fndecl = gimple_call_fndecl (stmt)) + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (fcode = DECL_FUNCTION_CODE (fndecl)) + && (fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS)) + { + tree res = gimple_call_arg (stmt, 0); + update_call_from_tree (&gsi, res); + stmt = gsi_stmt (gsi); + update_stmt (stmt); + } + } + } +} + +/* Initialize pass. */ +static void +chkp_init (void) +{ + basic_block bb; + gimple_stmt_iterator i; + + in_chkp_pass = true; + + for (bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; bb; bb = bb->next_bb) + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + chkp_unmark_stmt (gsi_stmt (i)); + + chkp_invalid_bounds = new hash_set<tree>; + chkp_completed_bounds_set = new hash_set<tree>; + delete chkp_reg_bounds; + chkp_reg_bounds = new hash_map<tree, tree>; + delete chkp_bound_vars; + chkp_bound_vars = new hash_map<tree, tree>; + chkp_reg_addr_bounds = new hash_map<tree, tree>; + chkp_incomplete_bounds_map = new hash_map<tree, tree>; + delete chkp_bounds_map; + chkp_bounds_map = new hash_map<tree, tree>; + chkp_abnormal_copies = BITMAP_GGC_ALLOC (); + + entry_block = NULL; + zero_bounds = NULL_TREE; + none_bounds = NULL_TREE; + incomplete_bounds = integer_zero_node; + tmp_var = NULL_TREE; + size_tmp_var = NULL_TREE; + + chkp_uintptr_type = lang_hooks.types.type_for_mode (ptr_mode, true); + + /* We create these constant bounds once for each object file. + These symbols go to comdat section and result in single copy + of each one in the final binary. */ + chkp_get_zero_bounds_var (); + chkp_get_none_bounds_var (); + + calculate_dominance_info (CDI_DOMINATORS); + calculate_dominance_info (CDI_POST_DOMINATORS); + + bitmap_obstack_initialize (NULL); +} + +/* Finalize instrumentation pass. */ +static void +chkp_fini (void) +{ + in_chkp_pass = false; + + delete chkp_invalid_bounds; + delete chkp_completed_bounds_set; + delete chkp_reg_addr_bounds; + delete chkp_incomplete_bounds_map; + + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + + bitmap_obstack_release (NULL); +} + +/* Main instrumentation pass function. */ +static unsigned int +chkp_execute (void) +{ + chkp_init (); + + chkp_instrument_function (); + + chkp_remove_useless_builtins (); + + chkp_function_mark_instrumented (cfun->decl); + + chkp_fix_cfg (); + + chkp_fini (); + + return 0; +} + +/* Instrumentation pass gate. */ +static bool +chkp_gate (void) +{ + return cgraph_node::get (cfun->decl)->instrumentation_clone + || lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)); +} + +namespace { + +const pass_data pass_data_chkp = +{ + GIMPLE_PASS, /* type */ + "chkp", /* 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 : public gimple_opt_pass +{ +public: + pass_chkp (gcc::context *ctxt) + : gimple_opt_pass (pass_data_chkp, ctxt) + {} + + /* opt_pass methods: */ + virtual opt_pass * clone () + { + return new pass_chkp (m_ctxt); + } + + virtual bool gate (function *) + { + return chkp_gate (); + } + + virtual unsigned int execute (function *) + { + return chkp_execute (); + } + +}; // class pass_chkp + +} // anon namespace + +gimple_opt_pass * +make_pass_chkp (gcc::context *ctxt) +{ + return new pass_chkp (ctxt); +} + +#include "gt-tree-chkp.h" diff --git a/gcc/tree-chkp.h b/gcc/tree-chkp.h new file mode 100644 index 00000000000..a349baf618f --- /dev/null +++ b/gcc/tree-chkp.h @@ -0,0 +1,58 @@ +/* Declaration of interface functions of Pointer Bounds Checker. + Copyright (C) 2014 Free Software Foundation, Inc. + +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/>. */ + +#ifndef GCC_TREE_CHKP_H +#define GCC_TREE_CHKP_H + +#define DECL_BOUNDS(NODE) (chkp_get_bounds (DECL_WRTL_CHECK (NODE))) + +#define SET_DECL_BOUNDS(NODE, VAL) \ + (chkp_set_bounds (DECL_WRTL_CHECK (NODE), VAL)) + +extern tree chkp_get_bounds (tree node); +extern void chkp_set_bounds (tree node, tree val); +extern bool chkp_register_var_initializer (tree var); +extern void chkp_finish_file (void); +extern bool chkp_type_has_pointer (const_tree type); +extern unsigned chkp_type_bounds_count (const_tree type); +extern tree chkp_make_bounds_for_struct_addr (tree ptr); +extern tree chkp_get_zero_bounds_var (void); +extern tree chkp_get_none_bounds_var (void); +extern void chkp_check_mem_access (tree first, tree last, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag); +extern void chkp_fix_cfg (void); +extern bool chkp_variable_size_type (tree type); +extern tree chkp_build_make_bounds_call (tree lb, tree size); +extern tree chkp_build_bndldx_call (tree addr, tree ptr); +extern tree chkp_build_bndstx_call (tree addr, tree ptr, tree bounds); +extern void chkp_find_bound_slots (const_tree type, bitmap res); +extern void chkp_build_bndstx (tree addr, tree ptr, tree bounds, + gimple_stmt_iterator *gsi); +extern gimple chkp_retbnd_call_by_val (tree val); +extern bool chkp_function_instrumented_p (tree fndecl); +extern void chkp_function_mark_instrumented (tree fndecl); +extern void chkp_copy_bounds_for_assign (gimple assign, + struct cgraph_edge *edge); +extern bool chkp_gimple_call_builtin_p (gimple call, + enum built_in_function code); +extern void chkp_expand_bounds_reset_for_mem (tree mem, tree ptr); + +#endif /* GCC_TREE_CHKP_H */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 30db89360fe..f8ee8ccb748 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -464,6 +464,8 @@ enum tree_index { TI_FILEPTR_TYPE, TI_POINTER_SIZED_TYPE, + TI_POINTER_BOUNDS_TYPE, + TI_DFLOAT32_TYPE, TI_DFLOAT64_TYPE, TI_DFLOAT128_TYPE, diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index bf130d1824b..8cb951095c4 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -80,6 +80,7 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "cfgloop.h" #include "builtins.h" +#include "tree-chkp.h" #include "rtl.h" /* FIXME: For asm_str_count. */ @@ -143,7 +144,8 @@ eni_weights eni_time_weights; /* Prototypes. */ -static tree declare_return_variable (copy_body_data *, tree, tree, basic_block); +static tree declare_return_variable (copy_body_data *, tree, tree, tree, + basic_block); static void remap_block (tree *, copy_body_data *); static void copy_bind_expr (tree *, int *, copy_body_data *); static void declare_inline_vars (tree, tree); @@ -152,8 +154,9 @@ static void prepend_lexical_block (tree current_block, tree new_block); static tree copy_decl_to_var (tree, copy_body_data *); static tree copy_result_decl_to_var (tree, copy_body_data *); static tree copy_decl_maybe_to_var (tree, copy_body_data *); -static gimple remap_gimple_stmt (gimple, copy_body_data *); +static gimple_seq remap_gimple_stmt (gimple, copy_body_data *); static bool delete_unreachable_blocks_update_callgraph (copy_body_data *id); +static void insert_init_stmt (copy_body_data *, basic_block, gimple); /* Insert a tree->tree mapping for ID. Despite the name suggests that the trees should be variables, it is used for more than that. */ @@ -793,8 +796,8 @@ remap_gimple_seq (gimple_seq body, copy_body_data *id) for (si = gsi_start (body); !gsi_end_p (si); gsi_next (&si)) { - gimple new_stmt = remap_gimple_stmt (gsi_stmt (si), id); - gimple_seq_add_stmt (&new_body, new_stmt); + gimple_seq new_stmts = remap_gimple_stmt (gsi_stmt (si), id); + gimple_seq_add_seq (&new_body, new_stmts); } return new_body; @@ -1296,12 +1299,13 @@ remap_eh_region_tree_nr (tree old_t_nr, copy_body_data *id) /* Helper for copy_bb. Remap statement STMT using the inlining information in ID. Return the new statement copy. */ -static gimple +static gimple_seq remap_gimple_stmt (gimple stmt, copy_body_data *id) { gimple copy = NULL; struct walk_stmt_info wi; bool skip_first = false; + gimple_seq stmts = NULL; /* Begin by recognizing trees that we'll completely rewrite for the inlining context. Our output for these trees is completely @@ -1316,6 +1320,17 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) if (gimple_code (stmt) == GIMPLE_RETURN && id->transform_return_to_modify) { tree retval = gimple_return_retval (stmt); + tree retbnd = gimple_return_retbnd (stmt); + tree bndslot = id->retbnd; + + if (retbnd && bndslot) + { + gimple bndcopy = gimple_build_assign (bndslot, retbnd); + memset (&wi, 0, sizeof (wi)); + wi.info = id; + walk_gimple_op (bndcopy, remap_gimple_op_r, &wi); + gimple_seq_add_stmt (&stmts, bndcopy); + } /* If we're returning something, just turn that into an assignment into the equivalent of the original RESULT_DECL. @@ -1333,9 +1348,18 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) retval); /* id->retvar is already substituted. Skip it on later remapping. */ skip_first = true; + + /* We need to copy bounds if return structure with pointers into + instrumented function. */ + if (chkp_function_instrumented_p (id->dst_fn) + && !bndslot + && !BOUNDED_P (id->retvar) + && chkp_type_has_pointer (TREE_TYPE (id->retvar))) + id->assign_stmts.safe_push (copy); + } else - return gimple_build_nop (); + return stmts; } else if (gimple_has_substatements (stmt)) { @@ -1499,7 +1523,7 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) value = *n; STRIP_TYPE_NOPS (value); if (TREE_CONSTANT (value) || TREE_READONLY (value)) - return gimple_build_nop (); + return NULL; } } @@ -1516,7 +1540,7 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) if (gimple_bb (def_stmt) && !bitmap_bit_p (id->blocks_to_copy, gimple_bb (def_stmt)->index)) - return gimple_build_nop (); + return NULL; } } @@ -1526,7 +1550,8 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) gimple_debug_bind_get_value (stmt), stmt); id->debug_stmts.safe_push (copy); - return copy; + gimple_seq_add_stmt (&stmts, copy); + return stmts; } if (gimple_debug_source_bind_p (stmt)) { @@ -1534,7 +1559,8 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) (gimple_debug_source_bind_get_var (stmt), gimple_debug_source_bind_get_value (stmt), stmt); id->debug_stmts.safe_push (copy); - return copy; + gimple_seq_add_stmt (&stmts, copy); + return stmts; } /* Create a new deep copy of the statement. */ @@ -1613,7 +1639,10 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) } if (gimple_debug_bind_p (copy) || gimple_debug_source_bind_p (copy)) - return copy; + { + gimple_seq_add_stmt (&stmts, copy); + return stmts; + } /* Remap all the operands in COPY. */ memset (&wi, 0, sizeof (wi)); @@ -1631,7 +1660,8 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) gimple_set_vuse (copy, NULL_TREE); } - return copy; + gimple_seq_add_stmt (&stmts, copy); + return stmts; } @@ -1672,36 +1702,59 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { + gimple_seq stmts; gimple stmt = gsi_stmt (gsi); gimple orig_stmt = stmt; + gimple_stmt_iterator stmts_gsi; + bool stmt_added = false; id->regimplify = false; - stmt = remap_gimple_stmt (stmt, id); - if (gimple_nop_p (stmt)) + stmts = remap_gimple_stmt (stmt, id); + + if (gimple_seq_empty_p (stmts)) continue; - gimple_duplicate_stmt_histograms (cfun, stmt, id->src_cfun, orig_stmt); seq_gsi = copy_gsi; - /* With return slot optimization we can end up with - non-gimple (foo *)&this->m, fix that here. */ - if (is_gimple_assign (stmt) - && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt)) - && !is_gimple_val (gimple_assign_rhs1 (stmt))) + for (stmts_gsi = gsi_start (stmts); + !gsi_end_p (stmts_gsi); ) { - tree new_rhs; - new_rhs = force_gimple_operand_gsi (&seq_gsi, - gimple_assign_rhs1 (stmt), - true, NULL, false, - GSI_CONTINUE_LINKING); - gimple_assign_set_rhs1 (stmt, new_rhs); - id->regimplify = false; - } + stmt = gsi_stmt (stmts_gsi); - gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT); + /* Advance iterator now before stmt is moved to seq_gsi. */ + gsi_next (&stmts_gsi); - if (id->regimplify) - gimple_regimplify_operands (stmt, &seq_gsi); + if (gimple_nop_p (stmt)) + continue; + + gimple_duplicate_stmt_histograms (cfun, stmt, id->src_cfun, + orig_stmt); + + /* With return slot optimization we can end up with + non-gimple (foo *)&this->m, fix that here. */ + if (is_gimple_assign (stmt) + && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt)) + && !is_gimple_val (gimple_assign_rhs1 (stmt))) + { + tree new_rhs; + new_rhs = force_gimple_operand_gsi (&seq_gsi, + gimple_assign_rhs1 (stmt), + true, NULL, false, + GSI_CONTINUE_LINKING); + gimple_assign_set_rhs1 (stmt, new_rhs); + id->regimplify = false; + } + + gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT); + + if (id->regimplify) + gimple_regimplify_operands (stmt, &seq_gsi); + + stmt_added = true; + } + + if (!stmt_added) + continue; /* If copy_basic_block has been empty at the start of this iteration, call gsi_start_bb again to get at the newly added statements. */ @@ -1728,13 +1781,29 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, gimple new_call; vec<tree> argarray; size_t nargs = gimple_call_num_args (id->gimple_call); - size_t n; + size_t n, i, nargs_to_copy; + bool remove_bounds = false; for (p = DECL_ARGUMENTS (id->src_fn); p; p = DECL_CHAIN (p)) nargs--; + /* Bounds should be removed from arg pack in case + we handle not instrumented call in instrumented + function. */ + nargs_to_copy = nargs; + if (gimple_call_with_bounds_p (id->gimple_call) + && !gimple_call_with_bounds_p (stmt)) + { + for (i = gimple_call_num_args (id->gimple_call) - nargs; + i < gimple_call_num_args (id->gimple_call); + i++) + if (POINTER_BOUNDS_P (gimple_call_arg (id->gimple_call, i))) + nargs_to_copy--; + remove_bounds = true; + } + /* Create the new array of arguments. */ - n = nargs + gimple_call_num_args (stmt); + n = nargs_to_copy + gimple_call_num_args (stmt); argarray.create (n); argarray.safe_grow_cleared (n); @@ -1743,11 +1812,26 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, gimple_call_arg_ptr (stmt, 0), gimple_call_num_args (stmt) * sizeof (tree)); - /* Append the arguments passed in '...' */ - memcpy (argarray.address () + gimple_call_num_args (stmt), - gimple_call_arg_ptr (id->gimple_call, 0) - + (gimple_call_num_args (id->gimple_call) - nargs), - nargs * sizeof (tree)); + if (remove_bounds) + { + /* Append the rest of arguments removing bounds. */ + unsigned cur = gimple_call_num_args (stmt); + i = gimple_call_num_args (id->gimple_call) - nargs; + for (i = gimple_call_num_args (id->gimple_call) - nargs; + i < gimple_call_num_args (id->gimple_call); + i++) + if (!POINTER_BOUNDS_P (gimple_call_arg (id->gimple_call, i))) + argarray[cur++] = gimple_call_arg (id->gimple_call, i); + gcc_assert (cur == n); + } + else + { + /* Append the arguments passed in '...' */ + memcpy (argarray.address () + gimple_call_num_args (stmt), + gimple_call_arg_ptr (id->gimple_call, 0) + + (gimple_call_num_args (id->gimple_call) - nargs), + nargs * sizeof (tree)); + } new_call = gimple_build_call_vec (gimple_call_fn (stmt), argarray); @@ -1773,13 +1857,20 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, { /* __builtin_va_arg_pack_len () should be replaced by the number of anonymous arguments. */ - size_t nargs = gimple_call_num_args (id->gimple_call); + size_t nargs = gimple_call_num_args (id->gimple_call), i; tree count, p; gimple new_stmt; for (p = DECL_ARGUMENTS (id->src_fn); p; p = DECL_CHAIN (p)) nargs--; + /* For instrumented calls we should ignore bounds. */ + for (i = gimple_call_num_args (id->gimple_call) - nargs; + i < gimple_call_num_args (id->gimple_call); + i++) + if (POINTER_BOUNDS_P (gimple_call_arg (id->gimple_call, i))) + nargs--; + count = build_int_cst (integer_type_node, nargs); new_stmt = gimple_build_assign (gimple_call_lhs (stmt), count); gsi_replace (©_gsi, new_stmt, false); @@ -3128,12 +3219,14 @@ initialize_inlined_parameters (copy_body_data *id, gimple stmt, is set only for CALL_EXPR_RETURN_SLOT_OPT. MODIFY_DEST, if non-null, was the LHS of the MODIFY_EXPR to which this call is the RHS. + RETURN_BOUNDS holds a destination for returned bounds. + The return value is a (possibly null) value that holds the result as seen by the caller. */ static tree declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest, - basic_block entry_bb) + tree return_bounds, basic_block entry_bb) { tree callee = id->src_fn; tree result = DECL_RESULT (callee); @@ -3313,6 +3406,19 @@ declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest, /* Remember this so we can ignore it in remap_decls. */ id->retvar = var; + /* If returned bounds are used, then make var for them. */ + if (return_bounds) + { + tree bndtemp = create_tmp_var (pointer_bounds_type_node, "retbnd"); + DECL_SEEN_IN_BIND_EXPR_P (bndtemp) = 1; + TREE_NO_WARNING (bndtemp) = 1; + declare_inline_vars (id->block, bndtemp); + + id->retbnd = bndtemp; + insert_init_stmt (id, entry_bb, + gimple_build_assign (bndtemp, chkp_get_zero_bounds_var ())); + } + return use; } @@ -4144,6 +4250,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) hash_map<tree, tree> *st = NULL; tree return_slot; tree modify_dest; + tree return_bounds = NULL; location_t saved_location; struct cgraph_edge *cg_edge; cgraph_inline_failed_t reason; @@ -4152,6 +4259,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) gimple_stmt_iterator gsi, stmt_gsi; bool successfully_inlined = FALSE; bool purge_dead_abnormal_edges; + unsigned int i; /* Set input_location here so we get the right instantiation context if we call instantiate_decl from inlinable_function_p. */ @@ -4240,6 +4348,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) /* We will be inlining this callee. */ id->eh_lp_nr = lookup_stmt_eh_lp (stmt); + id->assign_stmts.create (0); /* Update the callers EH personality. */ if (DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl)) @@ -4361,6 +4470,24 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) { modify_dest = gimple_call_lhs (stmt); + /* Remember where to copy returned bounds. */ + if (gimple_call_with_bounds_p (stmt) + && TREE_CODE (modify_dest) == SSA_NAME) + { + gimple retbnd = chkp_retbnd_call_by_val (modify_dest); + if (retbnd) + { + return_bounds = gimple_call_lhs (retbnd); + /* If returned bounds are not used then just + remove unused call. */ + if (!return_bounds) + { + gimple_stmt_iterator iter = gsi_for_stmt (retbnd); + gsi_remove (&iter, true); + } + } + } + /* The function which we are inlining might not return a value, in which case we should issue a warning that the function does not return a value. In that case the optimizers will @@ -4391,7 +4518,8 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) } /* Declare the return variable for the function. */ - use_retvar = declare_return_variable (id, return_slot, modify_dest, bb); + use_retvar = declare_return_variable (id, return_slot, modify_dest, + return_bounds, bb); /* Add local vars in this inlined callee to caller. */ add_local_variables (id->src_cfun, cfun, id); @@ -4443,6 +4571,12 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) stmt = gimple_build_assign (gimple_call_lhs (stmt), use_retvar); gsi_replace (&stmt_gsi, stmt, false); maybe_clean_or_replace_eh_stmt (old_stmt, stmt); + + /* Copy bounds if we copy structure with bounds. */ + if (chkp_function_instrumented_p (id->dst_fn) + && !BOUNDED_P (use_retvar) + && chkp_type_has_pointer (TREE_TYPE (use_retvar))) + id->assign_stmts.safe_push (stmt); } else { @@ -4474,6 +4608,20 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) gsi_remove (&stmt_gsi, true); } + /* Put returned bounds into the correct place if required. */ + if (return_bounds) + { + gimple old_stmt = SSA_NAME_DEF_STMT (return_bounds); + gimple new_stmt = gimple_build_assign (return_bounds, id->retbnd); + gimple_stmt_iterator bnd_gsi = gsi_for_stmt (old_stmt); + unlink_stmt_vdef (old_stmt); + gsi_replace (&bnd_gsi, new_stmt, false); + maybe_clean_or_replace_eh_stmt (old_stmt, new_stmt); + cgraph_update_edges_for_call_stmt (old_stmt, + gimple_call_fndecl (old_stmt), + new_stmt); + } + if (purge_dead_abnormal_edges) { gimple_purge_dead_eh_edges (return_block); @@ -4490,6 +4638,11 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) TREE_USED (gimple_assign_rhs1 (stmt)) = 1; } + /* Copy bounds for all generated assigns that need it. */ + for (i = 0; i < id->assign_stmts.length (); i++) + chkp_copy_bounds_for_assign (id->assign_stmts[i], cg_edge); + id->assign_stmts.release (); + /* Output the inlining info for this abstract function, since it has been inlined. If we don't do this now, we can lose the information about the variables in the function when the blocks get blown away as soon as we diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 53059da57b3..9eb75c7fc96 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -63,6 +63,12 @@ struct copy_body_data /* The VAR_DECL for the return value. */ tree retvar; + /* The VAR_DECL for the return bounds. */ + tree retbnd; + + /* Assign statements that need bounds copy. */ + vec<gimple> assign_stmts; + /* The map from local declarations in the inlined function to equivalents in the function into which it is being inlined. */ hash_map<tree, tree> *decl_map; diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 3db1a08b0dd..a3efdd84b08 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -332,6 +332,10 @@ extern void register_pass (register_pass_info *); extern void register_pass (opt_pass* pass, pass_positioning_ops pos, const char* ref_pass_name, int ref_pass_inst_number); +extern simple_ipa_opt_pass *make_pass_ipa_chkp_versioning (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_chkp_produce_thunks (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_chkp (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_chkp_opt (gcc::context *ctxt); extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt); extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt); @@ -451,7 +455,9 @@ extern simple_ipa_opt_pass extern simple_ipa_opt_pass *make_pass_ipa_tree_profile (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_auto_profile (gcc::context *ctxt); -extern simple_ipa_opt_pass *make_pass_early_local_passes (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_build_ssa_passes (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_chkp_instrumentation_passes (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_local_optimization_passes (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_whole_program_visibility (gcc::context *ctxt); diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 2211309ebb7..b8abd144f59 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -894,6 +894,7 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, break; case VOID_TYPE: + case POINTER_BOUNDS_TYPE: case INTEGER_TYPE: case REAL_TYPE: case FIXED_POINT_TYPE: diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c index 7fc52208362..52d85036e1f 100644 --- a/gcc/tree-ssa-ccp.c +++ b/gcc/tree-ssa-ccp.c @@ -164,6 +164,7 @@ along with GCC; see the file COPYING3. If not see #include "params.h" #include "wide-int-print.h" #include "builtins.h" +#include "tree-chkp.h" /* Possible lattice values. */ @@ -1945,6 +1946,8 @@ insert_clobber_before_stack_restore (tree saved_val, tree var, else if (gimple_assign_ssa_name_copy_p (stmt)) insert_clobber_before_stack_restore (gimple_assign_lhs (stmt), var, visited); + else if (chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDRET)) + continue; else gcc_assert (is_gimple_debug (stmt)); } diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index 68655b84d1f..923a0341fad 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -84,6 +84,7 @@ along with GCC; see the file COPYING3. If not see #include "flags.h" #include "cfgloop.h" #include "tree-scalar-evolution.h" +#include "tree-chkp.h" static struct stmt_stats { @@ -792,7 +793,21 @@ propagate_necessity (bool aggressive) && (DECL_FUNCTION_CODE (def_callee) == BUILT_IN_ALIGNED_ALLOC || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_MALLOC || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_CALLOC)) - continue; + { + gimple bounds_def_stmt; + tree bounds; + + /* For instrumented calls we should also check used + bounds are returned by the same allocation call. */ + if (!gimple_call_with_bounds_p (stmt) + || ((bounds = gimple_call_arg (stmt, 1)) + && TREE_CODE (bounds) == SSA_NAME + && (bounds_def_stmt = SSA_NAME_DEF_STMT (bounds)) + && chkp_gimple_call_builtin_p (bounds_def_stmt, + BUILT_IN_CHKP_BNDRET) + && gimple_call_arg (bounds_def_stmt, 0) == ptr)) + continue; + } } FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) @@ -1219,6 +1234,23 @@ eliminate_unnecessary_stmts (void) && !gimple_plf (def_stmt, STMT_NECESSARY)) gimple_set_plf (stmt, STMT_NECESSARY, false); } + /* We did not propagate necessity for free calls fed + by allocation function to allow unnecessary + alloc-free sequence elimination. For instrumented + calls it also means we did not mark bounds producer + as necessary and it is time to do it in case free + call is not removed. */ + if (gimple_call_with_bounds_p (stmt)) + { + gimple bounds_def_stmt; + tree bounds = gimple_call_arg (stmt, 1); + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + bounds_def_stmt = SSA_NAME_DEF_STMT (bounds); + if (bounds_def_stmt + && !gimple_plf (bounds_def_stmt, STMT_NECESSARY)) + gimple_set_plf (bounds_def_stmt, STMT_NECESSARY, + gimple_plf (stmt, STMT_NECESSARY)); + } } /* If GSI is not necessary then remove it. */ @@ -1249,7 +1281,9 @@ eliminate_unnecessary_stmts (void) && DECL_FUNCTION_CODE (call) != BUILT_IN_CALLOC && DECL_FUNCTION_CODE (call) != BUILT_IN_ALLOCA && (DECL_FUNCTION_CODE (call) - != BUILT_IN_ALLOCA_WITH_ALIGN)))) + != BUILT_IN_ALLOCA_WITH_ALIGN))) + /* Avoid doing so for bndret calls for the same reason. */ + && !chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDRET)) { something_changed = true; if (dump_file && (dump_flags & TDF_DETAILS)) diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 736fbf87c3a..776dacf206b 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -2517,6 +2517,8 @@ create_component_ref_by_pieces_1 (basic_block block, vn_reference_t ref, (TREE_CODE (fn) == FUNCTION_DECL ? build_fold_addr_expr (fn) : fn), nargs, args); + if (currop->with_bounds) + CALL_WITH_BOUNDS_P (folded) = true; free (args); if (sc) CALL_EXPR_STATIC_CHAIN (folded) = sc; diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c index 7c9bf6d3ee5..6968df6c273 100644 --- a/gcc/tree-ssa-sccvn.c +++ b/gcc/tree-ssa-sccvn.c @@ -1160,6 +1160,8 @@ copy_reference_ops_from_call (gimple call, if (stmt_could_throw_p (call) && (lr = lookup_stmt_eh_lp (call)) > 0) temp.op2 = size_int (lr); temp.off = -1; + if (gimple_call_with_bounds_p (call)) + temp.with_bounds = 1; result->safe_push (temp); /* Copy the call arguments. As they can be references as well, diff --git a/gcc/tree-ssa-sccvn.h b/gcc/tree-ssa-sccvn.h index ad996043faa..3b93d320f7c 100644 --- a/gcc/tree-ssa-sccvn.h +++ b/gcc/tree-ssa-sccvn.h @@ -80,7 +80,9 @@ typedef const struct vn_phi_s *const_vn_phi_t; typedef struct vn_reference_op_struct { - enum tree_code opcode; + ENUM_BITFIELD(tree_code) opcode : 16; + /* 1 for instrumented calls. */ + unsigned with_bounds : 1; /* Constant offset this op adds or -1 if it is variable. */ HOST_WIDE_INT off; tree type; diff --git a/gcc/tree.c b/gcc/tree.c index 6e5c375dc7b..c7c4c418461 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -1339,7 +1339,8 @@ wide_int_to_tree (tree type, const wide_int_ref &pcst) case POINTER_TYPE: case REFERENCE_TYPE: - /* Cache NULL pointer. */ + case POINTER_BOUNDS_TYPE: + /* Cache NULL pointer and zero bounds. */ if (hwi == 0) { limit = 1; @@ -3413,6 +3414,7 @@ type_contains_placeholder_1 (const_tree type) switch (TREE_CODE (type)) { case VOID_TYPE: + case POINTER_BOUNDS_TYPE: case COMPLEX_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE: @@ -9729,6 +9731,8 @@ build_common_tree_nodes (bool signed_char, bool short_double) void_type_node = make_node (VOID_TYPE); layout_type (void_type_node); + pointer_bounds_type_node = targetm.chkp_bound_type (); + /* We are not going to have real types in C with less than byte alignment, so we might as well not have any types that claim to have it. */ TYPE_ALIGN (void_type_node) = BITS_PER_UNIT; diff --git a/gcc/tree.def b/gcc/tree.def index ff6be21549f..91359a23876 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -31,7 +31,11 @@ along with GCC; see the file COPYING3. If not see These tree codes have been sorted so that the macros in tree.h that check for various tree codes are optimized into range checks. This gives a measurable performance improvement. When adding a new - code, consider its placement in relation to the other codes. */ + code, consider its placement in relation to the other codes. + + When adding a new tree code which might appear as GIMPLE_ASSIGN RHS + code, proper handler in chkp_compute_bounds_for_assignment may + be required. */ /* Any erroneous construct is parsed into a node of this type. This type of node is accepted without complaint in all contexts @@ -232,6 +236,11 @@ DEFTREECODE (QUAL_UNION_TYPE, "qual_union_type", tcc_type, 0) /* The void type in C */ DEFTREECODE (VOID_TYPE, "void_type", tcc_type, 0) +/* Type to hold bounds for a pointer. + Has TYPE_PRECISION component to specify number of bits used + by this type. */ +DEFTREECODE (POINTER_BOUNDS_TYPE, "pointer_bounds_type", tcc_type, 0) + /* Type of functions. Special fields: TREE_TYPE type of value returned. TYPE_ARG_TYPES list of types of arguments expected. diff --git a/gcc/tree.h b/gcc/tree.h index 1b661caacdc..d9fe0c288e1 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -560,6 +560,21 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, /* Nonzero if this type is a complete type. */ #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE) +/* Nonzero if this type is a pointer bounds type. */ +#define POINTER_BOUNDS_TYPE_P(NODE) \ + (TREE_CODE (NODE) == POINTER_BOUNDS_TYPE) + +/* Nonzero if this node has a pointer bounds type. */ +#define POINTER_BOUNDS_P(NODE) \ + (POINTER_BOUNDS_TYPE_P (TREE_TYPE (NODE))) + +/* Nonzero if this type supposes bounds existence. */ +#define BOUNDED_TYPE_P(type) (POINTER_TYPE_P (type)) + +/* Nonzero for objects with bounded type. */ +#define BOUNDED_P(node) \ + BOUNDED_TYPE_P (TREE_TYPE (node)) + /* Nonzero if this type is the (possibly qualified) void type. */ #define VOID_TYPE_P(NODE) (TREE_CODE (NODE) == VOID_TYPE) @@ -836,6 +851,9 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, #define CALL_ALLOCA_FOR_VAR_P(NODE) \ (CALL_EXPR_CHECK (NODE)->base.protected_flag) +/* In a CALL_EXPR, means call was instrumented by Pointer Bounds Checker. */ +#define CALL_WITH_BOUNDS_P(NODE) (CALL_EXPR_CHECK (NODE)->base.deprecated_flag) + /* In a type, nonzero means that all objects of the type are guaranteed by the language or front-end to be properly aligned, so we can indicate that a MEM of this type is aligned at least to the alignment of the type, even if it @@ -3284,6 +3302,8 @@ tree_operand_check_code (const_tree __t, enum tree_code __code, int __i, #define complex_double_type_node global_trees[TI_COMPLEX_DOUBLE_TYPE] #define complex_long_double_type_node global_trees[TI_COMPLEX_LONG_DOUBLE_TYPE] +#define pointer_bounds_type_node global_trees[TI_POINTER_BOUNDS_TYPE] + #define void_type_node global_trees[TI_VOID_TYPE] /* The C type `void *'. */ #define ptr_type_node global_trees[TI_PTR_TYPE] diff --git a/gcc/value-prof.c b/gcc/value-prof.c index 0e4ec8aefc0..1b203e8aef1 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see #include "builtins.h" #include "tree-nested.h" #include "params.h" +#include "tree-chkp.h" /* In this file value profile based optimizations are placed. Currently the following optimizations are implemented (for more detailed descriptions @@ -1382,7 +1383,7 @@ gimple gimple_ic (gimple icall_stmt, struct cgraph_node *direct_call, int prob, gcov_type count, gcov_type all) { - gimple dcall_stmt, load_stmt, cond_stmt; + gimple dcall_stmt, load_stmt, cond_stmt, iretbnd_stmt = NULL; tree tmp0, tmp1, tmp; basic_block cond_bb, dcall_bb, icall_bb, join_bb = NULL; tree optype = build_pointer_type (void_type_node); @@ -1396,6 +1397,9 @@ gimple_ic (gimple icall_stmt, struct cgraph_node *direct_call, cond_bb = gimple_bb (icall_stmt); gsi = gsi_for_stmt (icall_stmt); + if (gimple_call_with_bounds_p (icall_stmt) && gimple_call_lhs (icall_stmt)) + iretbnd_stmt = chkp_retbnd_call_by_val (gimple_call_lhs (icall_stmt)); + tmp0 = make_temp_ssa_name (optype, NULL, "PROF"); tmp1 = make_temp_ssa_name (optype, NULL, "PROF"); tmp = unshare_expr (gimple_call_fn (icall_stmt)); @@ -1488,6 +1492,50 @@ gimple_ic (gimple icall_stmt, struct cgraph_node *direct_call, gimple_call_set_lhs (dcall_stmt, duplicate_ssa_name (result, dcall_stmt)); add_phi_arg (phi, gimple_call_lhs (dcall_stmt), e_dj, UNKNOWN_LOCATION); + + /* If indirect call has following BUILT_IN_CHKP_BNDRET + call then we need to make it's copy for the direct + call. */ + if (iretbnd_stmt) + { + if (gimple_call_lhs (iretbnd_stmt)) + { + gimple copy; + + gimple_set_vdef (iretbnd_stmt, NULL_TREE); + gimple_set_vuse (iretbnd_stmt, NULL_TREE); + update_stmt (iretbnd_stmt); + + result = gimple_call_lhs (iretbnd_stmt); + phi = create_phi_node (result, join_bb); + + copy = gimple_copy (iretbnd_stmt); + gimple_call_set_arg (copy, 0, + gimple_call_lhs (dcall_stmt)); + gimple_call_set_lhs (copy, duplicate_ssa_name (result, copy)); + gsi_insert_on_edge (e_dj, copy); + add_phi_arg (phi, gimple_call_lhs (copy), + e_dj, UNKNOWN_LOCATION); + + gimple_call_set_arg (iretbnd_stmt, 0, + gimple_call_lhs (icall_stmt)); + gimple_call_set_lhs (iretbnd_stmt, + duplicate_ssa_name (result, iretbnd_stmt)); + psi = gsi_for_stmt (iretbnd_stmt); + gsi_remove (&psi, false); + gsi_insert_on_edge (e_ij, iretbnd_stmt); + add_phi_arg (phi, gimple_call_lhs (iretbnd_stmt), + e_ij, UNKNOWN_LOCATION); + + gsi_commit_one_edge_insert (e_dj, NULL); + gsi_commit_one_edge_insert (e_ij, NULL); + } + else + { + psi = gsi_for_stmt (iretbnd_stmt); + gsi_remove (&psi, true); + } + } } /* Build an EH edge for the direct call if necessary. */ diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index fb3829925fc..302968e8625 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -9813,7 +9813,8 @@ vt_add_function_parameters (void) for (parm = DECL_ARGUMENTS (current_function_decl); parm; parm = DECL_CHAIN (parm)) - vt_add_function_parameter (parm); + if (!POINTER_BOUNDS_P (parm)) + vt_add_function_parameter (parm); if (DECL_HAS_VALUE_EXPR_P (DECL_RESULT (current_function_decl))) { diff --git a/gcc/varasm.c b/gcc/varasm.c index f83fc5174ce..40eeb5ed2c2 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "asan.h" #include "rtl-iter.h" +#include "tree-chkp.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -1243,6 +1244,30 @@ use_blocks_for_decl_p (tree decl) return targetm.use_blocks_for_decl_p (decl); } +/* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS + until we find an identifier that is not itself a transparent alias. + Modify the alias passed to it by reference (and all aliases on the + way to the ultimate target), such that they do not have to be + followed again, and return the ultimate target of the alias + chain. */ + +static inline tree +ultimate_transparent_alias_target (tree *alias) +{ + tree target = *alias; + + if (IDENTIFIER_TRANSPARENT_ALIAS (target)) + { + gcc_assert (TREE_CHAIN (target)); + target = ultimate_transparent_alias_target (&TREE_CHAIN (target)); + gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target) + && ! TREE_CHAIN (target)); + *alias = target; + } + + return target; +} + /* Create the DECL_RTL for a VAR_DECL or FUNCTION_DECL. DECL should have static storage duration. In other words, it should not be an automatic variable, including PARM_DECLs. @@ -1257,6 +1282,7 @@ make_decl_rtl (tree decl) { const char *name = 0; int reg_number; + tree id; rtx x; /* Check that we are not being given an automatic variable. */ @@ -1314,7 +1340,12 @@ make_decl_rtl (tree decl) return; } - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + id = DECL_ASSEMBLER_NAME (decl); + if (TREE_CODE (decl) == FUNCTION_DECL + && cgraph_node::get (decl) + && cgraph_node::get (decl)->instrumentation_clone) + ultimate_transparent_alias_target (&id); + name = IDENTIFIER_POINTER (id); if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl)) @@ -1748,7 +1779,10 @@ assemble_start_function (tree decl, const char *fnname) /* Make function name accessible from other files, if appropriate. */ - if (TREE_PUBLIC (decl)) + if (TREE_PUBLIC (decl) + || (cgraph_node::get (decl)->instrumentation_clone + && cgraph_node::get (decl)->instrumented_version + && TREE_PUBLIC (cgraph_node::get (decl)->instrumented_version->decl))) { notice_global_symbol (decl); @@ -2438,30 +2472,6 @@ mark_decl_referenced (tree decl) } -/* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS - until we find an identifier that is not itself a transparent alias. - Modify the alias passed to it by reference (and all aliases on the - way to the ultimate target), such that they do not have to be - followed again, and return the ultimate target of the alias - chain. */ - -static inline tree -ultimate_transparent_alias_target (tree *alias) -{ - tree target = *alias; - - if (IDENTIFIER_TRANSPARENT_ALIAS (target)) - { - gcc_assert (TREE_CHAIN (target)); - target = ultimate_transparent_alias_target (&TREE_CHAIN (target)); - gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target) - && ! TREE_CHAIN (target)); - *alias = target; - } - - return target; -} - /* Output to FILE (an assembly file) a reference to NAME. If NAME starts with a *, the rest of NAME is output verbatim. Otherwise NAME is transformed in a target-specific way (usually by the @@ -3778,6 +3788,7 @@ output_constant_pool_2 (machine_mode mode, rtx x, unsigned int align) case MODE_UFRACT: case MODE_ACCUM: case MODE_UACCUM: + case MODE_POINTER_BOUNDS: assemble_integer (x, GET_MODE_SIZE (mode), align, 1); break; @@ -4677,6 +4688,7 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) case REFERENCE_TYPE: case OFFSET_TYPE: case FIXED_POINT_TYPE: + case POINTER_BOUNDS_TYPE: case NULLPTR_TYPE: if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode, EXPAND_INITIALIZER), @@ -5510,6 +5522,8 @@ vec<alias_pair, va_gc> *alias_pairs; void do_assemble_alias (tree decl, tree target) { + tree id; + /* Emulated TLS had better not get this var. */ gcc_assert (!(!targetm.have_tls && TREE_CODE (decl) == VAR_DECL @@ -5518,12 +5532,16 @@ do_assemble_alias (tree decl, tree target) if (TREE_ASM_WRITTEN (decl)) return; + id = DECL_ASSEMBLER_NAME (decl); + ultimate_transparent_alias_target (&id); + /* We must force creation of DECL_RTL for debug info generation, even though we don't use it here. */ make_decl_rtl (decl); TREE_ASM_WRITTEN (decl) = 1; TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1; + TREE_ASM_WRITTEN (id) = 1; if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) { @@ -5534,7 +5552,7 @@ do_assemble_alias (tree decl, tree target) #ifdef ASM_OUTPUT_WEAKREF ASM_OUTPUT_WEAKREF (asm_out_file, decl, - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), + IDENTIFIER_POINTER (id), IDENTIFIER_POINTER (target)); #else if (!TARGET_SUPPORTS_WEAK) @@ -5548,9 +5566,16 @@ do_assemble_alias (tree decl, tree target) } #ifdef ASM_OUTPUT_DEF + tree orig_decl = decl; + + if (TREE_CODE (decl) == FUNCTION_DECL + && cgraph_node::get (decl)->instrumentation_clone + && cgraph_node::get (decl)->instrumented_version) + orig_decl = cgraph_node::get (decl)->instrumented_version->decl; + /* Make name accessible from other files, if appropriate. */ - if (TREE_PUBLIC (decl)) + if (TREE_PUBLIC (decl) || TREE_PUBLIC (orig_decl)) { globalize_decl (decl); maybe_assemble_visibility (decl); @@ -5560,7 +5585,7 @@ do_assemble_alias (tree decl, tree target) #if defined (ASM_OUTPUT_TYPE_DIRECTIVE) if (targetm.has_ifunc_p ()) ASM_OUTPUT_TYPE_DIRECTIVE - (asm_out_file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), + (asm_out_file, IDENTIFIER_POINTER (id), IFUNC_ASM_TYPE); else #endif @@ -5572,7 +5597,7 @@ do_assemble_alias (tree decl, tree target) ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target); # else ASM_OUTPUT_DEF (asm_out_file, - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), + IDENTIFIER_POINTER (id), IDENTIFIER_POINTER (target)); # endif #elif defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL) @@ -5580,7 +5605,7 @@ do_assemble_alias (tree decl, tree target) const char *name; tree *p, t; - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + name = IDENTIFIER_POINTER (id); # ifdef ASM_WEAKEN_DECL ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target)); # else @@ -5589,7 +5614,8 @@ do_assemble_alias (tree decl, tree target) /* Remove this function from the pending weak list so that we do not emit multiple .weak directives for it. */ for (p = &weak_decls; (t = *p) ; ) - if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t))) + if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t)) + || id == DECL_ASSEMBLER_NAME (TREE_VALUE (t))) *p = TREE_CHAIN (t); else p = &TREE_CHAIN (t); @@ -5598,8 +5624,7 @@ do_assemble_alias (tree decl, tree target) list, for the same reason. */ for (p = &weakref_targets; (t = *p) ; ) { - if (DECL_ASSEMBLER_NAME (decl) - == ultimate_transparent_alias_target (&TREE_VALUE (t))) + if (id == ultimate_transparent_alias_target (&TREE_VALUE (t))) *p = TREE_CHAIN (t); else p = &TREE_CHAIN (t); @@ -5865,6 +5890,12 @@ maybe_assemble_visibility (tree decl) { enum symbol_visibility vis = DECL_VISIBILITY (decl); + if (TREE_CODE (decl) == FUNCTION_DECL + && cgraph_node::get (decl) + && cgraph_node::get (decl)->instrumentation_clone + && cgraph_node::get (decl)->instrumented_version) + vis = DECL_VISIBILITY (cgraph_node::get (decl)->instrumented_version->decl); + if (vis != VISIBILITY_DEFAULT) { targetm.asm_out.assemble_visibility (decl, vis); @@ -6435,6 +6466,7 @@ default_unique_section (tree decl, int reloc) bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP; const char *prefix, *name, *linkonce; char *string; + tree id; switch (categorize_decl_for_section (decl, reloc)) { @@ -6484,7 +6516,9 @@ default_unique_section (tree decl, int reloc) gcc_unreachable (); } - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + id = DECL_ASSEMBLER_NAME (decl); + ultimate_transparent_alias_target (&id); + name = IDENTIFIER_POINTER (id); name = targetm.strip_name_encoding (name); /* If we're using one_only, then there needs to be a .gnu.linkonce diff --git a/gcc/varpool.c b/gcc/varpool.c index 3d1ab690dcb..5bfb0a7c867 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -221,6 +221,8 @@ varpool_node::dump (FILE *f) fprintf (f, " output"); if (used_by_single_function) fprintf (f, " used-by-single-function"); + if (need_bounds_init) + fprintf (f, " need-bounds-init"); if (TREE_READONLY (decl)) fprintf (f, " read-only"); if (ctor_useable_for_folding_p ()) @@ -390,6 +392,12 @@ ctor_for_folding (tree decl) && TREE_CODE (decl) != CONST_DECL) return error_mark_node; + /* Static constant bounds are created to be + used instead of constants and therefore + do not let folding it. */ + if (POINTER_BOUNDS_P (decl)) + return error_mark_node; + if (TREE_CODE (decl) == CONST_DECL || DECL_IN_CONSTANT_POOL (decl)) return DECL_INITIAL (decl); |