[C++ PATCH] Fix -frepo (PR c++/34178, c++/34340, take 3)

Jakub Jelinek jakub@redhat.com
Fri Dec 7 10:35:00 GMT 2007


On Thu, Dec 06, 2007 at 08:27:55PM -0800, Mark Mitchell wrote:
> > But in repo7.C the const static data member is aggregate, not integral or
> > enum.  finish_static_data_member has
> >   /* Force the compiler to know when an uninitialized static const
> >      member is being used.  */
> >   if (CP_TYPE_CONST_P (TREE_TYPE (decl)) && init == 0)
> >     TREE_USED (decl) = 1;
> > Later on duplicate_decls clears DECL_EXTERNAL on this decl (because a
> > definition was parsed) and after tsubst finish_static_data_member
> > sets TREE_USED again.  Next in instantiate_decl we call
> > 14742     if (TREE_PUBLIC (d) && !DECL_REALLY_EXTERN (d) && !repo_emit_p (d))
> > on this, and with DECL_INTEGRAL_CONSTANT_VAR_P test in repo_emit_p that
> > returns 0, which means the rest of instantiate_decl is bypassed for this
> > decl.  Then this makes into cgraph, which emits it, as the decl
> > is !DECL_EXTERNAL, TREE_USED etc. (and even emits it without the
> > initialized as common symbol, as DECL_INITIAL is NULL).
> 
> I'm confused.  Are we talking about the second time we process the file,
> at link-time?

No, I'm talking about the original compilation, which creates the .rpo file.

Perhaps even shorter testcase with ./cc1plus -frepo testcase.C is:
struct A { int a; };
template <typename T> struct D { static const A b; };
template<typename T> const A D<T>::b = { 2 };
template class D<A>;
int main () { }

Here there is just one static data member.
finish_static_data_member_decl marks D<T>::b as TREE_USED because it is
const.  duplicate_decls is called on:
olddecl
 <var_decl 0x2aaaaea7f000 b
    type <record_type 0x2aaaaea75b40 A readonly type_1 type_5 type_6 SI
        size <integer_cst 0x2aaaae927a50 constant invariant 32>
        unit size <integer_cst 0x2aaaae9276c0 constant invariant 4>
        align 32 symtab 0 alias set -1 canonical type 0x2aaaaea75b40
        fields <field_decl 0x2aaaaea21f00 a type <integer_type 0x2aaaae93a540 int>
            nonlocal decl_3 SI file repo8.C line 8 col 7 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
            align 32 offset_align 128
            offset <integer_cst 0x2aaaae9276f0 constant invariant 0>
            bit offset <integer_cst 0x2aaaae948330 constant invariant 0> context <record_type 0x2aaaaea753c0 A> chain <type_decl 0x2aaaaea75540 A>>
        X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown>
    used public static external decl_3 decl_6 SI file repo8.C line 13 col 18 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
    align 32 context <record_type 0x2aaaaea75780 D>
    template-info 0x2aaaaea6bde0 chain <type_decl 0x2aaaaea759c0 D>>
newdecl
 <var_decl 0x2aaaaea7f0a0 b
    type <record_type 0x2aaaaea75b40 A readonly type_1 type_5 type_6 SI
        size <integer_cst 0x2aaaae927a50 constant invariant 32>
        unit size <integer_cst 0x2aaaae9276c0 constant invariant 4>
        align 32 symtab 0 alias set -1 canonical type 0x2aaaaea75b40
        fields <field_decl 0x2aaaaea21f00 a type <integer_type 0x2aaaae93a540 int>
            nonlocal decl_3 SI file repo8.C line 8 col 7 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
            align 32 offset_align 128
            offset <integer_cst 0x2aaaae9276f0 constant invariant 0>
            bit offset <integer_cst 0x2aaaae948330 constant invariant 0> context <record_type 0x2aaaaea753c0 A> chain <type_decl 0x2aaaaea75540 A>>
        X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown>
    public static SI file repo8.C line 16 col 36 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
    align 32 context <record_type 0x2aaaaea75780 D>
   >
