Hi, here is variant of patch that drops the field walking from gimple_extract_devirt_binfo_from_cst completely. As pointed out by Jason, it is pointless since all structures have BINFO in C++ and thus get_binfo_at_offset will do the job.
I would like to return the code back eventually to handle arrays&unions but that can be done incrementally (and this is not the only place that sufers from the problem) Martin: I am still not quite certain about the dynamic type changing logic. if this is the case ipa-prop needs to deal with and it handles only 0 offsets within the outer type, I guess it can just test the offset by itself? Honza Bootstrapped/regtested x86_64-linux, OK? * ipa-cp.c (ipa_get_indirect_edge_target_1): Update use of gimple_extract_devirt_binfo_from_cst. * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): Rework. (gimple_fold_call): Update use of gimple_extract_devirt_binfo_from_cst. * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. * gimple.h (gimple_extract_devirt_binfo_from_cst): Update. Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 201814) --- ipa-cp.c (working copy) *************** ipa_get_indirect_edge_target_1 (struct c *** 1541,1554 **** if (TREE_CODE (t) != TREE_BINFO) { tree binfo; binfo = gimple_extract_devirt_binfo_from_cst ! (t, ie->indirect_info->otr_type); if (!binfo) return NULL_TREE; ! binfo = get_binfo_at_offset (binfo, anc_offset, otr_type); ! if (!binfo) return NULL_TREE; ! return gimple_get_virt_method_for_binfo (token, binfo); } else { --- 1541,1564 ---- if (TREE_CODE (t) != TREE_BINFO) { tree binfo; + tree target, base_target; binfo = gimple_extract_devirt_binfo_from_cst ! (t, ie->indirect_info->otr_type, ! ie->indirect_info->offset); if (!binfo) return NULL_TREE; ! target = gimple_get_virt_method_for_binfo (token, binfo); ! if (!target) ! return NULL_TREE; ! /* Constructors may be partially inlined. We do not track ! if type is in construction and during that time the ! virtual table may correspond to virtual table of the ! base type. */ ! base_target = gimple_get_virt_method_for_binfo ! (token, TYPE_BINFO (ie->indirect_info->otr_type)); ! if (base_target != target) return NULL_TREE; ! return target; } else { Index: gimple-fold.c =================================================================== *** gimple-fold.c (revision 201814) --- gimple-fold.c (working copy) *************** gimple_fold_builtin (gimple stmt) *** 1006,1021 **** /* Return a binfo to be used for devirtualization of calls based on an object represented by a declaration (i.e. a global or automatically allocated one) or NULL if it cannot be found or is not safe. CST is expected to be an ! ADDR_EXPR of such object or the function will return NULL. Currently it is ! safe to use such binfo only if it has no base binfo (i.e. no ancestors) ! EXPECTED_TYPE is type of the class virtual belongs to. */ tree ! gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type) { HOST_WIDE_INT offset, size, max_size; ! tree base, type, binfo; ! bool last_artificial = false; if (!flag_devirtualize || TREE_CODE (cst) != ADDR_EXPR --- 1006,1025 ---- /* Return a binfo to be used for devirtualization of calls based on an object represented by a declaration (i.e. a global or automatically allocated one) or NULL if it cannot be found or is not safe. CST is expected to be an ! ADDR_EXPR of such object or the function will return NULL. ! ! It is up to the caller to check for absence of dynamic type changes. ! Because constructors may be partially inlined and the virtual tables ! during construction may be overwritten by virtual tables by base types, ! it is also up to caller to verify that either all base types have ! the same virtual method or that this does not happen. */ tree ! gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type, ! HOST_WIDE_INT otr_offset) { HOST_WIDE_INT offset, size, max_size; ! tree base, type; if (!flag_devirtualize || TREE_CODE (cst) != ADDR_EXPR *************** gimple_extract_devirt_binfo_from_cst (tr *** 1028,1074 **** if (!DECL_P (base) || max_size == -1 || max_size != size ! || TREE_CODE (type) != RECORD_TYPE) ! return NULL_TREE; ! ! /* Find the sub-object the constant actually refers to and mark whether it is ! an artificial one (as opposed to a user-defined one). */ ! while (true) ! { ! HOST_WIDE_INT pos, size; ! tree fld; ! ! if (types_same_for_odr (type, expected_type)) ! break; ! if (offset < 0) ! return NULL_TREE; ! ! for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) ! { ! if (TREE_CODE (fld) != FIELD_DECL) ! continue; ! ! pos = int_bit_position (fld); ! size = tree_low_cst (DECL_SIZE (fld), 1); ! if (pos <= offset && (pos + size) > offset) ! break; ! } ! if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE) ! return NULL_TREE; ! ! last_artificial = DECL_ARTIFICIAL (fld); ! type = TREE_TYPE (fld); ! offset -= pos; ! } ! /* Artificial sub-objects are ancestors, we do not want to use them for ! devirtualization, at least not here. */ ! if (last_artificial) ! return NULL_TREE; ! binfo = TYPE_BINFO (type); ! if (!binfo || BINFO_N_BASE_BINFOS (binfo) > 0) return NULL_TREE; ! else ! return binfo; } /* Attempt to fold a call statement referenced by the statement iterator GSI. --- 1032,1042 ---- if (!DECL_P (base) || max_size == -1 || max_size != size ! || TREE_CODE (type) != RECORD_TYPE ! || !TYPE_BINFO (type)) return NULL_TREE; ! return get_binfo_at_offset (TYPE_BINFO (type), ! offset + otr_offset, expected_type); } /* Attempt to fold a call statement referenced by the statement iterator GSI. *************** gimple_fold_call (gimple_stmt_iterator * *** 1108,1121 **** else { tree obj = OBJ_TYPE_REF_OBJECT (callee); tree binfo = gimple_extract_devirt_binfo_from_cst ! (obj, obj_type_ref_class (callee)); if (binfo) { HOST_WIDE_INT token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); ! if (fndecl) { gimple_call_set_fndecl (stmt, fndecl); changed = true; --- 1076,1095 ---- else { tree obj = OBJ_TYPE_REF_OBJECT (callee); + tree class_type = obj_type_ref_class (callee); tree binfo = gimple_extract_devirt_binfo_from_cst ! (obj, class_type, 0); if (binfo) { HOST_WIDE_INT token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); ! tree base_fndecl = gimple_get_virt_method_for_binfo (token, TYPE_BINFO (class_type)); ! /* Constructors may be partially inlined. We do not track ! if type is in construction and during that time the ! virtual table may correspond to virtual table of the ! base type. */ ! if (fndecl && base_fndecl == fndecl) { gimple_call_set_fndecl (stmt, fndecl); changed = true; Index: ipa-prop.c =================================================================== *** ipa-prop.c (revision 201814) --- ipa-prop.c (working copy) *************** try_make_edge_direct_virtual_call (struc *** 2444,2450 **** struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { ! tree binfo, target; binfo = ipa_value_from_jfunc (new_root_info, jfunc); --- 2444,2450 ---- struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { ! tree binfo, target, base_target = NULL; binfo = ipa_value_from_jfunc (new_root_info, jfunc); *************** try_make_edge_direct_virtual_call (struc *** 2454,2472 **** if (TREE_CODE (binfo) != TREE_BINFO) { binfo = gimple_extract_devirt_binfo_from_cst ! (binfo, ie->indirect_info->otr_type); if (!binfo) return NULL; } ! ! binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, ! ie->indirect_info->otr_type); if (binfo) target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, binfo); else return NULL; if (target) return ipa_make_edge_direct_to_target (ie, target); else --- 2454,2484 ---- if (TREE_CODE (binfo) != TREE_BINFO) { binfo = gimple_extract_devirt_binfo_from_cst ! (binfo, ie->indirect_info->otr_type, ! ie->indirect_info->offset); if (!binfo) return NULL; + /* Constructors may be partially inlined. We do not track + if type is in construction and during that time the + virtual table may correspond to virtual table of the + base type. */ + base_target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, + TYPE_BINFO (ie->indirect_info->otr_type)); + if (!base_target) + return NULL; } ! else ! binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, ! ie->indirect_info->otr_type); if (binfo) target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, binfo); else return NULL; + if (base_target && target != base_target) + return NULL; + if (target) return ipa_make_edge_direct_to_target (ie, target); else Index: gimple.h =================================================================== *** gimple.h (revision 201814) --- gimple.h (working copy) *************** unsigned get_gimple_rhs_num_ops (enum tr *** 854,860 **** gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); ! tree gimple_extract_devirt_binfo_from_cst (tree, tree); /* Returns true iff T is a scalar register variable. */ extern bool is_gimple_reg (tree); --- 854,860 ---- gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); ! tree gimple_extract_devirt_binfo_from_cst (tree, tree, HOST_WIDE_INT); /* Returns true iff T is a scalar register variable. */ extern bool is_gimple_reg (tree);