Compile of KOffice with gcc4.1 failed. Source condensed to a test case which produces the following error: --------------- test-case-1.cpp: In function ‘void prepare_inpaint()’: test-case-1.cpp:55: error: incorrect sharing of tree nodes D.1986_15 = "rb"[0]; "rb"[0]; test-case-1.cpp:55: internal compiler error: verify_stmts failed --------------- gcc version info: --------------- Using built-in specs. Target: i686-pc-linux-gnu Configured with: ./configure Thread model: posix gcc version 4.1.0 20050927 (experimental) --------------- Compile command: --------------- g++ -O2 -c test-case-1.cpp --------------- The compile fails with O1, O2 and O3 but works with O0. Source code of test case (note: I could not reduce it any further) : --------------- namespace cimg_library { template<typename T = float> struct CImg; template<typename T = float> struct CImgl; struct Exception { }; struct IOException : Exception { IOException( const char * ) { } }; namespace cimg { inline int* dummy( const char* const path, const char* const mode ) { throw IOException( mode[ 0 ] == 'r' ? "a" : (mode[ 0 ] == 'w' ? "b" : "" )); return 0; } } template<typename T> struct CImg { T *data; explicit CImg( const unsigned int dx = 0 ) { } CImg( const char *filename ) { CImgl<T>( filename ).get_append( 'v', 'p' ); } }; template<typename T> struct CImgl { CImgl( const unsigned int ) { } CImgl(const char* filename) { cimg::dummy( filename, "rb" ); unsigned int n; CImgl<T> res( n ); } CImg<T> get_append( const char, const char ) const { CImg<T> res; return res; } }; } using namespace cimg_library; void prepare_inpaint( ) { cimg_library::CImg<unsigned char> mask = CImg<unsigned char>( "" ); } ---------------
This is caused by the inliner.
Here is a slightly shorter testcase: template<typename T = float> struct CImg; template<typename T = float> struct CImgl; struct IOException { IOException( const char * ){} }; inline int* dummy( const char* const path, const char* const mode ) { throw IOException( mode[ 0 ] == 'r' ? "a" : (mode[ 0 ] == 'w' ? "b" : "" )); } template<typename T> struct CImg { T *data; explicit CImg( const unsigned int dx = 0 ) { } CImg( const char *filename ) { CImgl<T>( filename ).get_append( 'v', 'p' ); } }; template<typename T> struct CImgl { CImgl( const unsigned int ) { } CImgl(const char* filename) { dummy( filename, "rb" ); unsigned int n; CImgl<T> res( n ); } CImg<T> get_append( const char, const char ) const { CImg<T> res; } }; void prepare_inpaint( ) { CImg<unsigned char> mask = CImg<unsigned char>( "" ); } --- I have not tried to reduce it further.
Confirmed, reduced testcase: void IOException( char); inline int* dummy( const char* const mode ) { IOException(*mode+*mode); } void prepare_inpaint( ) { dummy ("rb"); } The problem is that we have &"rb"[0] and we get *(&"rb"[0]) but we only fold that to "rb"[0] instead of all the way to 'r'.
A regression hunt using the testcase from comment #3 identified this patch from hubicka@gcc.gnu.org: http://gcc.gnu.org/ml/gcc-cvs/2005-05/msg00624.html
The problem is, that with this part of the blamed patch =================================================================== RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v retrieving revision 1.182 retrieving revision 1.183 diff -u -r1.182 -r1.183 --- gcc/gcc/tree-inline.c 2005/04/23 21:27:58 1.182 +++ gcc/gcc/tree-inline.c 2005/05/13 13:56:54 1.183 @@ -566,21 +566,15 @@ { /* Get rid of *& from inline substitutions that can happen when a pointer argument is an ADDR_EXPR. */ - tree decl = TREE_OPERAND (*tp, 0), value; + tree decl = TREE_OPERAND (*tp, 0); splay_tree_node n; n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); if (n) { - value = (tree) n->value; - STRIP_NOPS (value); - if (TREE_CODE (value) == ADDR_EXPR - && (lang_hooks.types_compatible_p - (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0))))) - { - *tp = TREE_OPERAND (value, 0); - return copy_body_r (tp, walk_subtrees, data); - } + *tp = build_fold_indirect_ref ((tree)n->value); + *walk_subtrees = 0; + return NULL; } } we do not have *tp unshared anymore. Is build_fold_indirect_ref supposed to do that? Anyway, I'll test the following: Index: tree-inline.c =================================================================== RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v retrieving revision 1.210 diff -c -3 -p -r1.210 tree-inline.c *** tree-inline.c 1 Aug 2005 15:25:28 -0000 1.210 --- tree-inline.c 4 Oct 2005 10:43:23 -0000 *************** copy_body_r (tree *tp, int *walk_subtree *** 636,643 **** else *tp = build1 (INDIRECT_REF, type, (tree)n->value); } ! *walk_subtrees = 0; ! return NULL; } } --- 636,642 ---- else *tp = build1 (INDIRECT_REF, type, (tree)n->value); } ! /* Fall through to copying the folded tree. */ } }
Other approach, make sure we fold it. We don't have fold_build4, neither does fold handle it. But there's fold_read_from_constant_string. Index: fold-const.c =================================================================== RCS file: /cvs/gcc/gcc/gcc/fold-const.c,v retrieving revision 1.626 diff -c -3 -p -r1.626 fold-const.c *** fold-const.c 26 Sep 2005 01:28:38 -0000 1.626 --- fold-const.c 4 Oct 2005 10:59:06 -0000 *************** fold_indirect_ref_1 (tree type, tree op0 *** 11511,11519 **** { tree op = TREE_OPERAND (sub, 0); tree optype = TREE_TYPE (op); ! /* *&p => p */ if (type == optype) ! return op; /* *(foo *)&fooarray => fooarray[0] */ else if (TREE_CODE (optype) == ARRAY_TYPE && type == TREE_TYPE (optype)) --- 11511,11525 ---- { tree op = TREE_OPERAND (sub, 0); tree optype = TREE_TYPE (op); ! /* *&p => p; make sure to handle *&"str"[cst] here. */ if (type == optype) ! { ! tree fop = fold_read_from_constant_string (op); ! if (fop) ! return fop; ! else ! return op; ! } /* *(foo *)&fooarray => fooarray[0] */ else if (TREE_CODE (optype) == ARRAY_TYPE && type == TREE_TYPE (optype))
(In reply to comment #6) > Other approach, make sure we fold it. We don't have fold_build4, neither does > fold handle it. But there's fold_read_from_constant_string. I rather see this patch here than the first one for 4.1. I also like to see a lot of the code from tree-ccp.c for folding moved to the proper spot for 4.2.
Subject: Re: [4.1 Regression] error: incorrect sharing of tree nodes > > > ------- Comment #5 from rguenth at gcc dot gnu dot org 2005-10-04 10:44 ------- > The problem is, that with this part of the blamed patch > > =================================================================== > RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v > retrieving revision 1.182 > retrieving revision 1.183 > diff -u -r1.182 -r1.183 > --- gcc/gcc/tree-inline.c 2005/04/23 21:27:58 1.182 > +++ gcc/gcc/tree-inline.c 2005/05/13 13:56:54 1.183 > @@ -566,21 +566,15 @@ > { > /* Get rid of *& from inline substitutions that can happen when a > pointer argument is an ADDR_EXPR. */ > - tree decl = TREE_OPERAND (*tp, 0), value; > + tree decl = TREE_OPERAND (*tp, 0); > splay_tree_node n; > > n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); > if (n) > { > - value = (tree) n->value; > - STRIP_NOPS (value); > - if (TREE_CODE (value) == ADDR_EXPR > - && (lang_hooks.types_compatible_p > - (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0))))) > - { > - *tp = TREE_OPERAND (value, 0); > - return copy_body_r (tp, walk_subtrees, data); > - } > + *tp = build_fold_indirect_ref ((tree)n->value); > + *walk_subtrees = 0; > + return NULL; > } > } > > we do not have *tp unshared anymore. Is build_fold_indirect_ref supposed > to do that? It is not, but the entries of decl_map are expected to be shareable. There are couple of other places where n->value is substituted in without further copying. I see there can be NOP_EXPRs and ADDR_EXPRs that are supposed to be unshared. We don't want to recurse on substituted value as we would mess up recursive functions and friends, but probably somehing like unshare_expr call on all places we dosubstitution would work? I can look into that tomorrow unless someone beats me ;) Honza
Patch posted.
Honza, I'm stuck with rth's comment that there may be still sharing bugs even after making fold_indirect_ref_1 fold the offending statement. And as you noted, calling copy_body_r on the folded or unfolded tree causes either nice recursion or the assert at tree-inline.c:665 to trigger. So can you try either convince rth that just doing better folding is safe or try your unshare_expr idea? Thx, Richard.
Honza, Richi... well? Is anyone going to do anything with this bug? :-)
My fix to fold works and is in out beta compiler.
This is patch I am testing to prevent the sharing. I think it is good idea in addition to Richard's patch to make fold do it's job too: void IOException( char); inline int* dummy( const char* const mode ) { IOException(*mode+*mode); } void prepare_inpaint( ) { dummy ("rb"); } Index: tree-inline.c =================================================================== RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v retrieving revision 1.212 diff -c -3 -p -r1.212 tree-inline.c *** tree-inline.c 12 Oct 2005 23:34:09 -0000 1.212 --- tree-inline.c 27 Oct 2005 21:16:34 -0000 *************** copy_body_r (tree *tp, int *walk_subtree *** 639,644 **** --- 639,645 ---- n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); if (n) { + tree new; /* If we happen to get an ADDR_EXPR in n->value, strip it manually here as we'll eventually get ADDR_EXPRs which lie about their types pointed to. In this case *************** copy_body_r (tree *tp, int *walk_subtree *** 646,658 **** but we absolutely rely on that. As fold_indirect_ref does other useful transformations, try that first, though. */ tree type = TREE_TYPE (TREE_TYPE ((tree)n->value)); ! *tp = fold_indirect_ref_1 (type, (tree)n->value); if (! *tp) { ! if (TREE_CODE ((tree)n->value) == ADDR_EXPR) ! *tp = TREE_OPERAND ((tree)n->value, 0); else ! *tp = build1 (INDIRECT_REF, type, (tree)n->value); } *walk_subtrees = 0; return NULL; --- 647,660 ---- but we absolutely rely on that. As fold_indirect_ref does other useful transformations, try that first, though. */ tree type = TREE_TYPE (TREE_TYPE ((tree)n->value)); ! new = unshare_expr ((tree)n->value); ! *tp = fold_indirect_ref_1 (type, new); if (! *tp) { ! if (TREE_CODE (new) == ADDR_EXPR) ! *tp = TREE_OPERAND (new, 0); else ! *tp = build1 (INDIRECT_REF, type, new); } *walk_subtrees = 0; return NULL;
Subject: Bug 24172 Author: hubicka Date: Sun Oct 30 18:14:15 2005 New Revision: 106247 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=106247 Log: PR tree-optimization/24172 * tree-inline.c (copy_body_r): Unshare the substituted value first. * g++.dg/tree-ssa/pr24172.C: New testcase. Added: trunk/gcc/testsuite/g++.dg/tree-ssa/pr24172.C Modified: trunk/gcc/ChangeLog trunk/gcc/testsuite/ChangeLog trunk/gcc/tree-inline.c
Fixed by my patch