This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[lto] PATCH: Merge variables across object files


This patch adds support for merging variables across object files so
that we correctly recognize that if two object files both declare an
entity named "a", then those two entities are in fact the same:

We also complain about mismatches between the declarations.  For
example, if a.c contains:

  int a;

while b.c contains:

  extern short a;
  short *c = &a;

Then we get the error:

  a.o:0: error: type of 'a' does not match original declaration

(Yes, we should give the correct location for "a", and yes, it would
be nice to display the location of the two declarations and the
conflicting types.  Feel free to implement that!)

In order to perform the above test, I also added support for
DW_TAG_pointer_type.  (Anyone want to implement other DWARF "type
modifiers", like DW_TAG_reference_type, DW_TAG_const_type, ...?)

I also correct a bug in Daniel's patch; he had failed to update all
the places that used cu_start to use the new semantics he imposed.  I
reverted to the old semantics, since I think that's going to be more
convenient.

For reference, my current worklist:

* Add DIE cache.

* Write DejaGNU driver.

* Add support for functions.  (Though, here, I'm just going to build
  the FUNCTION_DECLs -- it's Kenny's job to handle the bodies of
  functions.) 

If you're planning to work on something related to LTO, please let me
know!

Thanks,

--
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

2006-07-09  Mark Mitchell  <mark@codesourcery.com>

	* lto.c (lto_abi_mismatch_error): New function.
	(lto_abbrev_read): Initialize num_abbrevs.
	(lto_read_form): Specify allowed form classes for
	DW_AT_declaration.  Adjust for change to lto_set_cu_context.
	(lto_read_variable_formal_parameter_constant_DIE): Handle
	DW_AT_declaration.  Call lto_symtab_merge_var.
	(lto_read_pointer_type_DIE): New function.
	(lto_read_base_type_DIE): Use build_nonstandard_integer_type.  Do
	not creat TYPE_DECLs for types that already have them.
	(lto_read_DIE): Add lto_read_pointer_type_DIE.
	(lto_set_cu_context): Make cu_start point to the header, not the
	first DIE.
	(lto_file_read): Adjust for change to lto_set_cu_context.
	* Make-lang.in (LTO_OBJS): Add lto-symtab.o.
	(lto/lto-symtab.o): New rule.
	* lto-tree.h (lang_identifier): Add decl field.
	(LANG_IDENTIFIER_CAST): New macro.
	(LTO_IDENTIFIER_DECL): Likewise.
	(lto_symtab_merge_var): Declare.
	* lto-symtab.c: New file.

Index: gcc/lto/lto.c
===================================================================
--- gcc/lto/lto.c	(revision 115297)
+++ gcc/lto/lto.c	(working copy)
@@ -251,6 +251,15 @@ lto_file_corrupt_error (const lto_fd *fd
 	       "%qs section", fd->name);
 }
 
+/* Issue an error indicating that the ABI used to compile the object
+   file does not match that currently in use by the LTO front end.  */
+static void
+lto_abi_mismatch_error (void)
+{
+  fatal_error ("ABI for object file does not match current "
+	       "compilation options");
+}
+
 /* Define a function:
 
      static C_TYPE 
@@ -409,7 +418,7 @@ lto_abbrev_read_attrs (lto_abbrev_fd *ab
 static void
 lto_abbrev_read (lto_abbrev_fd *abbrev_fd)
 {
-  size_t num_abbrevs;
+  size_t num_abbrevs = 0;
   unsigned pass;
   lto_fd *fd;
   
@@ -640,7 +649,7 @@ lto_read_form (lto_info_fd *info_fd, 
     DW_cl_error, /* decl_column */
     DW_cl_constant, /* decl_file */
     DW_cl_constant, /* decl_line */
-    DW_cl_error, /* declaration */
+    DW_cl_flag, /* declaration */
     DW_cl_error, /* discr_list */
     DW_cl_constant, /* encoding */
     DW_cl_flag, /* external */
