mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-27 04:55:01 +08:00
ipa-chkp.c: New.
gcc/ 2014-11-05 Ilya Enkovich <ilya.enkovich@intel.com> * ipa-chkp.c: New. * ipa-chkp.h: New. * tree-chkp.c: New. * tree-chkp.h: New. * tree-chkp-opt.c: New. * rtl-chkp.c: New. * rtl-chkp.h: New. * Makefile.in (OBJS): Add ipa-chkp.o, rtl-chkp.o, tree-chkp.o tree-chkp-opt.o. (GTFILES): Add tree-chkp.c. * mode-classes.def (MODE_POINTER_BOUNDS): New. * tree.def (POINTER_BOUNDS_TYPE): New. * genmodes.c (complete_mode): Support MODE_POINTER_BOUNDS. (POINTER_BOUNDS_MODE): New. (make_pointer_bounds_mode): New. * machmode.h (POINTER_BOUNDS_MODE_P): New. * stor-layout.c (int_mode_for_mode): Support MODE_POINTER_BOUNDS. (layout_type): Support POINTER_BOUNDS_TYPE. * tree-pretty-print.c (dump_generic_node): Support POINTER_BOUNDS_TYPE. * tree-core.h (tree_index): Add TI_POINTER_BOUNDS_TYPE. * tree.c (build_int_cst_wide): Support POINTER_BOUNDS_TYPE. (type_contains_placeholder_1): Likewise. (build_common_tree_nodes): Initialize pointer_bounds_type_node. * tree.h (POINTER_BOUNDS_TYPE_P): New. (pointer_bounds_type_node): New. (POINTER_BOUNDS_P): New. (BOUNDED_TYPE_P): New. (BOUNDED_P): New. (CALL_WITH_BOUNDS_P): New. * gimple.h (gf_mask): Add GF_CALL_WITH_BOUNDS. (gimple_call_with_bounds_p): New. (gimple_call_set_with_bounds): New. (gimple_return_retbnd): New. (gimple_return_set_retbnd): New * gimple.c (gimple_build_return): Increase number of ops for return statement. (gimple_build_call_from_tree): Propagate CALL_WITH_BOUNDS_P flag. * gimple-pretty-print.c (dump_gimple_return): Print second op. * rtl.h (CALL_EXPR_WITH_BOUNDS_P): New. * gimplify.c (gimplify_init_constructor): Avoid infinite loop during gimplification of bounds initializer. * calls.c: Include tree-chkp.h, rtl-chkp.h, bitmap.h. (special_function_p): Use original decl name when analyzing instrumentation clone. (arg_data): Add fields special_slot, pointer_arg and pointer_offset. (store_bounds): New. (emit_call_1): Propagate instrumentation flag for CALL. (initialize_argument_information): Compute pointer_arg, pointer_offset and special_slot for pointer bounds arguments. (finalize_must_preallocate): Preallocate when storing bounds in bounds table. (compute_argument_addresses): Skip pointer bounds. (expand_call): Store bounds into tables separately. Return result joined with resulting bounds. * cfgexpand.c: Include tree-chkp.h, rtl-chkp.h. (expand_call_stmt): Propagate bounds flag for CALL_EXPR. (expand_return): Add returned bounds arg. Handle returned bounds. (expand_gimple_stmt_1): Adjust to new expand_return signature. (gimple_expand_cfg): Reset rtx bounds map. * expr.c: Include tree-chkp.h, rtl-chkp.h. (expand_assignment): Handle returned bounds. (store_expr_with_bounds): New. Replaces store_expr with new bounds target argument. Handle bounds returned by calls. (store_expr): Now wraps store_expr_with_bounds. * expr.h (store_expr_with_bounds): New. * function.c: Include tree-chkp.h, rtl-chkp.h. (bounds_parm_data): New. (use_register_for_decl): Do not registerize decls used for bounds stores and loads. (assign_parms_augmented_arg_list): Add bounds of the result structure pointer as the second argument. (assign_parm_find_entry_rtl): Mark bounds are never passed on the stack. (assign_parm_is_stack_parm): Likewise. (assign_parm_load_bounds): New. (assign_bounds): New. (assign_parms): Load bounds and determine a location for returned bounds. (diddle_return_value_1): New. (diddle_return_value): Handle returned bounds. * function.h (rtl_data): Add field for returned bounds. * varasm.c: Include tree-chkp.h. (output_constant): Support POINTER_BOUNDS_TYPE. (output_constant_pool_2): Support MODE_POINTER_BOUNDS. (ultimate_transparent_alias_target): Move up. (make_decl_rtl): For instrumented function use name of the original decl. (assemble_start_function): Mark function as global in case it is instrumentation clone of the global function. (do_assemble_alias): Follow transparent alias chain for identifier. Check if original alias is public. (maybe_assemble_visibility): Use visibility of the original function for instrumented version. (default_unique_section): Likewise. * emit-rtl.c (immed_double_const): Support MODE_POINTER_BOUNDS. (init_emit_once): Build pointer bounds zero constants. * explow.c (trunc_int_for_mode): Support MODE_POINTER_BOUNDS. * target.def (builtin_chkp_function): New. (chkp_bound_type): New. (chkp_bound_mode): New. (chkp_make_bounds_constant): New. (chkp_initialize_bounds): New. (load_bounds_for_arg): New. (store_bounds_for_arg): New. (load_returned_bounds): New. (store_returned_bounds): New. (chkp_function_value_bounds): New. (setup_incoming_vararg_bounds): New. (function_arg): Update hook description with new possible return value CONST_INT. * targhooks.h (default_load_bounds_for_arg): New. (default_store_bounds_for_arg): New. (default_load_returned_bounds): New. (default_store_returned_bounds): New. (default_chkp_bound_type): New. (default_chkp_bound_mode): New. (default_builtin_chkp_function): New. (default_chkp_function_value_bounds): New. (default_chkp_make_bounds_constant): New. (default_chkp_initialize_bounds): New. (default_setup_incoming_vararg_bounds): New. * targhooks.c (default_load_bounds_for_arg): New. (default_store_bounds_for_arg): New. (default_load_returned_bounds): New. (default_store_returned_bounds): New. (default_chkp_bound_type): New. (default_chkp_bound_mode); New. (default_builtin_chkp_function): New. (default_chkp_function_value_bounds): New. (default_chkp_make_bounds_constant): New. (default_chkp_initialize_bounds): New. (default_setup_incoming_vararg_bounds): New. * builtin-types.def (BT_BND): New. (BT_FN_PTR_CONST_PTR): New. (BT_FN_CONST_PTR_CONST_PTR): New. (BT_FN_BND_CONST_PTR): New. (BT_FN_CONST_PTR_BND): New. (BT_FN_PTR_CONST_PTR_SIZE): New. (BT_FN_PTR_CONST_PTR_CONST_PTR): New. (BT_FN_VOID_PTRPTR_CONST_PTR): New. (BT_FN_VOID_CONST_PTR_SIZE): New. (BT_FN_VOID_PTR_BND): New. (BT_FN_CONST_PTR_CONST_PTR_CONST_PTR): New. (BT_FN_BND_CONST_PTR_SIZE): New. (BT_FN_PTR_CONST_PTR_CONST_PTR_SIZE): New. (BT_FN_VOID_CONST_PTR_BND_CONST_PTR): New. * chkp-builtins.def: New. * builtins.def: include chkp-builtins.def. (DEF_CHKP_BUILTIN): New. * builtins.c: Include tree-chkp.h and rtl-chkp.h. (expand_builtin): Support BUILT_IN_CHKP_INIT_PTR_BOUNDS, BUILT_IN_CHKP_NULL_PTR_BOUNDS, BUILT_IN_CHKP_COPY_PTR_BOUNDS, BUILT_IN_CHKP_CHECK_PTR_LBOUNDS, BUILT_IN_CHKP_CHECK_PTR_UBOUNDS, BUILT_IN_CHKP_CHECK_PTR_BOUNDS, BUILT_IN_CHKP_SET_PTR_BOUNDS, BUILT_IN_CHKP_NARROW_PTR_BOUNDS, BUILT_IN_CHKP_STORE_PTR_BOUNDS, BUILT_IN_CHKP_GET_PTR_LBOUND, BUILT_IN_CHKP_GET_PTR_UBOUND, BUILT_IN_CHKP_BNDMK, BUILT_IN_CHKP_BNDSTX, BUILT_IN_CHKP_BNDCL, BUILT_IN_CHKP_BNDCU, BUILT_IN_CHKP_BNDLDX, BUILT_IN_CHKP_BNDRET, BUILT_IN_CHKP_INTERSECT, BUILT_IN_CHKP_NARROW, BUILT_IN_CHKP_EXTRACT_LOWER, BUILT_IN_CHKP_EXTRACT_UPPER. (std_expand_builtin_va_start): Init bounds for va_list. * cppbuiltin.c (define_builtin_macros_for_compilation_flags): Add __CHKP__ macro when Pointer Bounds Checker is on. * params.def (PARAM_CHKP_MAX_CTOR_SIZE): New. * passes.def (pass_ipa_chkp_versioning): New. (pass_early_local_passes): Renamed to pass_build_ssa_passes. (pass_fixup_cfg): Moved to pass_chkp_instrumentation_passes. (pass_chkp_instrumentation_passes): New. (pass_ipa_chkp_produce_thunks): New. (pass_local_optimization_passes): New. (pass_chkp_opt): New. * tree-pass.h (make_pass_ipa_chkp_versioning): New. (make_pass_ipa_chkp_produce_thunks): New. (make_pass_chkp): New. (make_pass_chkp_opt): New. (make_pass_early_local_passes): Renamed to ... (make_pass_build_ssa_passes): This. (make_pass_chkp_instrumentation_passes): New. (make_pass_local_optimization_passes): New. * passes.c (pass_manager::execute_early_local_passes): Execute early passes in three steps. (execute_all_early_local_passes): Renamed to ... (execute_build_ssa_passes): This. (pass_data_early_local_passes): Renamed to ... (pass_data_build_ssa_passes): This. (pass_early_local_passes): Renamed to ... (pass_build_ssa_passes): This. (pass_data_chkp_instrumentation_passes): New. (pass_chkp_instrumentation_passes): New. (pass_data_local_optimization_passes): New. (pass_local_optimization_passes): New. (make_pass_early_local_passes): Renamed to ... (make_pass_build_ssa_passes): This. (make_pass_chkp_instrumentation_passes): New. (make_pass_local_optimization_passes): New. * c-family/c.opt (fcheck-pointer-bounds): New. (fchkp-check-incomplete-type): New. (fchkp-zero-input-bounds-for-main): New. (fchkp-first-field-has-own-bounds): New. (fchkp-narrow-bounds): New. (fchkp-narrow-to-innermost-array): New. (fchkp-optimize): New. (fchkp-use-fast-string-functions): New. (fchkp-use-nochk-string-functions): New. (fchkp-use-static-bounds): New. (fchkp-use-static-const-bounds): New. (fchkp-treat-zero-dynamic-size-as-infinite): New. (fchkp-check-read): New. (fchkp-check-write): New. (fchkp-store-bounds): New. (fchkp-instrument-calls): New. (fchkp-instrument-marked-only): New. (Wchkp): New. * c-family/c-common.c (handle_bnd_variable_size_attribute): New. (handle_bnd_legacy): New. (handle_bnd_instrument): New. (c_common_attribute_table): Add bnd_variable_size, bnd_legacy and bnd_instrument. Fix documentation. (c_common_format_attribute_table): Likewsie. * toplev.c: include tree-chkp.h. (process_options): Check Pointer Bounds Checker is supported. (compile_file): Add chkp_finish_file call. * ipa-cp.c (initialize_node_lattices): Use cgraph_local_p to handle instrumentation clones properly. (propagate_constants_accross_call): Do not propagate through instrumentation thunks. * ipa-pure-const.c (propagate_pure_const): Support IPA_REF_CHKP. * ipa-inline.c (early_inliner): Check edge has summary allocated. * ipa-split.c: Include tree-chkp.h. (find_retbnd): New. (split_part_set_ssa_name_p): New. (consider_split): Do not split retbnd and retval producers. (insert_bndret_call_after): new. (split_function): Propagate Pointer Bounds Checker instrumentation marks and handle returned bounds. * tree-ssa-sccvn.h (vn_reference_op_struct): Transform opcode into bit field and add with_bounds field. * tree-ssa-sccvn.c (copy_reference_ops_from_call): Set with_bounds field for instrumented calls. * tree-ssa-pre.c (create_component_ref_by_pieces_1): Restore CALL_WITH_BOUNDS_P flag for calls. * tree-ssa-ccp.c: Include tree-chkp.h. (insert_clobber_before_stack_restore): Handle BUILT_IN_CHKP_BNDRET calls. * tree-ssa-dce.c: Include tree-chkp.h. (propagate_necessity): For free call fed by alloc check bounds are also provided by the same alloc. (eliminate_unnecessary_stmts): Handle BUILT_IN_CHKP_BNDRET used by free calls. * tree-inline.c: Include tree-chkp.h. (declare_return_variable): Add arg holding returned bounds slot. Create and initialize returned bounds var. (remap_gimple_stmt): Handle returned bounds. Return sequence of statements instead of a single statement. (insert_init_stmt): Add declaration. (remap_gimple_seq): Adjust to new remap_gimple_stmt signature. (copy_bb): Adjust to changed return type of remap_gimple_stmt. Properly handle bounds in va_arg_pack and va_arg_pack_len. (expand_call_inline): Handle returned bounds. Add bounds copy for generated mem to mem assignments. * tree-inline.h (copy_body_data): Add fields retbnd and assign_stmts. * value-prof.c: Include tree-chkp.h. (gimple_ic): Support returned bounds. * ipa.c (cgraph_build_static_cdtor_1): Support contructors with "chkp ctor" and "bnd_legacy" attributes. (symtab_remove_unreachable_nodes): Keep initial values for pointer bounds to be used for checks eliminations. (process_references): Handle IPA_REF_CHKP. (walk_polymorphic_call_targets): Likewise. * ipa-visibility.c (cgraph_externally_visible_p): Mark instrumented 'main' as externally visible. (function_and_variable_visibility): Filter instrumentation thunks. * cgraph.h (cgraph_thunk_info): Add add_pointer_bounds_args field. (cgraph_node): Add instrumented_version, orig_decl and instrumentation_clone fields. (symtab_node::get_alias_target): Allow IPA_REF_CHKP reference. (varpool_node): Add need_bounds_init field. (cgraph_local_p): New. * cgraph.c: Include tree-chkp.h. (cgraph_node::remove): Fix instrumented_version of the referenced node if any. (cgraph_node::dump): Dump instrumentation_clone and instrumented_version fields. (cgraph_node::verify_node): Check correctness of IPA_REF_CHKP references and instrumentation thunks. (cgraph_can_remove_if_no_direct_calls_and_refs_p): Keep all not instrumented instrumentation clones alive. (cgraph_redirect_edge_call_stmt_to_callee): Support returned bounds. * cgraphbuild.c (rebuild_cgraph_edges): Rebuild IPA_REF_CHKP reference. (cgraph_rebuild_references): Likewise. * cgraphunit.c: Include tree-chkp.h. (assemble_thunks_and_aliases): Skip thunks calling instrumneted function version. (varpool_finalize_decl): Register statically initialized decls in Pointer Bounds Checker. (walk_polymorphic_call_targets): Do not mark generated call to __builtin_unreachable as with_bounds. (output_weakrefs): If there are both instrumented and original versions, output only one of them. (cgraph_node::expand_thunk): Set with_bounds flag for created call statement. * ipa-ref.h (ipa_ref_use): Add IPA_REF_CHKP. (ipa_ref): increase size of use field. * symtab.c (ipa_ref_use_name): Add element for IPA_REF_CHKP. * varpool.c (dump_varpool_node): Dump need_bounds_init field. (ctor_for_folding): Do not fold constant bounds vars. * lto-streamer.h (LTO_minor_version): Change minor version from 0 to 1. * lto-cgraph.c (compute_ltrans_boundary): Keep initial values for pointer bounds. (lto_output_node): Output instrumentation_clone, thunk.add_pointer_bounds_args and orig_decl field. (lto_output_ref): Adjust to new ipa_ref::use field size. (input_overwrite_node): Read instrumentation_clone field. (input_node): Read thunk.add_pointer_bounds_args and orig_decl fields. (input_ref): Adjust to new ipa_ref::use field size. (input_cgraph_1): Compute instrumented_version fields and restore IDENTIFIER_TRANSPARENT_ALIAS chains. (lto_output_varpool_node): Output need_bounds_init value. (input_varpool_node): Read need_bounds_init value. * lto-partition.c (add_symbol_to_partition_1): Keep original and instrumented versions together. (privatize_symbol_name): Restore transparent alias chain if required. (add_references_to_partition): Add references to pointer bounds vars. * dbxout.c (dbxout_type): Ignore POINTER_BOUNDS_TYPE. * dwarf2out.c (gen_subprogram_die): Ignore bound args. (gen_type_die_with_usage): Skip pointer bounds. (dwarf2out_global_decl): Likewise. (is_base_type): Support POINTER_BOUNDS_TYPE. (gen_formal_types_die): Skip pointer bounds. (gen_decl_die): Likewise. * var-tracking.c (vt_add_function_parameters): Skip bounds parameters. * ipa-icf.c (sem_function::merge): Do not merge when instrumentation thunk still exists. (sem_variable::merge): Reset need_bounds_init flag. * doc/extend.texi: Document Pointer Bounds Checker built-in functions and attributes. * doc/tm.texi.in (TARGET_LOAD_BOUNDS_FOR_ARG): New. (TARGET_STORE_BOUNDS_FOR_ARG): New. (TARGET_LOAD_RETURNED_BOUNDS): New. (TARGET_STORE_RETURNED_BOUNDS): New. (TARGET_CHKP_FUNCTION_VALUE_BOUNDS): New. (TARGET_SETUP_INCOMING_VARARG_BOUNDS): New. (TARGET_BUILTIN_CHKP_FUNCTION): New. (TARGET_CHKP_BOUND_TYPE): New. (TARGET_CHKP_BOUND_MODE): New. (TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New. (TARGET_CHKP_INITIALIZE_BOUNDS): New. * doc/tm.texi: Regenerated. * doc/rtl.texi (MODE_POINTER_BOUNDS): New. (BND32mode): New. (BND64mode): New. * doc/invoke.texi (-mmpx): New. (-mno-mpx): New. (chkp-max-ctor-size): New. * config/i386/constraints.md (w): New. (Ti): New. (Tb): New. * config/i386/i386-c.c (ix86_target_macros_internal): Add __MPX__. * config/i386/i386-modes.def (BND32): New. (BND64): New. * config/i386/i386-protos.h (ix86_bnd_prefixed_insn_p): New. * config/i386/i386.c: Include tree-chkp.h, rtl-chkp.h, tree-iterator.h. (regclass_map): Add bound registers. (dbx_register_map): Likewise. (dbx64_register_map): Likewise. (svr4_dbx_register_map): Likewise. (isa_opts): Add -mmpx. (PTA_MPX): New. (ix86_option_override_internal): Support MPX ISA. (ix86_conditional_register_usage): Support bound registers. (ix86_code_end): Add MPX bnd prefix. (output_set_got): Likewise. (print_reg): Avoid prefixes for bound registers. (ix86_print_operand): Add '!' (MPX bnd) print prefix support. (ix86_print_operand_punct_valid_p): Likewise. (ix86_print_operand_address): Support UNSPEC_BNDMK_ADDR and UNSPEC_BNDLDX_ADDR. (ix86_output_call_insn): Add MPX bnd prefix to branch instructions. (ix86_class_likely_spilled_p): Add bound regs support. (ix86_hard_regno_mode_ok): Likewise. (x86_order_regs_for_local_alloc): Likewise. (ix86_bnd_prefixed_insn_p): New. (ix86_builtins): Add IX86_BUILTIN_BNDMK, IX86_BUILTIN_BNDSTX, IX86_BUILTIN_BNDLDX, IX86_BUILTIN_BNDCL, IX86_BUILTIN_BNDCU, IX86_BUILTIN_BNDRET, IX86_BUILTIN_BNDNARROW, IX86_BUILTIN_BNDINT, IX86_BUILTIN_SIZEOF, IX86_BUILTIN_BNDLOWER, IX86_BUILTIN_BNDUPPER. (builtin_isa): Add leaf_p and nothrow_p fields. (def_builtin): Initialize leaf_p and nothrow_p. (ix86_add_new_builtins): Handle leaf_p and nothrow_p flags. (bdesc_mpx): New. (bdesc_mpx_const): New. (ix86_init_mpx_builtins): New. (ix86_init_builtins): Call ix86_init_mpx_builtins. (ix86_emit_cmove): New. (ix86_emit_move_max): New. (ix86_expand_builtin): Expand IX86_BUILTIN_BNDMK, IX86_BUILTIN_BNDSTX, IX86_BUILTIN_BNDLDX, IX86_BUILTIN_BNDCL, IX86_BUILTIN_BNDCU, IX86_BUILTIN_BNDRET, IX86_BUILTIN_BNDNARROW, IX86_BUILTIN_BNDINT, IX86_BUILTIN_SIZEOF, IX86_BUILTIN_BNDLOWER, IX86_BUILTIN_BNDUPPER. (ix86_function_value_bounds): New. (ix86_builtin_mpx_function): New. (ix86_get_arg_address_for_bt): New. (ix86_load_bounds): New. (ix86_store_bounds): New. (ix86_load_returned_bounds): New. (ix86_store_returned_bounds): New. (ix86_mpx_bound_mode): New. (ix86_make_bounds_constant): New. (ix86_initialize_bounds): (TARGET_LOAD_BOUNDS_FOR_ARG): New. (TARGET_STORE_BOUNDS_FOR_ARG): New. (TARGET_LOAD_RETURNED_BOUNDS): New. (TARGET_STORE_RETURNED_BOUNDS): New. (TARGET_CHKP_BOUND_MODE): New. (TARGET_BUILTIN_CHKP_FUNCTION): New. (TARGET_CHKP_FUNCTION_VALUE_BOUNDS): New. (TARGET_CHKP_MAKE_BOUNDS_CONSTANT): New. (TARGET_CHKP_INITIALIZE_BOUNDS): New. (ix86_option_override_internal): Do not support x32 with MPX. (init_cumulative_args): Init stdarg, bnd_regno, bnds_in_bt and force_bnd_pass. (function_arg_advance_32): Return number of used integer registers. (function_arg_advance_64): Likewise. (function_arg_advance_ms_64): Likewise. (ix86_function_arg_advance): Handle pointer bounds. (ix86_function_arg): Likewise. (ix86_function_value_regno_p): Mark fisrt bounds registers as possible function value. (ix86_function_value_1): Handle pointer bounds type/mode (ix86_return_in_memory): Likewise. (ix86_print_operand): Analyse insn to decide abounf "bnd" prefix. (ix86_expand_call): Generate returned bounds. (ix86_setup_incoming_vararg_bounds): New. (ix86_va_start): Initialize bounds for pointers in va_list. (TARGET_SETUP_INCOMING_VARARG_BOUNDS): New. * config/i386/i386.h (TARGET_MPX): New. (TARGET_MPX_P): New. (FIRST_PSEUDO_REGISTER): Fix to new value. (FIXED_REGISTERS): Add bound registers. (CALL_USED_REGISTERS): Likewise. (REG_ALLOC_ORDER): Likewise. (HARD_REGNO_NREGS): Likewise. (VALID_BND_REG_MODE): New. (FIRST_BND_REG): New. (LAST_BND_REG): New. (reg_class): Add BND_REGS. (REG_CLASS_NAMES): Likewise. (REG_CLASS_CONTENTS): Likewise. (BND_REGNO_P): New. (ANY_BND_REG_P): New. (BNDmode): New. (HI_REGISTER_NAMES): Add bound registers. (ix86_args): Add bnd_regno, bnds_in_bt, force_bnd_pass and stdarg fields. * config/i386/i386.md (UNSPEC_BNDMK): New. (UNSPEC_BNDMK_ADDR): New. (UNSPEC_BNDSTX): New. (UNSPEC_BNDLDX): New. (UNSPEC_BNDLDX_ADDR): New. (UNSPEC_BNDCL): New. (UNSPEC_BNDCU): New. (UNSPEC_BNDCN): New. (UNSPEC_MPX_FENCE): New. (UNSPEC_SIZEOF): New. (BND0_REG): New. (BND1_REG): New. (type): Add mpxmov, mpxmk, mpxchk, mpxld, mpxst. (length_immediate): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst. (prefix_rep): Check for bnd prefix. (prefix_0f): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst. (length_nobnd): New. (length): Use length_nobnd when specified. (memory): Support mpxmov, mpxmk, mpxchk, mpxld, mpxst. (BND): New. (bnd_ptr): New. (BNDCHECK): New. (bndcheck): New. (*jcc_1): Add MPX bnd prefix. (*jcc_2): Likewise. (jump): Likewise. (*indirect_jump): Likewise. (*tablejump_1): Likewise. (simple_return_internal): Likewise. (simple_return_internal_long): Likewise. (simple_return_pop_internal): Likewise. (simple_return_indirect_internal): Likewise. (<mode>_mk): New. (*<mode>_mk): New. (mov<mode>): New. (*mov<mode>_internal_mpx): New. (<mode>_<bndcheck>): New. (*<mode>_<bndcheck>): New. (<mode>_ldx): New. (*<mode>_ldx): New. (<mode>_stx): New. (*<mode>_stx): New. move_size_reloc_<mode>): New. * config/i386/predicates.md (address_mpx_no_base_operand): New. (address_mpx_no_index_operand): New. (bnd_mem_operator): New. (symbol_operand): New. (x86_64_immediate_size_operand): New. * config/i386/i386.opt (mmpx): New. * config/i386/i386-builtin-types.def (BND): New. (ULONG): New. (BND_FTYPE_PCVOID_ULONG): New. (VOID_FTYPE_BND_PCVOID): New. (VOID_FTYPE_PCVOID_PCVOID_BND): New. (BND_FTYPE_PCVOID_PCVOID): New. (BND_FTYPE_PCVOID): New. (BND_FTYPE_BND_BND): New. (PVOID_FTYPE_PVOID_PVOID_ULONG): New. (PVOID_FTYPE_PCVOID_BND_ULONG): New. (ULONG_FTYPE_VOID): New. (PVOID_FTYPE_BND): New. gcc/testsuite/ 2014-11-05 Ilya Enkovich <ilya.enkovich@intel.com> * gcc.target/i386/chkp-builtins-1.c: New. * gcc.target/i386/chkp-builtins-2.c: New. * gcc.target/i386/chkp-builtins-3.c: New. * gcc.target/i386/chkp-builtins-4.c: New. * gcc.target/i386/chkp-remove-bndint-1.c: New. * gcc.target/i386/chkp-remove-bndint-2.c: New. * gcc.target/i386/chkp-const-check-1.c: New. * gcc.target/i386/chkp-const-check-2.c: New. * gcc.target/i386/chkp-lifetime-1.c: New. * gcc.dg/pr37858.c: Replace early_local_cleanups pass name with build_ssa_passes. From-SVN: r217125
This commit is contained in:
parent
433e416433
commit
d5e254e19c
541
gcc/ChangeLog
541
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,
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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
|
||||
|
236
gcc/calls.c
236
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.
|
||||
|
@ -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))
|
||||
{
|
||||
|
113
gcc/cgraph.c
113
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)
|
||||
|
30
gcc/cgraph.h
30
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 */
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
71
gcc/chkp-builtins.def
Normal file
71
gcc/chkp-builtins.def
Normal file
@ -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)
|
@ -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"))
|
||||
|
@ -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)
|
||||
|
@ -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__");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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__");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
136
gcc/doc/tm.texi
136
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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
71
gcc/expr.c
71
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. */
|
||||
|
||||
|
@ -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,
|
||||
|
268
gcc/function.c
268
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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
46
gcc/gimple.h
46
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 \
|
||||
|
@ -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;
|
||||
|
647
gcc/ipa-chkp.c
Normal file
647
gcc/ipa-chkp.c
Normal file
@ -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);
|
||||
}
|
26
gcc/ipa-chkp.h
Normal file
26
gcc/ipa-chkp.h
Normal file
@ -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 */
|
20
gcc/ipa-cp.c
20
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. */
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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 ();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
107
gcc/ipa-split.c
107
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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
54
gcc/ipa.c
54
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)
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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];
|
||||
|
@ -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 */ \
|
||||
|
@ -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:
|
||||
|
94
gcc/passes.c
94
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_early_local_passes (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_local_optimization_passes (ctxt);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -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);
|
||||
|
307
gcc/rtl-chkp.c
Normal file
307
gcc/rtl-chkp.c
Normal file
@ -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);
|
||||
}
|
38
gcc/rtl-chkp.h
Normal file
38
gcc/rtl-chkp.h
Normal file
@ -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 */
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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[]=
|
||||
{
|
||||
|
164
gcc/target.def
164
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
|
||||
@ -3843,6 +3944,54 @@ not generate any instructions in this case.",
|
||||
int *pretend_args_size, int second_time),
|
||||
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\
|
||||
@ -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
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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" } } */
|
||||
|
9
gcc/testsuite/gcc.target/i386/chkp-builtins-1.c
Normal file
9
gcc/testsuite/gcc.target/i386/chkp-builtins-1.c
Normal file
@ -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);
|
||||
}
|
9
gcc/testsuite/gcc.target/i386/chkp-builtins-2.c
Normal file
9
gcc/testsuite/gcc.target/i386/chkp-builtins-2.c
Normal file
@ -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);
|
||||
}
|
9
gcc/testsuite/gcc.target/i386/chkp-builtins-3.c
Normal file
9
gcc/testsuite/gcc.target/i386/chkp-builtins-3.c
Normal file
@ -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);
|
||||
}
|
9
gcc/testsuite/gcc.target/i386/chkp-builtins-4.c
Normal file
9
gcc/testsuite/gcc.target/i386/chkp-builtins-4.c
Normal file
@ -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);
|
||||
}
|
11
gcc/testsuite/gcc.target/i386/chkp-const-check-1.c
Normal file
11
gcc/testsuite/gcc.target/i386/chkp-const-check-1.c
Normal file
@ -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;
|
||||
}
|
8
gcc/testsuite/gcc.target/i386/chkp-const-check-2.c
Normal file
8
gcc/testsuite/gcc.target/i386/chkp-const-check-2.c
Normal file
@ -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" "" } */
|
||||
}
|
15
gcc/testsuite/gcc.target/i386/chkp-lifetime-1.c
Normal file
15
gcc/testsuite/gcc.target/i386/chkp-lifetime-1.c
Normal file
@ -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;
|
||||
}
|
17
gcc/testsuite/gcc.target/i386/chkp-remove-bndint-1.c
Normal file
17
gcc/testsuite/gcc.target/i386/chkp-remove-bndint-1.c
Normal file
@ -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;
|
||||
}
|
17
gcc/testsuite/gcc.target/i386/chkp-remove-bndint-2.c
Normal file
17
gcc/testsuite/gcc.target/i386/chkp-remove-bndint-2.c
Normal file
@ -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" "" } */
|
||||
}
|
10
gcc/toplev.c
10
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
|
||||
|
1100
gcc/tree-chkp-opt.c
Normal file
1100
gcc/tree-chkp-opt.c
Normal file
File diff suppressed because it is too large
Load Diff
4252
gcc/tree-chkp.c
Normal file
4252
gcc/tree-chkp.c
Normal file
File diff suppressed because it is too large
Load Diff
58
gcc/tree-chkp.h
Normal file
58
gcc/tree-chkp.h
Normal file
@ -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 */
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
/* Advance iterator now before stmt is moved to seq_gsi. */
|
||||
gsi_next (&stmts_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;
|
||||
}
|
||||
|
||||
gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT);
|
||||
|
||||
if (id->regimplify)
|
||||
gimple_regimplify_operands (stmt, &seq_gsi);
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
11
gcc/tree.def
11
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.
|
||||
|
20
gcc/tree.h
20
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]
|
||||
|
@ -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. */
|
||||
|
@ -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)))
|
||||
{
|
||||
|
104
gcc/varasm.c
104
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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user