> > This thing simply stays in GIMPLE and serves no purpose. I tried to remove > > it at one point, but run into some regressions. I plan to return to this. > > Have no idea how this was intended to work. > > Some bits was added by: > > https://gcc.gnu.org/ml/gcc-patches/2005-04/txt00100.txt > > Code in build_objc_method_call seems predating changelogs. > > I see. But the above should be harmless unless you throw it onto the ODR > predicates?
Yes, but it is what I do two lines bellow... > > >> > >> > + /* Match the type information stored in the wrapper. */ > >> > + > >> > + flags &= ~(OEP_CONSTANT_ADDRESS_OF|OEP_ADDRESS_OF); > >> > + return (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg0)) > >> > + == tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg1)) > >> > >> Err, please use tree_int_cst_eq () > > > > OK updated in my local copy and, I will update other places too, this was > > directly copied from ipa-icf-gimple. > >> > >> > + && types_same_for_odr (obj_type_ref_class (arg0), > >> > + obj_type_ref_class (arg1)) > >> > >> Why do you need this check? The args of OBJ_TYPE_REF should be fully > >> specifying the object, no? > > > > This is the type of THIS pointer that is extracted from pointer type of > > OBJ_TYPE_REF. Because we otherwise consider different pointers compatible, > > I think we need to check it here. > > Hum, this is fold-const.c and thus GENERIC. GENERIC doesn't consider pointer > types compatible in general. So I think it is not needed. As you say > elsewhere > the caller is responsible for restricting typeof the trees it asks for > equality. ... My understanding is that operand_equal_p should check that the value of operand is the same and will be the same after subsequentoptimizations. For this reason it does its own type matching (comparsion of TYPE_MODE for expressions, TYPE_UNSIGNED where it matters). It also compares alias information for MEM_REF to be sure that both reads from memory will always return the same even after later optimizations. OBJ_TYPE_REF type info is no different from TBAA alias info. It is just additional information which is used for later optimization and therefore it must match across the two copies. Modulo the fact that we don't seem to fold these, I think one way to confuse us is: test ? ((derivedobject1 *)ptr)->foo () : ((derivedobject *)ptr)->foo () Here we have two NOP_EXPR that converts PTR to two derived objects and OBJ_TYPE_REF will look equivalent modulo having different type. If return value is the same, I think it w Plus operand_equal_p is used for both GENERIC and GIMPLE (which is somewhat ugly indeed. We may just fork the implementation eventally if wemove away completely from sharing code paths between GENERIC and GIMPLE). Ok, I tried to fix my testcase to be optimized and modify it to case where we need to type match. I can't do that with current tree but I think it is just a lazyness elsewhere that should be fixed. Doing: struct foo { virtual int test () __attribute__ ((pure)); }; int ret (struct foo *f, int v) { return v ? f->test() : f->test(); } We actually match both OBJ_TYPE_REFs in GENERIC folding: #0 operand_equal_p (arg0=0x7ffff6ad6b10, arg1=0x7ffff6ad6a50, flags=flags@entry=0) at ../../gcc/fold-const.c:2706 #1 0x00000000009fa979 in operand_equal_p (arg0=arg0@entry=0x7ffff6ae20e0, arg1=arg1@entry=0x7ffff6ae20a8, flags=flags@entry=0) at ../../gcc/fold-const.c:3130 #2 0x0000000001097e78 in generic_simplify_COND_EXPR (code=COND_EXPR, op2=0x7ffff6ae20e0, op1=0x7ffff6ae20a8, op0=0x7ffff6c35758, type=0x7ffff6adb7e0, loc=1042) at generic-match.c:24047 #3 generic_simplify (loc=loc@entry=1042, code=code@entry=COND_EXPR, type=type@entry=0x7ffff6adb7e0, op0=op0@entry=0x7ffff6c35758, op1=op1@entry=0x7ffff6ae20a8, op2=op2@entry=0x7ffff6ae20e0) at generic-match.c:24230 #4 0x0000000000a066cd in fold_ternary_loc (loc=loc@entry=1042, code=code@entry=COND_EXPR, type=type@entry=0x7ffff6adb7e0, op0=0x7ffff6c35758, op1=0x7ffff6ae20a8, op2=0x7ffff6ae20e0) at ../../gcc/fold-const.c:11377 #5 0x0000000000a28c85 in fold (expr=<optimized out>) at ../../gcc/fold-const.c:11996 #6 0x000000000079118a in fold_if_not_in_template (expr=<optimized out>) at ../../gcc/cp/tree.c:4287 This will not result in the desired optimization because we give up in the otuer operand_equal_p matching CALL_EXPR: 0x00000000009fa979 in operand_equal_p (arg0=arg0@entry=0x7ffff6ae20e0, arg1=arg1@entry=0x7ffff6ae20a8, flags=flags@entry=0) at ../../gcc/fold-const.c:3130 3130 if (! operand_equal_p (CALL_EXPR_FN (arg0), CALL_EXPR_FN (arg1), Value returned is $13 = 1 (gdb) l 3125 } 3126 else 3127 { 3128 /* If the CALL_EXPRs call different functions, then they are not 3129 equal. */ 3130 if (! operand_equal_p (CALL_EXPR_FN (arg0), CALL_EXPR_FN (arg1), 3131 flags)) 3132 return 0; 3133 } 3134 (gdb) 3135 { 3136 unsigned int cef = call_expr_flags (arg0); 3137 if (flags & OEP_PURE_SAME) 3138 cef &= ECF_CONST | ECF_PURE; 3139 else 3140 cef &= ECF_CONST; 3141 if (!cef) 3142 return 0; 3143 } Here we end up with flags=0 (why? It seems that generic_simplify_COND_EXPR could be happy about pure/const functions and in fact it should be happy about side effects, too, because only one branc of the ternary operation is going to be executed). I think for uses where we know only one code path is going to be thaken, we want OEP_MATCH_SIDE_EFFECTS flag to operand_equal_p telling it that two operands can be considered equvalent if the side effects are. Extending my testcase as follows: struct foo { virtual int test () __attribute__ ((pure)); }; struct foo1:foo { virtual int test () __attribute__ ((pure)); }; struct foo2:foo { virtual int test () __attribute__ ((pure)); }; int ret (struct foo *f, int v) { return v ? ((foo1 *)f)->test() : ((foo2 *)f)->test(); } Will led to false on comparing OBJ_TYPE_REF calling OP_SAME(1) (so no ODR type matching needed). I think this is just bug (missed optimization at least with -fno-strict-aliasing): the reason is that we match <component_ref 0x7ffff6ad6c90 type <record_type 0x7ffff6c3d930 foo addressable tree_2 needs-constructing type_5 type_6 BLK size <integer_cst 0x7ffff6ad7e58 constant 64> unit size <integer_cst 0x7ffff6ad7e70 constant 8> align 64 symtab 0 alias set -1 canonical type 0x7ffff6c3d930 fields <field_decl 0x7ffff6c3a4c0 _vptr.foo type <pointer_type 0x7ffff6c34540> unsigned virtual DI file t.C line 3 col 8 size <integer_cst 0x7ffff6ad7e58 64> unit size <integer_cst 0x7ffff6ad7e70 8> align 64 offset_align 128 offset <integer_cst 0x7ffff6ad7e88 constant 0> bit offset <integer_cst 0x7ffff6ad7ed0 constant 0> context <record_type 0x7ffff6c3d930 foo> chain <type_decl 0x7ffff6c3a428 foo>> context <translation_unit_decl 0x7ffff7ff81e0 D.1> full-name "struct foo" needs-constructor X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown pointer_to_this <pointer_type 0x7ffff6c3dbd0> reference_to_this <reference_type 0x7ffff6c3de70> chain <type_decl 0x7ffff6c3a390 foo>> arg 0 <indirect_ref 0x7ffff6c49da0 type <record_type 0x7ffff6c513f0 foo2 addressable tree_2 needs-constructing type_5 type_6 BLK size <integer_cst 0x7ffff6ad7e58 64> unit size <integer_cst 0x7ffff6ad7e70 8> align 64 symtab 0 alias set -1 canonical type 0x7ffff6c513f0 fields <field_decl 0x7ffff6c3aab0 D.2317> context <translation_unit_decl 0x7ffff7ff81e0 D.1> full-name "struct foo2" needs-constructor X() X(constX&) this=(X&) n_parents=1 use_template=0 interface-unknown pointer_to_this <pointer_type 0x7ffff6c51690> reference_to_this <reference_type 0x7ffff6c51930> chain <type_decl 0x7ffff6c3a980 foo2>> arg 0 <nop_expr 0x7ffff6c49d60 type <pointer_type 0x7ffff6c51690> arg 0 <parm_decl 0x7ffff6c4cf00 f>>> arg 1 <field_decl 0x7ffff6c3aab0 D.2317 type <record_type 0x7ffff6c3d930 foo> ignored decl_6 BLK file t.C line 9 col 8 size <integer_cst 0x7ffff6ad7e58 64> unit size <integer_cst 0x7ffff6ad7e70 8> align 64 offset_align 128 offset <integer_cst 0x7ffff6ad7e88 0> bit offset <integer_cst 0x7ffff6ad7ed0 0> context <record_type 0x7ffff6c513f0 foo2> chain <type_decl 0x7ffff6c3aa18 foo2 type <record_type 0x7ffff6c51498 foo2> nonlocal decl_4 VOID file t.C line 9 col 17 align 1 context <record_type 0x7ffff6c513f0 foo2> result <record_type 0x7ffff6c513f0 foo2> >>> Here we mismatch FIELD_DECL because currently we insist on them being equivalent as declarations. In one expression it comes from foo1 and in other from foo2. I think this is just missed optimization (ipa-icf-gimple just compare that FIELD_DECLS represents same offset and subtype and I think operand_equal_p should do, too). If you rememember, I have a patch for this in queue. https://gcc.gnu.org/ml/gcc-patches/2015-05/msg02112.html With -fno-strict-aliasing I think we ought to match both memory accesses as they really are equivalent and in longer run we ought to teach ipa-icf-gimple to merge alias infos in cases it merges memory accesses with mismatching TBAA info. This code also hits in LTO where two same types ends up not merged by tree merging. Honza