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:
Ilya Enkovich 2014-11-05 12:42:03 +00:00 committed by Ilya Enkovich
parent 433e416433
commit d5e254e19c
100 changed files with 11266 additions and 292 deletions

View File

@ -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,

View File

@ -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 \

View File

@ -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)

View File

@ -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;
}

View File

@ -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"

View File

@ -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. */

View File

@ -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

View File

@ -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.

View File

@ -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))
{

View File

@ -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)

View File

@ -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 */

View File

@ -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 {

View File

@ -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
View 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)

View File

@ -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"))

View File

@ -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)

View File

@ -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__");
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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")

View File

@ -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__");
}

View File

@ -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 ();
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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. */

View File

@ -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,

View File

@ -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)
{

View File

@ -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

View File

@ -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__)

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 \

View File

@ -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
View 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
View 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 */

View File

@ -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. */

View File

@ -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);

View File

@ -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))

View File

@ -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 ();

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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",

View File

@ -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];

View File

@ -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 */ \

View File

@ -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:

View File

@ -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 {

View File

@ -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
View 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
View 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 */

View File

@ -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)

View File

@ -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);

View File

@ -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[]=
{

View File

@ -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

View File

@ -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. */

View File

@ -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 */

View File

@ -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.

View File

@ -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" } } */

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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;
}

View 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" "" } */
}

View 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;
}

View 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;
}

View 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" "" } */
}

View File

@ -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

File diff suppressed because it is too large Load Diff

4252
gcc/tree-chkp.c Normal file

File diff suppressed because it is too large Load Diff

58
gcc/tree-chkp.h Normal file
View 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 */

View File

@ -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,

View File

@ -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 (&copy_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

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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));
}

View File

@ -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))

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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]

View File

@ -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. */

View File

@ -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)))
{

View File

@ -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

View File

@ -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);