e835ae2
2012-12-06  Jakub Jelinek  <jakub@redhat.com>
e835ae2
e835ae2
	PR debug/55608
e835ae2
	* dwarf2out.c (tree_add_const_value_attribute): Call ggc_free (array)
e835ae2
	on failure.
e835ae2
	(resolve_one_addr): Fail if referenced STRING_CST hasn't been written.
e835ae2
	(string_cst_pool_decl): New function.
e835ae2
	(resolve_addr_in_expr): Optimize DWARF location expression
e835ae2
	DW_OP_addr DW_OP_stack_value where DW_OP_addr refers to some variable
e835ae2
	which doesn't live in memory, but has DW_AT_location or
e835ae2
	DW_AT_const_value, or refers to a string literal, into
e835ae2
	DW_OP_GNU_implicit_pointer.
e835ae2
	(resolve_addr): If removing DW_AT_location of a variable because
e835ae2
	it was DW_OP_addr of address of the variable, but the variable doesn't
e835ae2
	live in memory, try to emit const value attribute for the initializer.
e835ae2
e835ae2
--- gcc/dwarf2out.c.jj	2012-11-21 18:44:25.847837373 +0100
e835ae2
+++ gcc/dwarf2out.c	2012-12-06 17:22:14.348761149 +0100
e835ae2
@@ -15492,6 +15492,7 @@ tree_add_const_value_attribute (dw_die_r
e835ae2
 	      add_AT_vec (die, DW_AT_const_value, size, 1, array);
e835ae2
 	      return true;
e835ae2
 	    }
e835ae2
+	  ggc_free (array);
e835ae2
 	}
e835ae2
     }
e835ae2
   return false;
e835ae2
@@ -22370,6 +22371,10 @@ resolve_one_addr (rtx *addr, void *data
e835ae2
       if (!rtl || !MEM_P (rtl))
e835ae2
 	return 1;
e835ae2
       rtl = XEXP (rtl, 0);
e835ae2
+      if (GET_CODE (rtl) == SYMBOL_REF
e835ae2
+	  && SYMBOL_REF_DECL (rtl)
e835ae2
+	  && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl)))
e835ae2
+	return 1;
e835ae2
       vec_safe_push (used_rtx_array, rtl);
e835ae2
       *addr = rtl;
e835ae2
       return 0;
e835ae2
@@ -22394,6 +22399,46 @@ resolve_one_addr (rtx *addr, void *data
e835ae2
   return 0;
e835ae2
 }
e835ae2
 
e835ae2
+/* For STRING_CST, return SYMBOL_REF of its constant pool entry,
e835ae2
+   if possible, and create DW_TAG_dwarf_procedure that can be referenced
e835ae2
+   from DW_OP_GNU_implicit_pointer if the string hasn't been seen yet.  */
e835ae2
+
e835ae2
+static rtx
e835ae2
+string_cst_pool_decl (tree t)
e835ae2
+{
e835ae2
+  rtx rtl = output_constant_def (t, 1);
e835ae2
+  unsigned char *array;
e835ae2
+  dw_loc_descr_ref l;
e835ae2
+  tree decl;
e835ae2
+  size_t len;
e835ae2
+  dw_die_ref ref;
e835ae2
+
e835ae2
+  if (!rtl || !MEM_P (rtl))
e835ae2
+    return NULL_RTX;
e835ae2
+  rtl = XEXP (rtl, 0);
e835ae2
+  if (GET_CODE (rtl) != SYMBOL_REF
e835ae2
+      || SYMBOL_REF_DECL (rtl) == NULL_TREE)
e835ae2
+    return NULL_RTX;
e835ae2
+
e835ae2
+  decl = SYMBOL_REF_DECL (rtl);
e835ae2
+  if (lookup_decl_die (decl))
e835ae2
+    return rtl;
e835ae2
+
e835ae2
+  len = TREE_STRING_LENGTH (t);
e835ae2
+  vec_safe_push (used_rtx_array, rtl);
e835ae2
+  ref = new_die (DW_TAG_dwarf_procedure, comp_unit_die (), decl);
e835ae2
+  array = (unsigned char *) ggc_alloc_atomic (len);
e835ae2
+  memcpy (array, TREE_STRING_POINTER (t), len);
e835ae2
+  l = new_loc_descr (DW_OP_implicit_value, len, 0);
e835ae2
+  l->dw_loc_oprnd2.val_class = dw_val_class_vec;
e835ae2
+  l->dw_loc_oprnd2.v.val_vec.length = len;
e835ae2
+  l->dw_loc_oprnd2.v.val_vec.elt_size = 1;
e835ae2
+  l->dw_loc_oprnd2.v.val_vec.array = array;
e835ae2
+  add_AT_loc (ref, DW_AT_location, l);
e835ae2
+  equate_decl_number_to_die (decl, ref);
e835ae2
+  return rtl;
e835ae2
+}
e835ae2
+
e835ae2
 /* Helper function for resolve_addr, handle one location
e835ae2
    expression, return false if at least one CONST_STRING or SYMBOL_REF in
e835ae2
    the location list couldn't be resolved.  */