@@ -824,7 +833,7 @@ lto_read_form (lto_info_fd *info_fd, 
 
 	out->cl = DW_cl_reference;
 	out->u.reference 
-	  = ((*form_context)->cu_start - (*form_context)->cu->cu_header_length
+	  = ((*form_context)->cu_start
 	     + lto_check_size_t_val (offset, "offset too large"));
 	if (out->u.reference >= (*form_context)->cu_end)
 	  lto_file_corrupt_error (fd);
@@ -1134,6 +1143,7 @@ lto_read_variable_formal_parameter_const
   tree name;
   tree type;
   bool external;
+  bool declaration;
   enum tree_code code;
   tree decl;
 
@@ -1144,6 +1154,7 @@ lto_read_variable_formal_parameter_const
   name = NULL_TREE;
   type = NULL_TREE;
   external = false;
+  declaration = false;
   code = ERROR_MARK;
   decl = NULL_TREE;
 
@@ -1174,6 +1185,9 @@ lto_read_variable_formal_parameter_const
       break;
 
     case DW_AT_declaration:
+      declaration = attr_data.u.flag;
+      break;
+
     case DW_AT_specification:
     case DW_AT_variable_parameter:
     case DW_AT_is_optional:
@@ -1201,6 +1215,7 @@ lto_read_variable_formal_parameter_const
     }
   decl = build_decl (code, name, type);
   TREE_PUBLIC (decl) = external;
+  DECL_EXTERNAL (decl) = declaration;
   if (!context->scope || TREE_CODE (context->scope) != FUNCTION_DECL)
     TREE_STATIC (decl) = 1;
   else
@@ -1209,15 +1224,53 @@ lto_read_variable_formal_parameter_const
        use the same method for variables outside of function scopes,
        for consistency.  */
     sorry ("cannot determine storage duration of local variables");
+  /* If this variable has already been declared, merge the
+     declarations.  */
+  decl = lto_symtab_merge_var (decl);
   /* Let the middle end know about the new entity.  */
-  rest_of_decl_compilation (decl,
-			    /*top_level=*/1,
-			    /*at_end=*/0);
+  if (decl != error_mark_node)
+    rest_of_decl_compilation (decl,
+			      /*top_level=*/1,
+			      /*at_end=*/0);
 
   lto_read_child_DIEs (fd, abbrev, context);
 }
 
 static void
+lto_read_pointer_type_DIE (lto_info_fd *fd,
+			   const DWARF2_abbrev *abbrev,
+			   lto_context *context)
+{
+  tree pointed_to;
+  tree type;
+
+  LTO_BEGIN_READ_ATTRS ()
+    {
+    case DW_AT_type:
+      pointed_to = lto_read_referenced_type_DIE (fd, 
+						 context, 
+						 attr_data.u.reference);
+      break;
+
+    case DW_AT_byte_size:
+      if (attr_data.cl != DW_cl_constant
+	  || attr_data.u.constant * BITS_PER_UNIT != POINTER_SIZE)
+	lto_abi_mismatch_error ();
+      break;
+    }
+  LTO_END_READ_ATTRS ();
+
+  /* The DW_AT_type attribute is required.  */
+  if (!pointed_to)
+    lto_file_corrupt_error ((lto_fd *)fd);
+  /* Build the pointer type.  */
+  type = build_pointer_type (pointed_to);
+
+  /* Record the type for our caller.  */
+  context->type = type;
+}
+
+static void
 lto_read_base_type_DIE (lto_info_fd *fd, 
 			const DWARF2_abbrev *abbrev,
 			lto_context *context)