so it still has DECL_AGGR_P set, but is no longer external.  start_decl
later clears DECL_IN_AGGR_P on this.  Then tsubst_decl creates a new
var_decl:
 <var_decl 0x2aaaaea7f1e0 b
    type <record_type 0x2aaaaea75b40 A readonly type_1 type_5 type_6 SI
        size <integer_cst 0x2aaaae927a50 constant invariant 32>
        unit size <integer_cst 0x2aaaae9276c0 constant invariant 4>
        align 32 symtab 0 alias set -1 canonical type 0x2aaaaea75b40
        fields <field_decl 0x2aaaaea21f00 a type <integer_type 0x2aaaae93a540 int>
            nonlocal decl_3 SI file repo8.C line 8 col 7 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
            align 32 offset_align 128
            offset <integer_cst 0x2aaaae9276f0 constant invariant 0>
            bit offset <integer_cst 0x2aaaae948330 constant invariant 0> context <record_type 0x2aaaaea753c0 A> chain <type_decl 0x2aaaaea75540 A>>
        X() X(constX&) this=(X&) n_parents=0 use_template=0 interface-unknown>
    readonly used public static tree_2 external SI file repo8.C line 16 col 36 size <integer_cst 0x2aaaae927a50 32> unit size <integer_cst 0x2aaaae9276c0 4>
    align 32 context <record_type 0x2aaaaea75e40 D>
    template-info 0x2aaaaea803c0>
and finish_static_data_member_decl on it 
737       if (CP_TYPE_CONST_P (TREE_TYPE (decl)) && init == 0)
738         TREE_USED (decl) = 1;
739       DECL_INITIAL (decl) = init;
740       DECL_IN_AGGR_P (decl) = 1;
instantiate_class_member -> mark_decl_instantiated marks this as
instantiated and finally instantiate_class_member -> instantiate_decl
calls that
14742     if (TREE_PUBLIC (d) && !DECL_REALLY_EXTERN (d) && !repo_emit_p (d))

If repo_emit_p doesn't return 0 in this case, then instantiate_decl
would later do:
      /* Clear out DECL_RTL; whatever was there before may not be right
         since we've reset the type of the declaration.  */
      SET_DECL_RTL (d, NULL_RTX);
      DECL_IN_AGGR_P (d) = 0;

      /* The initializer is placed in DECL_INITIAL by
         regenerate_decl_from_template.  Pull it out so that
         finish_decl can process it.  */
      init = DECL_INITIAL (d);
      DECL_INITIAL (d) = NULL_TREE;
      DECL_INITIALIZED_P (d) = 0;

      /* Clear DECL_EXTERNAL so that cp_finish_decl will process the
         initializer.  That function will defer actual emission until
         we have a chance to determine linkage.  */
      DECL_EXTERNAL (d) = 0;

      /* Enter the scope of D so that access-checking works correctly.  */
      push_nested_class (DECL_CONTEXT (d));
      finish_decl (d, init, NULL_TREE);
      pop_nested_class ();
But as repo_emit_p above returned 0, it will just go out, but 0x2aaaaea7f1e0
is TREE_USED, !DECL_EXTERNAL so it will be emitted, even without
initializer (because regenerate_decl_from_template which sets DECL_INITIAL
has not been called either).  If I explicitly make d DECL_EXTERNAL again
when repo_emit_p returns 0, this testcase and even one with additional
const A *x = &D<B>::b;
compiles, doesn't contain _ZN1DI1BE1bE definition (only references it in
x var):
rm -f repo7{.o,.s,.rpo}; ./g++ -B ./ -frepo -c repo7.C; nm repo7.o; ./g++ -B ./ -frepo repo7.o -o repo7 -L ../x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/; cat repo7.rpo; nm repo7 | grep ' _Z'
0000000000000000 R _ZN1B1bE
                 U _ZN1DI1BE1bE
                 U __gxx_personality_v0
0000000000000000 T main
0000000000000000 D x
collect: recompiling repo7.C
collect: relinking
M repo7.C
D /usr/src/gcc/obj/gcc
A '-B' './' '-frepo' '-c' '-shared-libgcc' '-mtune=generic' '-frandom-seed=0xb3d94816' '-shared-libgcc'
C _ZN1DI1BE1bE
000000000040063c R _ZN1B1bE
0000000000400640 V _ZN1DI1BE1bE

which looks ok.  Making it DECL_EXTERNAL and !DECL_NOT_REALLY_EXTERN matches
what import_export_decl (the only other caller of repo_emit_p) is already
doing if repo_emit_p returns 0 - sets import_p and later:
  if (import_p)
    {
      /* If we are importing DECL into this translation unit, mark is
         an undefined here.  */
      DECL_EXTERNAL (decl) = 1;
      DECL_NOT_REALLY_EXTERN (decl) = 0;
    }

I have regtested following patch on x86_64-linux, ok for trunk?