e835ae2
@@ -22402,23 +22447,81 @@ static bool
e835ae2
 resolve_addr_in_expr (dw_loc_descr_ref loc)
e835ae2
 {
e835ae2
   dw_loc_descr_ref keep = NULL;
e835ae2
-  for (; loc; loc = loc->dw_loc_next)
e835ae2
+  bool start_of_dw_expr = true;
e835ae2
+  for (; loc; start_of_dw_expr = (loc->dw_loc_opc == DW_OP_piece
e835ae2
+				  || loc->dw_loc_opc == DW_OP_bit_piece),
e835ae2
+	      loc = loc->dw_loc_next)
e835ae2
     switch (loc->dw_loc_opc)
e835ae2
       {
e835ae2
       case DW_OP_addr:
e835ae2
 	if (resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL))
e835ae2
-	  return false;
e835ae2
+	  {
e835ae2
+	    if (start_of_dw_expr
e835ae2
+		&& loc->dw_loc_next
e835ae2
+		&& loc->dw_loc_next->dw_loc_opc == DW_OP_stack_value
e835ae2
+		&& !dwarf_strict)
e835ae2
+	      {
e835ae2
+		rtx rtl = loc->dw_loc_oprnd1.v.val_addr;
e835ae2
+		HOST_WIDE_INT offset = 0;
e835ae2
+		dw_die_ref ref = NULL;
e835ae2
+		tree decl;
e835ae2
+
e835ae2
+		if (GET_CODE (rtl) == CONST
e835ae2
+		    && GET_CODE (XEXP (rtl, 0)) == PLUS
e835ae2
+		    && CONST_INT_P (XEXP (XEXP (rtl, 0), 1)))
e835ae2
+		  {
e835ae2
+		    offset = INTVAL (XEXP (XEXP (rtl, 0), 1));
e835ae2
+		    rtl = XEXP (XEXP (rtl, 0), 0);
e835ae2
+		  }
e835ae2
+		if (GET_CODE (rtl) == CONST_STRING)
e835ae2
+		  {
e835ae2
+		    size_t len = strlen (XSTR (rtl, 0)) + 1;
e835ae2
+		    tree t = build_string (len, XSTR (rtl, 0));
e835ae2
+		    tree tlen = size_int (len - 1);
e835ae2
+
e835ae2
+		    TREE_TYPE (t)
e835ae2
+		      = build_array_type (char_type_node,
e835ae2
+					  build_index_type (tlen));
e835ae2
+		    rtl = string_cst_pool_decl (t);
e835ae2
+		    if (!rtl)
e835ae2
+		      return false;
e835ae2
+		  }
e835ae2
+		if (GET_CODE (rtl) == SYMBOL_REF
e835ae2
+		    && SYMBOL_REF_DECL (rtl))
e835ae2
+		  {
e835ae2
+		    decl = SYMBOL_REF_DECL (rtl);
e835ae2
+		    if (TREE_CODE (decl) == VAR_DECL
e835ae2
+			&& !DECL_EXTERNAL (decl))
e835ae2
+		      {
e835ae2
+			ref = lookup_decl_die (decl);
e835ae2
+			if (ref
e835ae2
+			    && (get_AT (ref, DW_AT_location)
e835ae2
+				|| get_AT (ref, DW_AT_const_value)))
e835ae2
+			  {
e835ae2
+			    loc->dw_loc_opc = DW_OP_GNU_implicit_pointer;
e835ae2
+			    loc->dw_loc_oprnd1.val_class
e835ae2
+			      = dw_val_class_die_ref;
e835ae2
+			    loc->dw_loc_oprnd1.val_entry = NULL;
e835ae2
+			    loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
e835ae2
+			    loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
e835ae2
+			    loc->dw_loc_next = loc->dw_loc_next->dw_loc_next;
e835ae2
+			    loc->dw_loc_oprnd2.v.val_int = offset;
e835ae2
+			    break;
e835ae2
+			  }
e835ae2
+		      }
e835ae2
+		  }
e835ae2
+	      }
e835ae2
+	    return false;
e835ae2
+	  }
e835ae2
 	break;
e835ae2
       case DW_OP_GNU_addr_index:
e835ae2
       case DW_OP_GNU_const_index:
e835ae2
-        {
e835ae2
-          if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
e835ae2
-               || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
e835ae2
-              && resolve_one_addr (&loc->dw_loc_oprnd1.val_entry->addr.rtl,
e835ae2
-                                   NULL))
e835ae2
-            return false;
e835ae2
-        }
e835ae2
-       break;
e835ae2
+	if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
e835ae2
+	     || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
e835ae2
+	    && resolve_one_addr (&loc->dw_loc_oprnd1.val_entry->addr.rtl,
e835ae2
+				 NULL))
e835ae2
+	  return false;
e835ae2
+	break;
e835ae2
       case DW_OP_const4u:
e835ae2
       case DW_OP_const8u:
e835ae2
 	if (loc->dtprel
e835ae2
@@ -22601,8 +22704,79 @@ resolve_addr (dw_die_ref die)
e835ae2
 	       || l->dw_loc_next != NULL)
e835ae2
 	      && !resolve_addr_in_expr (l))
e835ae2
 	    {
e835ae2
-              if (dwarf_split_debug_info)
e835ae2
-                remove_loc_list_addr_table_entries (l);
e835ae2
+	      if (dwarf_split_debug_info)
e835ae2
+		remove_loc_list_addr_table_entries (l);
e835ae2
+	      if (l != NULL
e835ae2
+		  && l->dw_loc_next == NULL
e835ae2
+		  && l->dw_loc_opc == DW_OP_addr
e835ae2
+		  && GET_CODE (l->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF
e835ae2
+		  && SYMBOL_REF_DECL (l->dw_loc_oprnd1.v.val_addr)
e835ae2
+		  && a->dw_attr == DW_AT_location)
e835ae2
+		{
e835ae2
+		  tree decl = SYMBOL_REF_DECL (l->dw_loc_oprnd1.v.val_addr);
e835ae2
+		  remove_AT (die, a->dw_attr);
e835ae2
+		  ix--;
e835ae2
+		  if (TREE_CODE (decl) == VAR_DECL
e835ae2
+		      && lookup_decl_die (decl) == die
e835ae2
+		      && !DECL_EXTERNAL (decl)
e835ae2
+		      && TREE_STATIC (decl)
e835ae2
+		      && DECL_INITIAL (decl)
e835ae2
+		      && !DECL_P (DECL_INITIAL (decl))
e835ae2
+		      && !get_AT (die, DW_AT_const_value))
e835ae2
+		    {
e835ae2
+		      tree init = DECL_INITIAL (decl);
e835ae2
+		      HOST_WIDE_INT offset = 0;
e835ae2
+		      /* For variables that have been optimized away and thus
e835ae2
+			 don't have a memory location, see if we can emit
e835ae2
+			 DW_AT_const_value instead.  */
e835ae2
+		      if (tree_add_const_value_attribute (die, init))
e835ae2
+			break;
e835ae2
+		      if (dwarf_strict)
e835ae2
+			break;
e835ae2
+		      STRIP_NOPS (init);
e835ae2
+		      if (TREE_CODE (init) == POINTER_PLUS_EXPR
e835ae2
+			  && host_integerp (TREE_OPERAND (init, 1), 0))
e835ae2
+			{
e835ae2
+			  offset = tree_low_cst (TREE_OPERAND (init, 1), 0);
e835ae2
+			  init = TREE_OPERAND (init, 0);
e835ae2
+			  STRIP_NOPS (init);
e835ae2
+			}
e835ae2
+		      if (TREE_CODE (init) != ADDR_EXPR)
e835ae2
+			break;
e835ae2
+		      if ((TREE_CODE (TREE_OPERAND (init, 0)) == STRING_CST
e835ae2
+			   && !TREE_ASM_WRITTEN (TREE_OPERAND (init, 0)))
e835ae2
+			  || (TREE_CODE (TREE_OPERAND (init, 0)) == VAR_DECL
e835ae2
+			      && !DECL_EXTERNAL (TREE_OPERAND (init, 0))
e835ae2
+			      && TREE_OPERAND (init, 0) != decl))
e835ae2
+			{
e835ae2
+			  dw_die_ref ref;
e835ae2
+			  tree decl;
e835ae2
+
e835ae2
+			  if (TREE_CODE (TREE_OPERAND (init, 0)) == STRING_CST)
e835ae2
+			    {
e835ae2
+			      rtx rtl
e835ae2
+				= string_cst_pool_decl (TREE_OPERAND (init, 0));
e835ae2
+			      if (!rtl)
e835ae2
+				break;
e835ae2
+			      decl = SYMBOL_REF_DECL (rtl);
e835ae2
+			    }
e835ae2
+			  else
e835ae2
+			    decl = TREE_OPERAND (init, 0);
e835ae2
+			  ref = lookup_decl_die (decl);
e835ae2
+			  if (ref == NULL
e835ae2
+			      || (!get_AT (ref, DW_AT_location)
e835ae2
+				  && !get_AT (ref, DW_AT_const_value)))
e835ae2
+			    break;
e835ae2
+			  l = new_loc_descr (DW_OP_GNU_implicit_pointer, 0,
e835ae2
+					     offset);
e835ae2
+			  l->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
e835ae2
+			  l->dw_loc_oprnd1.v.val_die_ref.die = ref;
e835ae2
+			  l->dw_loc_oprnd1.v.val_die_ref.external = 0;
e835ae2
+			  add_AT_loc (die, DW_AT_location, l);
e835ae2
+			}
e835ae2
+		    }
e835ae2
+		  break;
e835ae2
+		}
e835ae2
 	      remove_AT (die, a->dw_attr);
e835ae2
 	      ix--;
e835ae2
 	    }