@@ -1228,7 +1281,6 @@ lto_read_base_type_DIE (lto_info_fd *fd,
   bool have_size;
   uint64_t size;
   tree type;
-  tree decl;
 
   name = NULL_TREE;
   have_encoding = false;
@@ -1249,10 +1301,10 @@ lto_read_base_type_DIE (lto_info_fd *fd,
       break;
 
     case DW_AT_byte_size:
+      have_size = true;
       switch (attr_data.cl)
 	{
 	case DW_cl_constant:
-	  have_size = true;
 	  size = attr_data.u.constant;
 	  break;
 	default:
@@ -1283,22 +1335,25 @@ lto_read_base_type_DIE (lto_info_fd *fd,
 	bits = (BITS_PER_UNIT 
 		* lto_check_int_val (size,
 				     "size of base type too large"));
-	type = ((encoding == DW_ATE_signed) 
-		? make_signed_type 
-		: make_unsigned_type) (bits);
+	type = build_nonstandard_integer_type (bits,
+					       encoding == DW_ATE_unsigned);
       }
       break;
     default:
       sorry ("unsupported base type encoding");
       break;
     }
-  /* Set the name.  */
-  decl = build_decl (TYPE_DECL, name, type);
-  TYPE_NAME (type) = decl;
-  /* Let the middle end know about the type declaration.  */
-  rest_of_decl_compilation (decl, 
-			    /*top_level=*/1,
-			    /*at_end=*/0);
+  /* If this is a new type, declare it.  */
+  if (!TYPE_NAME (type))
+    {
+      tree decl;
+      decl = build_decl (TYPE_DECL, name, type);
+      TYPE_NAME (type) = decl;
+      /* Let the middle end know about the type declaration.  */
+      rest_of_decl_compilation (decl, 
+				/*top_level=*/1,
+				/*at_end=*/0);
+    }
  
   /* Record the type for our caller.  */
   context->type = type;
@@ -1332,7 +1387,7 @@ lto_read_DIE (lto_info_fd *fd, lto_conte
       NULL, /* padding */
       NULL, /* member */
       NULL, /* padding */
-      NULL, /* pointer_type */
+      lto_read_pointer_type_DIE,
       NULL, /* reference_type */
       lto_read_compile_unit_DIE,
       NULL, /* string_type */
@@ -1512,9 +1567,9 @@ static void
 lto_set_cu_context (lto_context *context, lto_info_fd *fd,
 		    DWARF2_CompUnit *unit)
 {
-  context->cu_start = fd->base.start + unit->cu_start_offset;
-  context->cu_end = 
-    context->cu_start + unit->cu_length - unit->cu_header_length;
+  context->cu_start = 
+    fd->base.start + unit->cu_start_offset - unit->cu_header_length;
+  context->cu_end = context->cu_start + unit->cu_length;
   context->cu = unit;
 }
 
@@ -1541,7 +1596,7 @@ lto_file_read (lto_file *file)
 
       /* Set up the context.  */
       lto_set_cu_context (&context, &file->debug_info, unit);
-      fd->cur = context.cu_start;
+      fd->cur = context.cu_start + unit->cu_header_length;
       context.scope = NULL_TREE;
       context.type = NULL_TREE;
 
Index: gcc/lto/Make-lang.in
===================================================================
--- gcc/lto/Make-lang.in	(revision 115297)
+++ gcc/lto/Make-lang.in	(working copy)
@@ -26,7 +26,7 @@
 # The name of the LTO compiler.
 LTO_EXE = lto1$(exeext)
 # The LTO-specific object files inclued in $(LTO_EXE).
-LTO_OBJS = lto/lto-lang.o lto/lto.o lto/lto-elf.o
+LTO_OBJS = lto/lto-lang.o lto/lto.o lto/lto-elf.o lto/lto-symtab.o
 
 ########################################################################
 # Rules
@@ -81,3 +81,5 @@ lto/lto.o: lto/lto.c $(CONFIG_H) $(CGRAP
 	$(GGC_H) opts.h $(SYSTEM_H) toplev.h $(TREE_H) $(TM_H) lto/lto.h
 lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H)\
 	toplev.h lto/lto.h
+lto/lto-symtab.o: lto/lto-symtab.c $(CONFIG_H) coretypes.h \
+	$(SYSTEM_H) toplev.h $(TREE_H) lto/lto.h lto/lto-tree.h
Index: gcc/lto/lto-tree.h
===================================================================
--- gcc/lto/lto-tree.h	(revision 115297)
+++ gcc/lto/lto-tree.h	(working copy)
@@ -25,6 +25,8 @@ Boston, MA 02110-1301, USA.  */
 struct lang_identifier GTY(())
 {
   struct tree_identifier base;
+  /* LTO_IDENTIFIER_DECL */
+  tree decl;
 };
 
 struct lang_decl GTY(())
@@ -51,4 +53,15 @@ union lang_tree_node GTY(
 			desc ("tree_node_structure (&%h)"))) generic;
 };
 
+/* Return NODE (an IDENTIFIER_NODE) as a pointer to a
+   "lang_identifier".  */
+#define LANG_IDENTIFIER_CAST(NODE) \
+  ((struct lang_identifier*)IDENTIFIER_NODE_CHECK (NODE))
+
+/* Return the VAR_DECL or FUNCTION_DECL with external linkage whose
+   DECL_ASSEMBLER_NAME is NODE, or NULL_TREE if there is no such
+   declaration.  */ 
+#define LTO_IDENTIFIER_DECL(NODE)		\
+  (LANG_IDENTIFIER_CAST (NODE)->decl)
+
 #endif /* GCC_LTO_TREE_H */
Index: gcc/lto/lto.h
===================================================================
--- gcc/lto/lto.h	(revision 115297)
+++ gcc/lto/lto.h	(working copy)
@@ -109,4 +109,18 @@ extern lto_file *lto_elf_file_open (cons
 /* Close an ELF input file.  */
 extern void lto_elf_file_close (lto_file *file);
 
+/* lto-symtab.c */
+
+/* The NEW_VAR (a VAR_DECL) has just been read.  If there is an
+   existing variable with the same name, merge the declaration for
+   NEW_VAR with the previous declaration and return the previous
+   declaration.  In this case, NEW_VAR must no longer be used by the
+   caller.  All other entities referenced from NEW_VAR (including, in
+   particular, its type) must already have been merged before this
+   function is called.  If the merge fails (due to inconsistencies
+   between the declarations), an error message is issued, and
+   error_mark_node is returned.  If there is no previous declaration,
+   NEW_VAR is returned.  */
+extern tree lto_symtab_merge_var (tree new_var);
+
 #endif /* LTO_H */
Index: gcc/lto/lto-symtab.c
===================================================================
--- gcc/lto/lto-symtab.c	(revision 0)
+++ gcc/lto/lto-symtab.c	(revision 0)
@@ -0,0 +1,171 @@
+/* LTO symbol table.
+   Copyright 2006 Free Software Foundation, Inc.
+   Contributed by CodeSourcery, 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 2, 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 COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "toplev.h"
+#include "tree.h"
+#include "lto.h"
+#include "lto-tree.h"
+
+/* Returns true iff TYPE_1 and TYPE_2 are the same type.  */
+static bool
+lto_same_type_p (tree type_1, tree type_2)
+{
+  /* ??? For now, assume that type equality is pointer equality.  */
+  return type_1 == type_2;
+}
+
+/* Returns true iff the union of ATTRIBUTES_1 and ATTRIBUTES_2 can be
+   applied to DECL.  */
+static bool
+lto_compatible_attributes_p (tree decl ATTRIBUTE_UNUSED, 
+			     tree attributes_1, 
+			     tree attributes_2)
+{
+  /* ??? For now, assume two attribute sets are compatible only if they
+     are both empty.  */
+  return !attributes_1 && !attributes_2;
+}
+
+tree 
+lto_symtab_merge_var (tree new_var) 
+{ 
+  tree old_var;
+  tree name;
+
+  gcc_assert (TREE_CODE (new_var) == VAR_DECL);
+  /* Variables with internal linkage do not need to be merged.  */
+  if (!TREE_PUBLIC (new_var))
+    return new_var;
+  /* Check that variables reaching this function do not have
+     properties inconsistent with having external linkage.  If any of
+     these asertions fail, then the object file reader has failed to
+     detect these cases and issue appropriate error messages.  */
+  gcc_assert (TREE_STATIC (new_var));
+  gcc_assert (!DECL_NONLOCAL (new_var));
+  gcc_assert (!DECL_REGISTER (new_var));
+  gcc_assert (!(DECL_EXTERNAL (new_var) && DECL_INITIAL (new_var)));
+  /* Retrieve the previous declaration.  */
+  name = DECL_ASSEMBLER_NAME (new_var);
+  old_var = LTO_IDENTIFIER_DECL (name);
+  /* If there was no previous declaration, then there is nothing to
+     merge.  */
+  if (!old_var)
+    {
+      LTO_IDENTIFIER_DECL (name) = new_var;
+      return new_var;
+    }
+  /* Check for inconsistencies.  */
+  if (TREE_CODE (old_var) != VAR_DECL)
+    {
+      gcc_assert (TREE_CODE (old_var) == FUNCTION_DECL);
+      error ("function %qD redeclared as variable", old_var);
+      return error_mark_node;
+    }
+  if (!lto_same_type_p (TREE_TYPE (old_var), TREE_TYPE (new_var)))
+    {
+      /* ??? We should allow an array type with unspecified bounds to
+	 be merged with an array type whose bounds are specified, so
+	 as to allow "extern int i[];" in one file to be combined with
+	 "int i[3];" in another.  */ 
+      error ("type of %qD does not match original declaration",
+	     new_var);
+      return error_mark_node;
+    }
+  if (DECL_UNSIGNED (old_var) != DECL_UNSIGNED (new_var))
+    {
+      error ("signedness of %qD does not match original declaration",
+	     new_var);
+      return error_mark_node;
+    }
+  if (!tree_int_cst_equal (DECL_SIZE (old_var),
+			   DECL_SIZE (new_var))
+      || !tree_int_cst_equal (DECL_SIZE_UNIT (old_var),
+			      DECL_SIZE_UNIT (new_var)))
+    {
+      error ("size of %qD does not match original declaration", 
+	     new_var);
+      return error_mark_node;
+    }
+  if (DECL_ALIGN (old_var) != DECL_ALIGN (new_var))
+    {
+      error ("alignment of %qD does not match original declaration",
+	     new_var);
+      return error_mark_node;
+    }
+  if (DECL_MODE (old_var) != DECL_MODE (new_var))
+    {
+      error ("machine mode of %qD does not match original declaration",
+	     new_var);
+      return error_mark_node;
+    }
+  if (!DECL_EXTERNAL (old_var) && !DECL_EXTERNAL (new_var))
+    {
+      /* ??? We should handle COMDAT here.  */
+      error ("%qD has already been defined", new_var);
+      return error_mark_node;
+    }
+  if (!lto_compatible_attributes_p (old_var,
+				    DECL_ATTRIBUTES (old_var),
+				    DECL_ATTRIBUTES (new_var)))
+    {
+      error ("attributes applied to %qD are incompatible with original "
+	     "declaration", new_var);
+      return error_mark_node;
+    }
+  /* We do not require matches for:
+
+     - DECL_NAME
+
+       Only the name used in object files matters.
+
+     - DECL_CONTEXT  
+
+       An entity might be declared in a C++ namespace in one file and
+       with a C identifier in another file.  
+
+     - TREE_PRIVATE, TREE_PROTECTED
+
+       Access control is the problem of the front end that created the
+       object file.  
+       
+     Therefore, at this point we have decided to merge the variables.  */
+  TREE_SIDE_EFFECTS (old_var) |= TREE_SIDE_EFFECTS (new_var);
+  TREE_CONSTANT (old_var) |= TREE_CONSTANT (new_var);
+  TREE_ADDRESSABLE (old_var) |= TREE_ADDRESSABLE (new_var);
+  TREE_THIS_VOLATILE (old_var) |= TREE_THIS_VOLATILE (new_var);
+  TREE_READONLY (old_var) |= TREE_READONLY (new_var);
+  TREE_INVARIANT (old_var) |= TREE_INVARIANT (new_var);
+  DECL_EXTERNAL (old_var) &= DECL_EXTERNAL (new_var);
+  DECL_PRESERVE_P (old_var) |= DECL_PRESERVE_P (new_var);
+  if (DECL_INITIAL (new_var))
+    {
+      gcc_assert (!DECL_INITIAL (old_var));
+      DECL_INITIAL (old_var) = DECL_INITIAL (new_var);
+    }
+  /* Callers should use the value returned.  So, we no longer need
+     NEW_VAR.  */
+  ggc_free (new_var);
+
+  return old_var;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]