2007-12-07  Jakub Jelinek  <jakub@redhat.com>

	PR c++/34178
	PR c++/34340
	* repo.c (repo_emit_p): Return 2 for DECL_INTEGRAL_CONSTANT_VAR_P
	in class scope rather than DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P.
	* decl2.c (import_export_decl): Don't make VAR_DECLs import_p when
	flag_use_repository and repo_emit_p returned 2.
	* pt.c (instantiate_decl): If repo_emit_p returned 0, mark d as
	DECL_EXTERNAL and !DECL_NOT_REALLY_EXTERN.

	* g++.dg/template/repo6.C: New test.
	* g++.dg/template/repo7.C: New test.

--- gcc/cp/pt.c.jj	2007-12-05 21:42:08.000000000 +0100
+++ gcc/cp/pt.c	2007-12-07 11:11:22.000000000 +0100
@@ -14753,7 +14753,14 @@ instantiate_decl (tree d, int defer_ok,
       if (!(TREE_CODE (d) == FUNCTION_DECL
 	    && flag_inline_trees
 	    && DECL_DECLARED_INLINE_P (d)))
-	goto out;
+	{
+	  /* If we are importing DECL into this translation unit, mark it
+	     as undefined here.  */
+	  DECL_EXTERNAL (d) = 1;
+	  DECL_NOT_REALLY_EXTERN (d) = 0;
+	  DECL_INTERFACE_KNOWN (d) = 1;
+	  goto out;
+	}
     }
 
   need_push = !cfun || !global_bindings_p ();
--- gcc/cp/decl2.c.jj	2007-12-06 12:00:12.000000000 +0100
+++ gcc/cp/decl2.c	2007-12-06 12:00:19.000000000 +0100
@@ -2230,7 +2230,8 @@ import_export_decl (tree decl)
     {
       /* DECL is an implicit instantiation of a function or static
 	 data member.  */
-      if (flag_implicit_templates
+      if ((flag_implicit_templates
+	   && !flag_use_repository)
 	  || (flag_implicit_inline_templates
 	      && TREE_CODE (decl) == FUNCTION_DECL
 	      && DECL_DECLARED_INLINE_P (decl)))
--- gcc/cp/repo.c.jj	2007-12-06 11:53:40.000000000 +0100
+++ gcc/cp/repo.c	2007-12-07 09:35:46.000000000 +0100
@@ -304,10 +304,10 @@ repo_emit_p (tree decl)
 	  && (!TYPE_LANG_SPECIFIC (type)
 	      || !CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
 	return 2;
-      /* Static data members initialized by constant expressions must
+      /* Const static data members initialized by constant expressions must
 	 be processed where needed so that their definitions are
 	 available.  */
-      if (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)
+      if (DECL_INTEGRAL_CONSTANT_VAR_P (decl)
 	  && DECL_CLASS_SCOPE_P (decl))
 	return 2;
     }
--- gcc/testsuite/g++.dg/template/repo6.C.jj	2007-12-06 12:00:19.000000000 +0100
+++ gcc/testsuite/g++.dg/template/repo6.C	2007-12-06 12:00:19.000000000 +0100
@@ -0,0 +1,25 @@
+// PR c++/34178
+// { dg-options "-frepo" }
+// { dg-final { cleanup-repo-files } }
+// { dg-require-host-local "" }
+
+template<typename T>
+class A
+{
+private:
+  static const int x;
+  static int y;
+
+public:
+  int getX () { return x + y; }
+};
+
+template<typename T> const int A<T>::x = 0;
+template<typename T> int A<T>::y = 0;
+
+int
+main ()
+{
+  A<int> a;
+  return a.getX();
+}
--- gcc/testsuite/g++.dg/template/repo7.C.jj	2007-12-06 12:00:12.000000000 +0100
+++ gcc/testsuite/g++.dg/template/repo7.C	2007-12-07 11:07:53.000000000 +0100
@@ -0,0 +1,24 @@
+// PR c++/34340
+// { dg-options "-frepo" }
+// { dg-final { cleanup-repo-files } }
+// { dg-require-host-local "" }
+
+struct A
+{
+  int a;
+};
+
+template <typename T> struct D
+{
+  static const A b;
+};
+
+template<typename T> const A D<T>::b = { 2 };
+template class D<A>;
+
+const A *x = &D<A>::b;
+
+int
+main ()
+{
+}


	Jakub



More information about the Gcc-patches mailing list