This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
[RFC] Better debug info by substitution tracking for inliner (and other passes eliminating whole user variables)
- From: Jan Hubicka <hubicka at ucw dot cz>
- To: gcc at gcc dot gnu dot org, dan at debian dot org, rguenther at suse dot de, martinj at suse dot cz, aoliva at redhat dot com
- Date: Thu, 5 Mar 2009 11:54:31 +0100
- Subject: [RFC] Better debug info by substitution tracking for inliner (and other passes eliminating whole user variables)
Hi,
this patch resulted from attempt to solve regression we have in
gdb.opt/inline-locals.exp gdb testsuite and also problems with fact that when
clonning function by ipa-cp we lose any information on function argument.
(and yes, it solves it)
The gdb.opt/inline-locals.exp testsuite shows common pattern
func (argument)
{
}
otherfunc (otherargument)
{
func (otherargument);
}
Where we now fully replace ARGUMENT by OTHERARGUMENT when inlining while older
compilers actually substituted backwards, so ARGUMENT was available while
OTHERARGUMENT was not. This is quite irritating behaviour when trying to
debug since parameters of inlined functions tends to be lost.
The patch adds mechanizm for tracking inline substitutions, so we
actually note that on whole scope of its existence ARGUMENT is having
same value as OTHERARGUMENT. This is represented by extending
NONLOCALIZED_VAR vector to contain optimized out declaration and it's
replacement
I know that this is one of problems being solved on debug branch, but
there is problem with DEBUG_INSN approach and scalability for C++
programs. In tramp3d and similar testcases we have >10 inlined function
scopes for every instruction, so adding about 20 DEBUG_INSN markers into
stream for every instruction would mean that we will need 20 times more
declaratoins, statements, RTL instruction, track 20 times more variable
locations and we would have to do that -g or not. So if we want to have
DEBUG_INSN approach, those aproaches should combine themselves well:
inline substitution tracking will handle most cases chaply and
DEBUG_INSN will help to keep the substitutions valid.
Inlining is quite special, in particular:
1) it is probably only place in compiler capable of producing so much of
garbage as above
2) when I am substituting value by local value of function I inline into,
I know that the local value I inline into is going to stay constant in the
whole scope of function parameter
3) It affects backtraces that are probably most important :)
I make attempt in tree-inline to look up stable enough replacement in
simplify_replacement (i.e. walking SSA graph and trying to find something
that is not going to disappear, as function parameter, user variable
or address of user variable) and I am not making any attempts to update
replacement removing them when variables they refer to become dead
though I think we can do some job here.
The tracked locations looks like this (this part of dump of scope tree
of tramp3d's main listing used vars and unused vars. Nonlocalized vars
are vars completely replaced by inliner substitution and those we have
valid replacement for are mentioned as "replaced by")
{ Scope block #0 (unused)
{ Scope block #546633
struct Inform out;
struct Grid grid;
struct GridLayout layout;
struct UniformRectilinearMesh mesh;
struct GridLayout layout1;
struct UniformRectilinearMesh mesh1;
struct GridLayout layout0;
struct UniformRectilinearMesh mesh0;
struct Centering cell;
struct Centering face;
struct Centering edge;
struct Centering vertex;
struct Field rh;
struct Field v;
struct Field T;
struct Field pg;
struct Field ph;
struct Field cs;
double t;
struct Field scratchv;
struct Field scratchc;
struct Field scratchc_v;
struct Field scratchc_v2;
struct Field flm;
struct Field fle;
struct Field flvv;
struct Field flvc;
struct Field Tij;
struct Field cv;
struct Field dlmdlt;
struct Field xmue;
struct Field nue;
int it;
bool eeq; (unused)
bool ietot; (unused)
struct Field vint;
struct Field cent;
double rh0;
double T0;
struct timeval end_time;
struct timeval start_time;
double iteration_time;
{ Scope block #546634 (unused) tramp3d-v4.cpp:56084 Originating from : static void Grid (struct Grid *, int);
struct Grid * const this = struct Grid * const;replaced by:&grid (nonlocalized)
{ Scope block #546636 (unused) tramp3d-v4.cpp:12004 Originating from : static void Domain (struct Domain *, int);
struct Domain * const this = struct Domain * const;replaced by:&grid.D.171787 (nonlocalized)
{ Scope block #546637 (unused) tramp3d-v4.cpp:3271 Originating from : static void DomainBase (struct DomainBase *, int);
struct DomainBase * const this = struct DomainBase * const;replaced by:&grid.D.171787.D.171460 (nonlocalized)
{ Scope block #546639 (unused) tramp3d-v4.cpp:3010 Originating from : static void Grid (struct Grid *, int);
struct Grid * const this = struct Grid * const; (nonlocalized)
{ Scope block #546640 (unused) tramp3d-v4.cpp:12100 Originating from : static void Domain (struct Domain *, int);
struct Domain * const this = struct Domain * const; (nonlocalized)
{ Scope block #546641 (unused) tramp3d-v4.cpp:3253 Originating from : static void DomainBase (struct DomainBase *, int);
struct DomainBase * const this = struct DomainBase * const; (nonlocalized)
{ Scope block #546642 (unused) tramp3d-v4.cpp:3010 Originating from : static void IndirectionList (struct IndirectionList *, int);
struct IndirectionList * const this = struct IndirectionList * const; (nonlocalized)
{ Scope block #546643 Originating from :#0
}
}
}
}
}
}
}
}
{ Scope block #546644 (unused) tramp3d-v4.cpp:56084 Originating from : static void UniformRectilinearMesh (struct UniformRectilinearMesh *, int);
struct UniformRectilinearMesh * const this = struct UniformRectilinearMesh * const;replaced by:&mesh (nonlocalized)
{ Scope block #546646 (unused) tramp3d-v4.cpp:47358 Originating from : static void RefCountedPtr (struct RefCountedPtr *, int);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh.data_m (nonlocalized)
{ Scope block #546647 (unused) tramp3d-v4.cpp:6637 Originating from : static void invalidate (struct RefCountedPtr *);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh.data_m (nonlocalized)
{ Scope block #546648 Originating from :#0
{ Scope block #546649 (unused) tramp3d-v4.cpp:6630 Originating from : static bool removeRefAndCheckGarbage (struct RefCounted *);
struct RefCounted * const this = struct RefCounted * const;replaced by:&mesh.data_m.ptr_m->D.227190.D.227100 (nonlocalized)
{ Scope block #546650 Originating from :#72
bool test; (nonlocalized)
}
}
{ Scope block #546651 (unused) tramp3d-v4.cpp:6630 Originating from : static bool isValid (const struct RefCountedPtr *);
const struct RefCountedPtr * const this = const struct RefCountedPtr * const;replaced by:&mesh.data_m (nonlocalized)
{ Scope block #546652 Originating from :#0
}
}
}
}
}
}
{ Scope block #546653 (unused) tramp3d-v4.cpp:56084 Originating from : static void UniformRectilinearMesh (struct UniformRectilinearMesh *, int);
struct UniformRectilinearMesh * const this = struct UniformRectilinearMesh * const;replaced by:&mesh1 (nonlocalized)
{ Scope block #546655 (unused) tramp3d-v4.cpp:47358 Originating from : static void RefCountedPtr (struct RefCountedPtr *, int);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh1.data_m (nonlocalized)
{ Scope block #546656 (unused) tramp3d-v4.cpp:6637 Originating from : static void invalidate (struct RefCountedPtr *);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh1.data_m (nonlocalized)
{ Scope block #546657 Originating from :#0
{ Scope block #546658 (unused) tramp3d-v4.cpp:6630 Originating from : static bool removeRefAndCheckGarbage (struct RefCounted *);
struct RefCounted * const this = struct RefCounted * const;replaced by:&mesh1.data_m.ptr_m->D.227190.D.227100 (nonlocalized)
{ Scope block #546659 Originating from :#72
bool test; (nonlocalized)
}
}
{ Scope block #546660 (unused) tramp3d-v4.cpp:6630 Originating from : static bool isValid (const struct RefCountedPtr *);
const struct RefCountedPtr * const this = const struct RefCountedPtr * const;replaced by:&mesh1.data_m (nonlocalized)
{ Scope block #546661 Originating from :#0
}
}
}
}
}
}
{ Scope block #546662 (unused) tramp3d-v4.cpp:56084 Originating from : static void UniformRectilinearMesh (struct UniformRectilinearMesh *, int);
struct UniformRectilinearMesh * const this = struct UniformRectilinearMesh * const;replaced by:&mesh0 (nonlocalized)
{ Scope block #546664 (unused) tramp3d-v4.cpp:47358 Originating from : static void RefCountedPtr (struct RefCountedPtr *, int);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh0.data_m (nonlocalized)
{ Scope block #546665 (unused) tramp3d-v4.cpp:6637 Originating from : static void invalidate (struct RefCountedPtr *);
struct RefCountedPtr * const this = struct RefCountedPtr * const;replaced by:&mesh0.data_m (nonlocalized)
{ Scope block #546666 Originating from :#0
{ Scope block #546667 (unused) tramp3d-v4.cpp:6630 Originating from : static bool removeRefAndCheckGarbage (struct RefCounted *);
struct RefCounted * const this = struct RefCounted * const;replaced by:&mesh0.data_m.ptr_m->D.227190.D.227100 (nonlocalized)
{ Scope block #546668 Originating from :#72
bool test; (nonlocalized)
}
}
{ Scope block #546669 (unused) tramp3d-v4.cpp:6630 Originating from : static bool isValid (const struct RefCountedPtr *);
const struct RefCountedPtr * const this = const struct RefCountedPtr * const;replaced by:&mesh0.data_m (nonlocalized)
{ Scope block #546670 Originating from :#0
}
}
}
}
}
}
{ Scope block #546671 (unused) tramp3d-v4.cpp:56082 Originating from : static struct Inform & operator<< (struct Inform &, struct Inform & (*<T4c31>) (struct Inform &));
struct Inform & o = struct Inform &;replaced by:&out (nonlocalized)
struct Inform & (*<T4c31>) (struct Inform &) d = struct Inform & (*<T4c31>) (struct Inform &);replaced by:endl (nonlocalized)
{ Scope block #546673 (unused) tramp3d-v4.cpp:284 Originating from : static struct Inform & endl (struct Inform &);
struct Inform & inf = struct Inform &;replaced by:&out (nonlocalized)
{ Scope block #546674 Originating from :#0
}
}
}
{ Scope block #546675 (unused) tramp3d-v4.cpp:56082 Originating from : static struct Field operator* (const struct Field &, const struct Field &);
const struct Field & l = const struct Field &;replaced by:&rh (nonlocalized)
const struct Field & r = const struct Field &;replaced by:&T (nonlocalized)
{ Scope block #546677 (unused) Originating from :#130527
typedef struct Tree_t Tree_t; (unused)
{ Scope block #546678 (unused) tramp3d-v4.cpp:40104 Originating from : static struct Field make (const struct BinaryNode &);
const struct BinaryNode & tree = const struct BinaryNode &; (nonlocalized)
{ Scope block #546679 (unused) tramp3d-v4.cpp:39825 Originating from : static void Field (struct Field *, const struct Engine &);
struct Field * const this = struct Field * const; (nonlocalized)
const struct Engine & i1 = const struct Engine &;replaced by:&D.933789 (nonlocalized)
... so the moral is that was majority of eliminated variables comes from
inliner and there are actually a lot more of these than existing
variables.
At the end of tree-ssa pass I get relatively good coverage. Out of 79000
variables optimized out by inliner, 36000 still have valid replacement, so
about 30%. For comparsion, all the unused declarations together that are local
(optimized out vars, types etc.) sums to about 5000. There are about 2000
surviving user vars, so we improve chance of seeing random var in debugger by
1800% :) We do better on constants and THIS pointers than on random expressions.
By manualy inspecting the cases where replacement are lost, there
are either valid reasons (i.e. the variable becomes completely dead) or it was
eliminated by SRA/copyrename passes.
After early inlining there are locations for 115000 variables out of 279000 and
main drop is in SRA where a lot of THIS pointers are eliminated. SRA can
quite easilly made to update the replacements and actually drop replacements
for the SRAed out variables probably improving ratio quite a bit. Because SRA
interleave with inliner, this will improve early inliner scores too.
Another offender seems to by copyrename that in fact is other good example
where such substitutions can be very easilly tracked.
Finally when it comes to dwarf encoding the main problem seems to be that
dwarf insist on describing function value as location in memory. This is
not quite compatible with values passed by reference where we optimize out
the pointer to value and thus we still know address it points to but we no
longer have memory location for the pointer itself. Similarly when we replace
"this" by "outer_this->field" where field is not at the beggining, we are lost,
since we have only value of this in memory, not value of this+16.
Out of 35000 replacements we try to produce, 11000 would be expandable
if we had way to describe value of variable not just its address. There
is DW_OP_value in dwarf4 for that but it is not supported by GDB. As a
temporary hack I added code that actually unpeels REFERNECE_TYPE or
POINTER_TYPE from the type of variable so printing still works. It
makes backtraces look funny, as it tends to embedd whole THIS pointer in
the stack frame.
10000 expands as correct dwarf expressions (constants or locations), so
overall we get 60% of tracked substitutions communicated to debugger.
With disabling SRA one gets to about 50% of variables still tracked at
optimized dump and with additionally disabling copyrename it is about 60%
or 20% of all user variables.
Important fact is also that tracking is very cheap. We need extra pointer per
eliminated decl that does not show in stats at all. We often just share the
expression since those tends to be siple constants and decls. We build some
extra trees for the tracking expressions and some extra dwarf nodes. But
my current tree actually uses less memory on tramp3d because I improved
effectivity of substitution little bit.
So I wonder if there are some comments? If there is not significand opposition
relative to the approach, I would like to fix problems and commit it to
pretty-IPA branch where we can work with Martin to update new SRA pass to
handle SRAing. Since dwarf allows to express memory location consisting
of multiple places, it should be possible to fully track simple SRAed out
struct, we are however lost when we have pointer to those struct since there
is no means describing "there is no memory location for this pointer, but it
would be pointing to this structure" except for my aforementioned hack of unpeeling
references/pointers.
Comments?
Honza
Index: tree.h
===================================================================
*** tree.h (revision 144621)
--- tree.h (working copy)
*************** DEF_VEC_P(tree);
*** 191,196 ****
--- 191,205 ----
DEF_VEC_ALLOC_P(tree,gc);
DEF_VEC_ALLOC_P(tree,heap);
+ typedef struct nonlocalized_var GTY(())
+ {
+ tree decl;
+ tree replacement;
+ } nonlocalized_var;
+
+ DEF_VEC_O(nonlocalized_var);
+ DEF_VEC_ALLOC_O(nonlocalized_var,gc);
+
/* Classify which part of the compiler has defined a given builtin function.
Note that we assume below that this is no more than two bits. */
*************** struct varray_head_tag;
*** 1969,1976 ****
/* In a BLOCK node. */
#define BLOCK_VARS(NODE) (BLOCK_CHECK (NODE)->block.vars)
#define BLOCK_NONLOCALIZED_VARS(NODE) (BLOCK_CHECK (NODE)->block.nonlocalized_vars)
! #define BLOCK_NUM_NONLOCALIZED_VARS(NODE) VEC_length (tree, BLOCK_NONLOCALIZED_VARS (NODE))
! #define BLOCK_NONLOCALIZED_VAR(NODE,N) VEC_index (tree, BLOCK_NONLOCALIZED_VARS (NODE), N)
#define BLOCK_SUBBLOCKS(NODE) (BLOCK_CHECK (NODE)->block.subblocks)
#define BLOCK_SUPERCONTEXT(NODE) (BLOCK_CHECK (NODE)->block.supercontext)
/* Note: when changing this, make sure to find the places
--- 1978,1986 ----
/* In a BLOCK node. */
#define BLOCK_VARS(NODE) (BLOCK_CHECK (NODE)->block.vars)
#define BLOCK_NONLOCALIZED_VARS(NODE) (BLOCK_CHECK (NODE)->block.nonlocalized_vars)
! #define BLOCK_NUM_NONLOCALIZED_VARS(NODE) VEC_length (nonlocalized_var, BLOCK_NONLOCALIZED_VARS (NODE))
! #define BLOCK_NONLOCALIZED_VAR(NODE,N) VEC_index (nonlocalized_var, BLOCK_NONLOCALIZED_VARS (NODE), N)->decl
! #define BLOCK_NONLOCALIZED_VAR_REPLACEMENT(NODE,N) VEC_index (nonlocalized_var, BLOCK_NONLOCALIZED_VARS (NODE), N)->replacement
#define BLOCK_SUBBLOCKS(NODE) (BLOCK_CHECK (NODE)->block.subblocks)
#define BLOCK_SUPERCONTEXT(NODE) (BLOCK_CHECK (NODE)->block.supercontext)
/* Note: when changing this, make sure to find the places
*************** struct tree_block GTY(())
*** 2025,2031 ****
location_t locus;
tree vars;
! VEC(tree,gc) *nonlocalized_vars;
tree subblocks;
tree supercontext;
--- 2035,2041 ----
location_t locus;
tree vars;
! VEC(nonlocalized_var,gc) *nonlocalized_vars;
tree subblocks;
tree supercontext;
Index: final.c
===================================================================
*** final.c (revision 144621)
--- final.c (working copy)
*************** struct rtl_opt_pass pass_final =
*** 4220,4226 ****
{
{
RTL_PASS,
! NULL, /* name */
NULL, /* gate */
rest_of_handle_final, /* execute */
NULL, /* sub */
--- 4220,4226 ----
{
{
RTL_PASS,
! "final", /* name */
NULL, /* gate */
rest_of_handle_final, /* execute */
NULL, /* sub */
Index: dwarf2out.c
===================================================================
*** dwarf2out.c (revision 144621)
--- dwarf2out.c (working copy)
*************** along with GCC; see the file COPYING3.
*** 89,94 ****
--- 89,95 ----
#include "hashtab.h"
#include "cgraph.h"
#include "input.h"
+ #include "gimple.h"
#ifdef DWARF2_DEBUGGING_INFO
static void dwarf2out_source_line (unsigned int, const char *);
*************** static HOST_WIDE_INT field_byte_offset (
*** 5112,5125 ****
static void add_AT_location_description (dw_die_ref, enum dwarf_attribute,
dw_loc_descr_ref);
static void add_data_member_location_attribute (dw_die_ref, tree);
- static void add_const_value_attribute (dw_die_ref, rtx);
static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *);
static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
static void insert_float (const_rtx, unsigned char *);
static rtx rtl_for_decl_location (tree);
! static void add_location_or_const_value_attribute (dw_die_ref, tree,
enum dwarf_attribute);
! static void tree_add_const_value_attribute (dw_die_ref, tree);
static void add_name_attribute (dw_die_ref, const char *);
static void add_comp_dir_attribute (dw_die_ref);
static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree);
--- 5113,5125 ----
static void add_AT_location_description (dw_die_ref, enum dwarf_attribute,
dw_loc_descr_ref);
static void add_data_member_location_attribute (dw_die_ref, tree);
static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *);
static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
static void insert_float (const_rtx, unsigned char *);
static rtx rtl_for_decl_location (tree);
! static bool add_location_or_const_value_attribute (dw_die_ref, tree,
enum dwarf_attribute);
! static bool tree_add_const_value_attribute (dw_die_ref, tree);
static void add_name_attribute (dw_die_ref, const char *);
static void add_comp_dir_attribute (dw_die_ref);
static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree);
*************** static void gen_inlined_enumeration_type
*** 5153,5163 ****
static void gen_inlined_structure_type_die (tree, dw_die_ref);
static void gen_inlined_union_type_die (tree, dw_die_ref);
static dw_die_ref gen_enumeration_type_die (tree, dw_die_ref);
! static dw_die_ref gen_formal_parameter_die (tree, tree, dw_die_ref);
static void gen_unspecified_parameters_die (tree, dw_die_ref);
static void gen_formal_types_die (tree, dw_die_ref);
static void gen_subprogram_die (tree, dw_die_ref);
! static void gen_variable_die (tree, tree, dw_die_ref);
static void gen_const_die (tree, dw_die_ref);
static void gen_label_die (tree, dw_die_ref);
static void gen_lexical_block_die (tree, dw_die_ref, int);
--- 5153,5163 ----
static void gen_inlined_structure_type_die (tree, dw_die_ref);
static void gen_inlined_union_type_die (tree, dw_die_ref);
static dw_die_ref gen_enumeration_type_die (tree, dw_die_ref);
! static dw_die_ref gen_formal_parameter_die (tree, tree, dw_die_ref, tree);
static void gen_unspecified_parameters_die (tree, dw_die_ref);
static void gen_formal_types_die (tree, dw_die_ref);
static void gen_subprogram_die (tree, dw_die_ref);
! static void gen_variable_die (tree, tree, dw_die_ref, tree);
static void gen_const_die (tree, dw_die_ref);
static void gen_label_die (tree, dw_die_ref);
static void gen_lexical_block_die (tree, dw_die_ref, int);
*************** static void gen_block_die (tree, dw_die_
*** 5177,5183 ****
static void decls_for_scope (tree, dw_die_ref, int);
static int is_redundant_typedef (const_tree);
static void gen_namespace_die (tree);
! static void gen_decl_die (tree, tree, dw_die_ref);
static dw_die_ref force_decl_die (tree);
static dw_die_ref force_type_die (tree);
static dw_die_ref setup_namespace_context (tree, dw_die_ref);
--- 5177,5183 ----
static void decls_for_scope (tree, dw_die_ref, int);
static int is_redundant_typedef (const_tree);
static void gen_namespace_die (tree);
! static void gen_decl_die (tree, tree, dw_die_ref, tree);
static dw_die_ref force_decl_die (tree);
static dw_die_ref force_type_die (tree);
static dw_die_ref setup_namespace_context (tree, dw_die_ref);
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10373,10384 ****
return 0;
case ADDR_EXPR:
! /* If we already want an address, there's nothing we can do. */
if (want_address)
! return 0;
!
/* Otherwise, process the argument and look for the address. */
! return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 1);
case VAR_DECL:
if (DECL_THREAD_LOCAL_P (loc))
--- 10373,10408 ----
return 0;
case ADDR_EXPR:
! /* If we already want an address, see if there is INDIRECT_REF inside
! e.g. for &this->field. */
if (want_address)
! {
! tree obj, offset;
! HOST_WIDE_INT bitsize, bitpos;
! enum machine_mode mode;
! int volatilep;
! int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc));
!
! obj = get_inner_reference (TREE_OPERAND (loc, 0),
! &bitsize, &bitpos, &offset, &mode,
! &unsignedp, &volatilep, false);
! STRIP_NOPS (obj);
! if (INDIRECT_REF_P (obj)
! && !bitpos && !offset)
! {
! ret = loc_descriptor_from_tree_1 (TREE_OPERAND (obj, 0), want_address);
! have_address = 1;
! }
! /* Try luck and perhaps find the address available in constant pool. */
! else if (is_gimple_ip_invariant_address (loc))
! goto cst_address;
! else
! return NULL;
! }
/* Otherwise, process the argument and look for the address. */
! else
! return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 1);
! break;
case VAR_DECL:
if (DECL_THREAD_LOCAL_P (loc))
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10445,10451 ****
rtx rtl = rtl_for_decl_location (loc);
if (rtl == NULL_RTX)
! return 0;
else if (GET_CODE (rtl) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (rtl);
--- 10469,10483 ----
rtx rtl = rtl_for_decl_location (loc);
if (rtl == NULL_RTX)
! {
! if (dump_file)
! {
! fprintf (dump_file, "Expansion for decl failed. No RTL ");
! print_generic_decl (dump_file, loc, 0);
! fprintf (dump_file, "\n");
! }
! return 0;
! }
else if (GET_CODE (rtl) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (rtl);
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10481,10486 ****
--- 10513,10520 ----
break;
case INDIRECT_REF:
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
have_address = 1;
break;
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10498,10503 ****
--- 10532,10539 ----
case BIT_FIELD_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
{
tree obj, offset;
HOST_WIDE_INT bitsize, bitpos, bytepos;
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10540,10563 ****
}
case INTEGER_CST:
if (host_integerp (loc, 0))
ret = int_loc_descriptor (tree_low_cst (loc, 0));
else
return 0;
break;
case CONSTRUCTOR:
{
/* Get an RTL for this, if something has been emitted. */
rtx rtl = lookup_constant_def (loc);
enum machine_mode mode;
if (!rtl || !MEM_P (rtl))
! return 0;
mode = GET_MODE (rtl);
rtl = XEXP (rtl, 0);
ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
have_address = 1;
break;
}
--- 10576,10624 ----
}
case INTEGER_CST:
+ /* With inlining we tend to create expressions like &0 as variable replacements
+ (each time we took address of variable, the address operation was optimized out
+ and variable replaced by constant.)
+ We might get lucky with constant pool. */
+ if (want_address)
+ goto cst_address;
if (host_integerp (loc, 0))
ret = int_loc_descriptor (tree_low_cst (loc, 0));
else
return 0;
break;
+ case REAL_CST:
+ case STRING_CST:
+ if (want_address)
+ goto cst_address;
+ /* We can handle STRING CSTs. */
+ gcc_unreachable ();
+
case CONSTRUCTOR:
+ cst_address:
+ return 0;
{
/* Get an RTL for this, if something has been emitted. */
rtx rtl = lookup_constant_def (loc);
enum machine_mode mode;
if (!rtl || !MEM_P (rtl))
! {
! gcc_assert (!rtl);
! if (dump_file)
! {
! fprintf (dump_file, "Failed to find constant expr in constant pool ");
! print_generic_expr (dump_file, loc, 0);
! fprintf (dump_file, "\n");
! }
! return 0;
! }
mode = GET_MODE (rtl);
rtl = XEXP (rtl, 0);
ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
have_address = 1;
+ debug_tree (loc);
break;
}
*************** loc_descriptor_from_tree_1 (tree loc, in
*** 10753,10758 ****
--- 10814,10820 ----
#ifdef ENABLE_CHECKING
/* Otherwise this is a generic code; we should just lists all of
these explicitly. We forgot one. */
+ debug_tree (loc);
gcc_unreachable ();
#else
/* In a release build, we want to degrade gracefully: better to
*************** insert_float (const_rtx rtl, unsigned ch
*** 11149,11155 ****
to an inlined function. They can also arise in C++ where declared
constants do not necessarily get memory "homes". */
! static void
add_const_value_attribute (dw_die_ref die, rtx rtl)
{
switch (GET_CODE (rtl))
--- 11211,11217 ----
to an inlined function. They can also arise in C++ where declared
constants do not necessarily get memory "homes". */
! static bool
add_const_value_attribute (dw_die_ref die, rtx rtl)
{
switch (GET_CODE (rtl))
*************** add_const_value_attribute (dw_die_ref di
*** 11283,11295 ****
*value* which the artificial local variable always has during its
lifetime. We currently have no way to represent such quasi-constant
values in Dwarf, so for now we just punt and generate nothing. */
break;
default:
/* No other kinds of rtx should be possible here. */
gcc_unreachable ();
}
!
}
/* Determine whether the evaluation of EXPR references any variables
--- 11345,11358 ----
*value* which the artificial local variable always has during its
lifetime. We currently have no way to represent such quasi-constant
values in Dwarf, so for now we just punt and generate nothing. */
+ return false;
break;
default:
/* No other kinds of rtx should be possible here. */
gcc_unreachable ();
}
! return true;
}
/* Determine whether the evaluation of EXPR references any variables
*************** loc_by_reference (dw_loc_descr_ref loc,
*** 11736,11742 ****
pointer. This can happen for example if an actual argument in an inlined
function call evaluates to a compile-time constant address. */
! static void
add_location_or_const_value_attribute (dw_die_ref die, tree decl,
enum dwarf_attribute attr)
{
--- 11799,11805 ----
pointer. This can happen for example if an actual argument in an inlined
function call evaluates to a compile-time constant address. */
! static bool
add_location_or_const_value_attribute (dw_die_ref die, tree decl,
enum dwarf_attribute attr)
{
*************** add_location_or_const_value_attribute (d
*** 11745,11751 ****
var_loc_list *loc_list;
struct var_loc_node *node;
if (TREE_CODE (decl) == ERROR_MARK)
! return;
gcc_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL);
--- 11808,11814 ----
var_loc_list *loc_list;
struct var_loc_node *node;
if (TREE_CODE (decl) == ERROR_MARK)
! return false;
gcc_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL);
*************** add_location_or_const_value_attribute (d
*** 11825,11831 ****
/* Finally, add the location list to the DIE, and we are done. */
add_AT_loc_list (die, attr, list);
! return;
}
/* Try to get some constant RTL for this decl, and use that as the value of
--- 11888,11894 ----
/* Finally, add the location list to the DIE, and we are done. */
add_AT_loc_list (die, attr, list);
! return true;
}
/* Try to get some constant RTL for this decl, and use that as the value of
*************** add_location_or_const_value_attribute (d
*** 11834,11841 ****
rtl = rtl_for_decl_location (decl);
if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING))
{
! add_const_value_attribute (die, rtl);
! return;
}
/* If we have tried to generate the location otherwise, and it
--- 11897,11903 ----
rtl = rtl_for_decl_location (decl);
if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING))
{
! return add_const_value_attribute (die, rtl);
}
/* If we have tried to generate the location otherwise, and it
*************** add_location_or_const_value_attribute (d
*** 11851,11857 ****
{
descr = loc_by_reference (descr, decl);
add_AT_location_description (die, attr, descr);
! return;
}
}
--- 11913,11919 ----
{
descr = loc_by_reference (descr, decl);
add_AT_location_description (die, attr, descr);
! return true;
}
}
*************** add_location_or_const_value_attribute (d
*** 11862,11872 ****
{
descr = loc_by_reference (descr, decl);
add_AT_location_description (die, attr, descr);
! return;
}
/* None of that worked, so it must not really have a location;
try adding a constant value attribute from the DECL_INITIAL. */
! tree_add_const_value_attribute (die, decl);
}
/* Add VARIABLE and DIE into deferred locations list. */
--- 11924,11934 ----
{
descr = loc_by_reference (descr, decl);
add_AT_location_description (die, attr, descr);
! return true;
}
/* None of that worked, so it must not really have a location;
try adding a constant value attribute from the DECL_INITIAL. */
! return tree_add_const_value_attribute (die, decl);
}
/* Add VARIABLE and DIE into deferred locations list. */
*************** native_encode_initializer (tree init, un
*** 12027,12068 ****
}
}
/* If we don't have a copy of this variable in memory for some reason (such
as a C++ member constant that doesn't have an out-of-line definition),
we should tell the debugger about the constant value. */
! static void
tree_add_const_value_attribute (dw_die_ref var_die, tree decl)
{
tree init;
tree type = TREE_TYPE (decl);
- rtx rtl;
if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL)
! return;
init = DECL_INITIAL (decl);
if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl) && init)
/* OK */;
else
! return;
! rtl = rtl_for_decl_init (init, type);
! if (rtl)
! add_const_value_attribute (var_die, rtl);
! /* If the host and target are sane, try harder. */
! else if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
! && initializer_constant_valid_p (init, type))
{
! HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (init));
! if (size > 0 && (int) size == size)
{
! unsigned char *array = GGC_CNEWVEC (unsigned char, size);
! if (native_encode_initializer (init, array, size))
! add_AT_vec (var_die, DW_AT_const_value, size, 1, array);
}
}
}
/* Convert the CFI instructions for the current function into a
--- 12089,12324 ----
}
}
+ /* Try to produce constant value for INIT. Return true if sucesful. */
+
+ static bool
+ tree_add_const_value_attribute_1 (dw_die_ref var_die, tree type, tree init)
+ {
+ rtx rtl = rtl_for_decl_init (init, type);
+ if (rtl)
+ {
+ add_const_value_attribute (var_die, rtl);
+ return true;
+ }
+ /* If the host and target are sane, try harder. */
+ else if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
+ && initializer_constant_valid_p (init, type))
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (init));
+ if (size > 0 && (int) size == size)
+ {
+ unsigned char *array = GGC_CNEWVEC (unsigned char, size);
+
+ if (native_encode_initializer (init, array, size))
+ {
+ add_AT_vec (var_die, DW_AT_const_value, size, 1, array);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/* If we don't have a copy of this variable in memory for some reason (such
as a C++ member constant that doesn't have an out-of-line definition),
we should tell the debugger about the constant value. */
! static bool
tree_add_const_value_attribute (dw_die_ref var_die, tree decl)
{
tree init;
tree type = TREE_TYPE (decl);
if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL)
! return false;
init = DECL_INITIAL (decl);
if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl) && init)
/* OK */;
else
! return false;
! return tree_add_const_value_attribute_1 (var_die, type, init);
! }
! static tree
! add_replacement (dw_die_ref var_die, tree decl, tree init, dw_die_ref context_die)
! {
! tree type = TREE_TYPE (decl);
! dw_loc_descr_ref loc;
! tree ref_init = NULL;
! tree old_init = NULL;
! bool by_reference = false;
! tree real_type = TREE_TYPE (decl);
!
! if (!init)
! return NULL;
! if (!reload_completed)
! return NULL;
! if ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL)
! && DECL_BY_REFERENCE (decl))
! by_reference = true, real_type = TREE_TYPE (decl);
!
! if (dump_file)
! {
! fprintf (dump_file, "Expanding replacement for decl ");
! print_generic_decl (dump_file, decl, 0);
! fprintf (dump_file, " \n replaced by ");
! print_generic_expr (dump_file, init, 0);
! fprintf (dump_file, "\n");
! }
! init = fold (init);
!
! /* Until DW_OP_value was added to DWARF4 there was no means of expanding
! value of variable as an expression based on other variables and still
! the expressive power is limited to pointer and integer types.
!
! We want to find either compile time constant or pointer to the value
! resisting in meory. So try to do smart job on variables passed by
! reference where there is some chance that the address itself is still
! stored somewhere. */
!
! if (by_reference && TREE_CODE (init) == ADDR_EXPR)
! ref_init = init, init = TREE_OPERAND (init, 0), by_reference = false;
! if (!by_reference && tree_add_const_value_attribute_1 (var_die, type, init))
{
! if (dump_file)
{
! fprintf (dump_file, "Expanded as constant\n");
! print_die (var_die, dump_file);
! }
! return NULL;
! }
! /* loc_descriptor_from_tree_1 is not able to handle location lists.
! It is a lot better to try to figure out if the value of variable
! is exactly same as value of other variable. In that case
! we can use location list of that other variable. */
! while (old_init != init)
! {
! tree obj, offset;
! HOST_WIDE_INT bitsize, bitpos;
! enum machine_mode mode;
! int volatilep;
! int unsignedp = TYPE_UNSIGNED (TREE_TYPE (init));
!
! old_init = init;
!
! STRIP_NOPS (init);
! init = fold (init);
! if (by_reference && TREE_CODE (init) == ADDR_EXPR)
! init = TREE_OPERAND (init, 0), by_reference = false;
! else if (DECL_P (init) && DECL_HAS_VALUE_EXPR_P (init))
! init = DECL_VALUE_EXPR (init);
! /* If we already want an address, see if there is INDIRECT_REF inside
! e.g. for &this->field. */
! else if (TREE_CODE (init) == ADDR_EXPR)
! {
! obj = get_inner_reference (TREE_OPERAND (init, 0),
! &bitsize, &bitpos, &offset, &mode,
! &unsignedp, &volatilep, false);
! STRIP_NOPS (obj);
! if (INDIRECT_REF_P (obj))
! {
! if (!bitpos && !offset)
! init = TREE_OPERAND (obj, 0);
! else if (dump_file)
! fprintf (dump_file, "Nonzero offset in component reference\n");
! }
}
+ /* Skip component references that does not change address. */
+ else if ((obj = get_inner_reference (init,
+ &bitsize, &bitpos, &offset, &mode,
+ &unsignedp, &volatilep, false)) != NULL_TREE
+ && obj != init
+ && !bitpos && !offset)
+ init = obj;
+ /* See if expression is simple enough so it is equivalent to other variable.
+ We should not output constants here; these would be wrong since we removed
+ casts. */
+ else if ((TREE_CODE (init) == PARM_DECL || TREE_CODE (init) == RESULT_DECL || TREE_CODE (init) == VAR_DECL)
+ && !by_reference
+ && add_location_or_const_value_attribute (var_die, init, DW_AT_location))
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Expanded as direct location\n");
+ print_die (var_die, dump_file);
+ }
+ return NULL;
+ }
+ else if (by_reference)
+ ref_init = init, init = build_fold_indirect_ref (init), by_reference = false;
+ if (dump_file && init != old_init)
+ {
+ fprintf (dump_file, " simplified as ");
+ print_generic_expr (dump_file, init, 0);
+ fprintf (dump_file, "\n");
+ }
+ }
+ gcc_assert (!by_reference);
+ if (ref_init)
+ loc = loc_descriptor_from_tree_1 (ref_init, 0);
+ else
+ loc = NULL;
+ if (!loc)
+ loc = loc_descriptor_from_tree_1 (init, 2);
+ if (loc)
+ {
+ add_AT_location_description (var_die, DW_AT_location, loc);
+ if (dump_file)
+ {
+ fprintf (dump_file, "Expanded as tree location expression\n");
+ print_die (var_die, dump_file);
+ }
+ return NULL;
+ }
+
+ /* A grand hack. We can not find value being readilly in memory, but we still can compute
+ the value. If the type is pointer or reference, we can simply turn it into the referenced
+ type and get values output in debugger correctly. */
+ if ((TREE_CODE (real_type) == REFERENCE_TYPE
+ || TREE_CODE (real_type) == POINTER_TYPE)
+ && TREE_CODE (TREE_TYPE (real_type)) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (real_type)) != METHOD_TYPE)
+ {
+ tree ref = build_fold_indirect_ref (init);
+ bool added = false;
+ if (TREE_CODE (ref) == PARM_DECL || TREE_CODE (ref) == RESULT_DECL || TREE_CODE (ref) == VAR_DECL)
+ added = add_location_or_const_value_attribute (var_die, ref, DW_AT_location);
+ if (!added)
+ loc = loc_descriptor_from_tree_1 (init, 0);
+ else
+ loc = NULL;
+ if (loc || added)
+ {
+ if (TREE_CODE (real_type) == REFERENCE_TYPE
+ || TREE_CODE (real_type) == POINTER_TYPE)
+ {
+ add_type_attribute (var_die, TREE_TYPE (real_type),
+ TREE_READONLY (decl),
+ TREE_THIS_VOLATILE (decl),
+ context_die);
+ if (!added)
+ add_AT_location_description (var_die, DW_AT_location, loc);
+ if (dump_file)
+ fprintf (dump_file, "Dropped reference type and expanded %s\n",
+ added ? "direct location" : "tree location");
+ return TREE_TYPE (real_type);
+ }
+ else if (dump_file)
+ fprintf (dump_file, "Can expand as expression\n");
+ }
+ }
+ #if 0
+ if (loc)
+ {
+ add_AT_location_description (var_die, DW_AT_const_value, loc);
+ return;
}
+ #endif
+ if (dump_file)
+ fprintf (dump_file, "Failed to expand\n");
+ return NULL_TREE;
}
/* Convert the CFI instructions for the current function into a
*************** descr_info_loc (tree val, tree base_decl
*** 12971,12976 ****
--- 13227,13234 ----
return int_loc_descriptor (tree_low_cst (val, 0));
break;
case INDIRECT_REF:
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
size = int_size_in_bytes (TREE_TYPE (val));
if (size < 0)
break;
*************** gen_enumeration_type_die (tree type, dw_
*** 13293,13299 ****
argument type of some subprogram type. */
static dw_die_ref
! gen_formal_parameter_die (tree node, tree origin, dw_die_ref context_die)
{
tree node_or_origin = node ? node : origin;
dw_die_ref parm_die
--- 13551,13558 ----
argument type of some subprogram type. */
static dw_die_ref
! gen_formal_parameter_die (tree node, tree origin, dw_die_ref context_die,
! tree replacement)
{
tree node_or_origin = node ? node : origin;
dw_die_ref parm_die
*************** gen_formal_parameter_die (tree node, tre
*** 13325,13333 ****
if (node)
equate_decl_number_to_die (node, parm_die);
if (! DECL_ABSTRACT (node_or_origin))
! add_location_or_const_value_attribute (parm_die, node_or_origin,
! DW_AT_location);
!
break;
case tcc_type:
--- 13584,13597 ----
if (node)
equate_decl_number_to_die (node, parm_die);
if (! DECL_ABSTRACT (node_or_origin))
! {
! if (replacement)
! add_replacement (parm_die, node_or_origin, replacement, context_die);
! else if (TREE_STATIC (node_or_origin) || node)
! add_location_or_const_value_attribute (parm_die,
! node_or_origin,
! DW_AT_location);
! }
break;
case tcc_type:
*************** gen_formal_types_die (tree function_or_m
*** 13385,13391 ****
break;
/* Output a (nameless) DIE to represent the formal parameter itself. */
! parm_die = gen_formal_parameter_die (formal_type, NULL, context_die);
if ((TREE_CODE (function_or_method_type) == METHOD_TYPE
&& link == first_parm_type)
|| (arg && DECL_ARTIFICIAL (arg)))
--- 13649,13655 ----
break;
/* Output a (nameless) DIE to represent the formal parameter itself. */
! parm_die = gen_formal_parameter_die (formal_type, NULL, context_die, NULL_TREE);
if ((TREE_CODE (function_or_method_type) == METHOD_TYPE
&& link == first_parm_type)
|| (arg && DECL_ARTIFICIAL (arg)))
*************** gen_type_die_for_member (tree type, tree
*** 13445,13451 ****
}
}
else
! gen_variable_die (member, NULL_TREE, type_die);
pop_decl_scope ();
}
--- 13709,13715 ----
}
}
else
! gen_variable_die (member, NULL_TREE, type_die, NULL_TREE);
pop_decl_scope ();
}
*************** gen_subprogram_die (tree decl, dw_die_re
*** 13790,13796 ****
"__builtin_va_alist"))
gen_unspecified_parameters_die (parm, subr_die);
else
! gen_decl_die (parm, NULL, subr_die);
}
/* Decide whether we need an unspecified_parameters DIE at the end.
--- 14054,14060 ----
"__builtin_va_alist"))
gen_unspecified_parameters_die (parm, subr_die);
else
! gen_decl_die (parm, NULL, subr_die, NULL);
}
/* Decide whether we need an unspecified_parameters DIE at the end.
*************** gen_subprogram_die (tree decl, dw_die_re
*** 13832,13838 ****
{
/* Emit a DW_TAG_variable DIE for a named return value. */
if (DECL_NAME (DECL_RESULT (decl)))
! gen_decl_die (DECL_RESULT (decl), NULL, subr_die);
current_function_has_inlines = 0;
decls_for_scope (outer_scope, subr_die, 0);
--- 14096,14102 ----
{
/* Emit a DW_TAG_variable DIE for a named return value. */
if (DECL_NAME (DECL_RESULT (decl)))
! gen_decl_die (DECL_RESULT (decl), NULL, subr_die, NULL);
current_function_has_inlines = 0;
decls_for_scope (outer_scope, subr_die, 0);
*************** common_block_die_table_eq (const void *x
*** 13878,13884 ****
Either DECL or ORIGIN must be non-null. */
static void
! gen_variable_die (tree decl, tree origin, dw_die_ref context_die)
{
HOST_WIDE_INT off;
tree com_decl;
--- 14142,14148 ----
Either DECL or ORIGIN must be non-null. */
static void
! gen_variable_die (tree decl, tree origin, dw_die_ref context_die, tree replacement)
{
HOST_WIDE_INT off;
tree com_decl;
*************** gen_variable_die (tree decl, tree origin
*** 14087,14093 ****
if (TREE_CODE (decl_or_origin) == VAR_DECL && TREE_STATIC (decl_or_origin)
&& !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl_or_origin)))
defer_location (decl_or_origin, var_die);
! else
add_location_or_const_value_attribute (var_die,
decl_or_origin,
DW_AT_location);
--- 14351,14359 ----
if (TREE_CODE (decl_or_origin) == VAR_DECL && TREE_STATIC (decl_or_origin)
&& !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl_or_origin)))
defer_location (decl_or_origin, var_die);
! else if (replacement)
! add_replacement (var_die, decl_or_origin, replacement, context_die);
! else if (TREE_STATIC (decl_or_origin) || decl)
add_location_or_const_value_attribute (var_die,
decl_or_origin,
DW_AT_location);
*************** gen_variable_die (tree decl, tree origin
*** 14095,14100 ****
--- 14361,14368 ----
}
else
tree_add_const_value_attribute (var_die, decl_or_origin);
+ if (replacement)
+ add_replacement (var_die, decl_or_origin, replacement, context_die);
}
/* Generate a DIE to represent a named constant. */
*************** gen_member_die (tree type, dw_die_ref co
*** 14477,14483 ****
if (child)
splice_child_die (context_die, child);
else
! gen_decl_die (member, NULL, context_die);
}
/* Now output info about the function members (if any). */
--- 14745,14751 ----
if (child)
splice_child_die (context_die, child);
else
! gen_decl_die (member, NULL, context_die, NULL);
}
/* Now output info about the function members (if any). */
*************** gen_member_die (tree type, dw_die_ref co
*** 14491,14497 ****
if (child)
splice_child_die (context_die, child);
else
! gen_decl_die (member, NULL, context_die);
}
}
--- 14759,14765 ----
if (child)
splice_child_die (context_die, child);
else
! gen_decl_die (member, NULL, context_die, NULL);
}
}
*************** gen_type_die_with_usage (tree type, dw_d
*** 14666,14672 ****
gcc_assert (DECL_ORIGINAL_TYPE (TYPE_NAME (type)) != type);
TREE_ASM_WRITTEN (type) = 1;
! gen_decl_die (TYPE_NAME (type), NULL, context_die);
return;
}
--- 14934,14940 ----
gcc_assert (DECL_ORIGINAL_TYPE (TYPE_NAME (type)) != type);
TREE_ASM_WRITTEN (type) = 1;
! gen_decl_die (TYPE_NAME (type), NULL, context_die, NULL);
return;
}
*************** gen_block_die (tree stmt, dw_die_ref con
*** 14936,14942 ****
/* Process variable DECL (or variable with origin ORIGIN) within
block STMT and add it to CONTEXT_DIE. */
static void
! process_scope_var (tree stmt, tree decl, tree origin, dw_die_ref context_die)
{
dw_die_ref die;
tree decl_or_origin = decl ? decl : origin;
--- 15204,15211 ----
/* Process variable DECL (or variable with origin ORIGIN) within
block STMT and add it to CONTEXT_DIE. */
static void
! process_scope_var (tree stmt, tree decl, tree origin, dw_die_ref context_die,
! tree replacement)
{
dw_die_ref die;
tree decl_or_origin = decl ? decl : origin;
*************** process_scope_var (tree stmt, tree decl,
*** 14959,14965 ****
dwarf2out_imported_module_or_decl_1 (decl_or_origin, DECL_NAME (decl_or_origin),
stmt, context_die);
else
! gen_decl_die (decl, origin, context_die);
}
/* Generate all of the decls declared within a given scope and (recursively)
--- 15228,15234 ----
dwarf2out_imported_module_or_decl_1 (decl_or_origin, DECL_NAME (decl_or_origin),
stmt, context_die);
else
! gen_decl_die (decl, origin, context_die, replacement);
}
/* Generate all of the decls declared within a given scope and (recursively)
*************** decls_for_scope (tree stmt, dw_die_ref c
*** 14981,14990 ****
sub-blocks. Also, nested function and tag DIEs have been
generated with a parent of NULL; fix that up now. */
for (decl = BLOCK_VARS (stmt); decl != NULL; decl = TREE_CHAIN (decl))
! process_scope_var (stmt, decl, NULL_TREE, context_die);
for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i),
! context_die);
/* If we're at -g1, we're not interested in subblocks. */
if (debug_info_level <= DINFO_LEVEL_TERSE)
--- 15250,15259 ----
sub-blocks. Also, nested function and tag DIEs have been
generated with a parent of NULL; fix that up now. */
for (decl = BLOCK_VARS (stmt); decl != NULL; decl = TREE_CHAIN (decl))
! process_scope_var (stmt, decl, NULL_TREE, context_die, NULL_TREE);
for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i),
! context_die, BLOCK_NONLOCALIZED_VAR_REPLACEMENT (stmt, i));
/* If we're at -g1, we're not interested in subblocks. */
if (debug_info_level <= DINFO_LEVEL_TERSE)
*************** force_decl_die (tree decl)
*** 15067,15073 ****
gen_decl_die() call. */
saved_external_flag = DECL_EXTERNAL (decl);
DECL_EXTERNAL (decl) = 1;
! gen_decl_die (decl, NULL, context_die);
DECL_EXTERNAL (decl) = saved_external_flag;
break;
--- 15336,15342 ----
gen_decl_die() call. */
saved_external_flag = DECL_EXTERNAL (decl);
DECL_EXTERNAL (decl) = 1;
! gen_decl_die (decl, NULL, context_die, NULL);
DECL_EXTERNAL (decl) = saved_external_flag;
break;
*************** declare_in_namespace (tree thing, dw_die
*** 15150,15156 ****
if (is_fortran ())
return ns_context;
if (DECL_P (thing))
! gen_decl_die (thing, NULL, ns_context);
else
gen_type_die (thing, ns_context);
}
--- 15419,15425 ----
if (is_fortran ())
return ns_context;
if (DECL_P (thing))
! gen_decl_die (thing, NULL, ns_context, NULL);
else
gen_type_die (thing, ns_context);
}
*************** gen_namespace_die (tree decl)
*** 15201,15207 ****
/* Generate Dwarf debug information for a decl described by DECL. */
static void
! gen_decl_die (tree decl, tree origin, dw_die_ref context_die)
{
tree decl_or_origin = decl ? decl : origin;
tree class_origin = NULL;
--- 15470,15476 ----
/* Generate Dwarf debug information for a decl described by DECL. */
static void
! gen_decl_die (tree decl, tree origin, dw_die_ref context_die, tree replacement)
{
tree decl_or_origin = decl ? decl : origin;
tree class_origin = NULL;
*************** gen_decl_die (tree decl, tree origin, dw
*** 15353,15361 ****
if (!origin)
origin = decl_ultimate_origin (decl);
if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL)
! gen_formal_parameter_die (decl, origin, context_die);
else
! gen_variable_die (decl, origin, context_die);
break;
case FIELD_DECL:
--- 15622,15630 ----
if (!origin)
origin = decl_ultimate_origin (decl);
if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL)
! gen_formal_parameter_die (decl, origin, context_die, replacement);
else
! gen_variable_die (decl, origin, context_die, replacement);
break;
case FIELD_DECL:
*************** gen_decl_die (tree decl, tree origin, dw
*** 15375,15381 ****
gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
else
gen_type_die (TREE_TYPE (decl_or_origin), context_die);
! gen_formal_parameter_die (decl, origin, context_die);
break;
case NAMESPACE_DECL:
--- 15644,15650 ----
gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
else
gen_type_die (TREE_TYPE (decl_or_origin), context_die);
! gen_formal_parameter_die (decl, origin, context_die, replacement);
break;
case NAMESPACE_DECL:
*************** dwarf2out_decl (tree decl)
*** 15666,15672 ****
return;
}
! gen_decl_die (decl, NULL, context_die);
}
/* Output a marker (i.e. a label) for the beginning of the generated code for
--- 15935,15941 ----
return;
}
! gen_decl_die (decl, NULL, context_die, NULL);
}
/* Output a marker (i.e. a label) for the beginning of the generated code for
Index: tree-ssa-live.c
===================================================================
*** tree-ssa-live.c (revision 144621)
--- tree-ssa-live.c (working copy)
*************** mark_scope_block_unused (tree scope)
*** 459,464 ****
--- 459,486 ----
mark_scope_block_unused (t);
}
+ /* Called via walk_tree. See if expression contains some variable that is
+ no longer used in source function. */
+ static tree
+ lookup_dead_vars (tree * tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+ {
+ if (is_gimple_min_invariant (*tp)
+ || TYPE_P (*tp))
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+ if (DECL_P (*tp))
+ {
+ *walk_subtrees = 0;
+ if (TREE_CODE (*tp) == VAR_DECL
+ && (!var_ann (*tp)
+ || !var_ann (*tp)->used))
+ return *tp;
+ }
+ return NULL_TREE;
+ }
+
/* Look if the block is dead (by possibly eliminating its dead subblocks)
and return true if so.
Block is declared dead if:
*************** remove_unused_scope_block_p (tree scope)
*** 479,484 ****
--- 501,513 ----
bool unused = !TREE_USED (scope);
var_ann_t ann;
int nsubblocks = 0;
+ unsigned int i;
+
+ for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (scope); i++)
+ if (BLOCK_NONLOCALIZED_VAR_REPLACEMENT (scope, i)
+ && walk_tree (&BLOCK_NONLOCALIZED_VAR_REPLACEMENT (scope, i),
+ lookup_dead_vars, NULL, NULL))
+ BLOCK_NONLOCALIZED_VAR_REPLACEMENT (scope, i) = NULL_TREE;
for (t = &BLOCK_VARS (scope); *t; t = next)
{
*************** dump_scope_block (FILE *file, int indent
*** 654,659 ****
--- 683,695 ----
fprintf (file, "%*s",indent, "");
print_generic_decl (file, BLOCK_NONLOCALIZED_VAR (scope, i),
flags);
+ if (BLOCK_NONLOCALIZED_VAR_REPLACEMENT (scope, i))
+ {
+ fprintf (file, "replaced by:");
+ print_generic_expr (file,
+ BLOCK_NONLOCALIZED_VAR_REPLACEMENT (scope, i),
+ flags);
+ }
fprintf (file, " (nonlocalized)\n");
}
for (t = BLOCK_SUBBLOCKS (scope); t ; t = BLOCK_CHAIN (t))
Index: tree-inline.c
===================================================================
*** tree-inline.c (revision 144621)
--- tree-inline.c (working copy)
*************** remap_ssa_name (tree name, copy_body_dat
*** 221,226 ****
--- 221,285 ----
return new_tree;
}
+ /* Return previously remapped type of TYPE in ID. Return NULL if TYPE
+ is NULL or TYPE has not been remapped before. */
+
+ static tree
+ remapped_type (tree type, copy_body_data *id)
+ {
+ tree *node;
+
+ if (type == NULL)
+ return type;
+
+ /* See if we have remapped this type. */
+ node = (tree *) pointer_map_contains (id->decl_map, type);
+ if (node)
+ return *node;
+ else
+ return NULL;
+ }
+
+ /* Decide if DECL can be put into BLOCK_NONLOCAL_VARs. */
+
+ static bool
+ can_be_nonlocal (tree decl, copy_body_data *id)
+ {
+ /* We can not duplicate function decls. */
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ return true;
+
+ /* Local static vars must be non-local or we get multiple declaration
+ problems. */
+ if (TREE_CODE (decl) == VAR_DECL
+ && !auto_var_in_fn_p (decl, id->src_fn))
+ return true;
+
+ if (TREE_CODE (decl) == PARM_DECL)
+ return false;
+
+ /* At the moment dwarf2out can handle only these types of nodes. We
+ can support more later. */
+ if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != PARM_DECL)
+ return false;
+
+ /* We must use global type. We call remapped_type instead of
+ remap_type since we don't want to remap this type here if it
+ hasn't been remapped before. */
+ if (TREE_TYPE (decl) != remapped_type (TREE_TYPE (decl), id))
+ return false;
+
+ /* Wihtout SSA we can't tell if variable is used. */
+ if (!gimple_in_ssa_p (cfun))
+ return false;
+
+ /* Live variables must be copied so we can attach DECL_RTL. */
+ if (var_ann (decl))
+ return false;
+
+ return true;
+ }
+
/* Remap DECL during the copying of the BLOCK tree for the function. */
tree
*************** remap_decl (tree decl, copy_body_data *i
*** 238,244 ****
/* If we didn't already have an equivalent for this declaration,
create one now. */
! if (!n)
{
/* Make a copy of the variable or label. */
tree t = id->copy_decl (decl, id);
--- 297,303 ----
/* If we didn't already have an equivalent for this declaration,
create one now. */
! if (!n && !can_be_nonlocal (decl, id))
{
/* Make a copy of the variable or label. */
tree t = id->copy_decl (decl, id);
*************** remap_decl (tree decl, copy_body_data *i
*** 287,292 ****
--- 346,368 ----
}
return t;
}
+ /* Nonlocal decls can not be used by function body, because we are not making
+ duplicate declarations for them: for static variables or functions we can't
+ make duplicate declarations because of one declaration rule. This function
+ should never be called on them.
+
+ We however need to handle gratefully dead local variables. These still can get
+ references in e.g. variable length array types.
+ In such cases the size of type is lost and we mark this by substituting
+ error_mark_node there. */
+ else if (!n)
+ {
+ /* Make sure no one called us on static variable. */
+ gcc_assert (auto_var_in_fn_p (decl, id->src_fn));
+ gcc_assert (!var_ann (decl));
+ insert_decl_map (id, decl, error_mark_node);
+ return error_mark_node;
+ }
return unshare_expr (*n);
}
*************** remap_type (tree type, copy_body_data *i
*** 426,490 ****
return tmp;
}
! /* Return previously remapped type of TYPE in ID. Return NULL if TYPE
! is NULL or TYPE has not been remapped before. */
!
static tree
! remapped_type (tree type, copy_body_data *id)
! {
! tree *node;
!
! if (type == NULL)
! return type;
!
! /* See if we have remapped this type. */
! node = (tree *) pointer_map_contains (id->decl_map, type);
! if (node)
! return *node;
! else
! return NULL;
! }
!
! /* The type only needs remapping if it's variably modified. */
! /* Decide if DECL can be put into BLOCK_NONLOCAL_VARs. */
!
! static bool
! can_be_nonlocal (tree decl, copy_body_data *id)
{
! /* We can not duplicate function decls. */
! if (TREE_CODE (decl) == FUNCTION_DECL)
! return true;
!
! /* Local static vars must be non-local or we get multiple declaration
! problems. */
! if (TREE_CODE (decl) == VAR_DECL
! && !auto_var_in_fn_p (decl, id->src_fn))
! return true;
!
! /* At the moment dwarf2out can handle only these types of nodes. We
! can support more later. */
! if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != PARM_DECL)
! return false;
! /* We must use global type. We call remapped_type instead of
! remap_type since we don't want to remap this type here if it
! hasn't been remapped before. */
! if (TREE_TYPE (decl) != remapped_type (TREE_TYPE (decl), id))
! return false;
! /* Wihtout SSA we can't tell if variable is used. */
! if (!gimple_in_ssa_p (cfun))
! return false;
! /* Live variables must be copied so we can attach DECL_RTL. */
! if (var_ann (decl))
! return false;
! return true;
}
static tree
! remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id)
{
tree old_var;
tree new_decls = NULL_TREE;
--- 502,716 ----
return tmp;
}
! /* See if we can turn REPLACEMENT into expression that uses user variables
! and thus can be output in debug information. Choose variant that has
! best chance to survive till end of compilation and be represented in
! debug info.
!
! We look primarily for the most common scenarios: constants, copies and
! accestors to subobjects.
!
! Dwarf2 has quite limited powers here: the actual value of variable must
! be either constant or stored in memory. This we can represent
! &pointer->firstfield, but not &pointer->secondfield or &local_var.
! Substitutions can however cascade, so &pointer->secondfield can later
! optimize into memory reference e.g. by SRA. */
static tree
! simplify_replacement (tree replacement, tree fn, bool only_address)
{
! tree tmp = replacement;
! tree tmp2 = NULL;
! /* If we get back to original argument, we are almost always victorious! */
! if (TREE_CODE (replacement) == PARM_DECL)
! return replacement;
!
! /* Addresses of objects are also safe since they do not change. */
! if (DECL_P (replacement) && only_address)
! return replacement;
!
! /* If we track to user variable, note it since it has good chance to be tracked
! to debug info. */
! if ((DECL_P (replacement) && !DECL_IGNORED_P (replacement))
! && auto_var_in_fn_p (replacement, fn))
! return replacement;
!
! /* Invariants are win. */
! if (is_gimple_min_invariant (replacement))
! return replacement;
!
! /* Look into conversions and handled components. */
! if (TREE_CODE (replacement) == NOP_EXPR
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE)
! return fold_convert (TREE_TYPE (replacement), tmp);
!
! if (TREE_CODE (replacement) == VIEW_CONVERT_EXPR
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE)
! return fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (replacement), tmp);
!
! if (TREE_CODE (replacement) == COMPONENT_REF
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE)
! {
! if (TREE_OPERAND (replacement, 2)
! && (tmp2 =
! simplify_replacement (TREE_OPERAND (replacement, 2), fn,
! 0)) == NULL_TREE)
! return NULL;
! return fold_build3 (COMPONENT_REF, TREE_TYPE (replacement), tmp,
! TREE_OPERAND (replacement, 1), tmp2);
! }
!
! if ((TREE_CODE (replacement) == REALPART_EXPR
! || TREE_CODE (replacement) == IMAGPART_EXPR)
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE)
! return fold_build1 (TREE_CODE (replacement), TREE_TYPE (replacement),
! tmp);
!
! if (TREE_CODE (replacement) == ARRAY_REF
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE
! && (tmp2 =
! simplify_replacement (TREE_OPERAND (replacement, 1), fn,
! 0)) != NULL_TREE)
! {
! tree tmp3 = NULL;
! tree tmp4 = NULL;
! if (TREE_OPERAND (replacement, 2)
! && (tmp3 =
! simplify_replacement (TREE_OPERAND (replacement, 2), fn,
! 0)) == NULL_TREE)
! return NULL;
! if (TREE_OPERAND (replacement, 3)
! && (tmp4 =
! simplify_replacement (TREE_OPERAND (replacement, 3), fn,
! 0)) == NULL_TREE)
! return NULL;
! return
! fold (build4
! (ARRAY_REF, TREE_TYPE (replacement), tmp, tmp2, tmp3, tmp4));
! }
!
! if (TREE_CODE (replacement) == BIT_FIELD_REF
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! only_address)) != NULL_TREE)
! return fold (build3 (BIT_FIELD_REF, TREE_TYPE (replacement), tmp,
! TREE_OPERAND (replacement, 1),
! TREE_OPERAND (replacement, 2)));
!
! if (TREE_CODE (replacement) == INDIRECT_REF
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! false)) != NULL_TREE)
! return build_fold_indirect_ref (tmp);
!
! if (TREE_CODE (replacement) == ADDR_EXPR
! && (tmp =
! simplify_replacement (TREE_OPERAND (replacement, 0), fn,
! true)) != NULL_TREE)
! return build_fold_addr_expr (tmp);
!
! if (TREE_CODE (replacement) == SSA_NAME)
! {
! gimple stmt = SSA_NAME_DEF_STMT (replacement);
!
! /* Early inlining happens on nonoptimized function body.
! See if we can propagate up to constant or user variable. */
! if (gimple_code (stmt) == GIMPLE_ASSIGN)
! {
! enum tree_code subcode = gimple_assign_rhs_code (stmt);
!
! if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
! == GIMPLE_SINGLE_RHS
! && (tmp =
! simplify_replacement (gimple_assign_rhs1 (stmt), fn,
! only_address)))
! return tmp;
!
! if (subcode == NOP_EXPR
! && (tmp =
! simplify_replacement (gimple_assign_rhs1 (stmt), fn,
! only_address)))
! {
! gcc_assert (get_gimple_rhs_class
! (gimple_assign_rhs_code (stmt)));
! return fold_convert (TREE_TYPE (replacement), tmp);
! }
! if (subcode == ADDR_EXPR
! && (tmp =
! simplify_replacement (gimple_assign_rhs1 (stmt), fn, true)))
! {
! gcc_assert (get_gimple_rhs_class
! (gimple_assign_rhs_code (stmt)));
! return build_fold_addr_expr (tmp);
! }
! }
! /* With priority look deeper in the SSA graph. Since ealry inlining is done
! before constant propagation we might easilly miss completely constant
! user variables otherwise. */
! if ((tmp =
! simplify_replacement (SSA_NAME_VAR (replacement), fn,
! only_address)) != NULL_TREE)
! return tmp;
! if (!only_address)
! return SSA_NAME_VAR (replacement);
! }
! if (DECL_P (replacement) && auto_var_in_fn_p (replacement, fn))
! return replacement;
! return NULL;
! }
! /* DECL is not going to be copied into function body. Declare it in NONLOCALIZED_LIST
! and if possible note it being replaced by REPLACEMENT. */
! static void
! declare_nonlocalized_var (VEC(nonlocalized_var,gc) **nonlocalized_list,
! tree decl, tree replacement, copy_body_data *id,
! bool newly_nonlocal_p)
! {
! nonlocalized_var var;
! tree origin_var = DECL_ORIGIN (decl);
!
! if (debug_info_level <= DINFO_LEVEL_TERSE)
! replacement = NULL_TREE;
! if (TREE_READONLY (decl) && !TREE_THIS_VOLATILE (decl) && DECL_INITIAL (decl)
! && TREE_CODE (decl) == VAR_DECL)
! replacement = NULL_TREE;
! if (replacement && newly_nonlocal_p)
! replacement = simplify_replacement (replacement, id->src_fn, false);
! if (replacement)
! {
! if (!useless_type_conversion_p (TREE_TYPE (origin_var), TREE_TYPE (replacement)))
! {
! if (fold_convertible_p (TREE_TYPE (origin_var), replacement))
! replacement = fold_build1 (NOP_EXPR, TREE_TYPE (replacement), replacement);
! else gcc_unreachable ();
! replacement = NULL;
! }
! }
! var.decl = origin_var;
! var.replacement = replacement;
! if (newly_nonlocal_p && TREE_CODE (decl) == PARM_DECL
! && replacement
! && !pointer_map_contains (id->decl_map, decl))
! insert_decl_map (id, decl, replacement);
! if (debug_info_level > DINFO_LEVEL_TERSE
! && !DECL_IGNORED_P (decl))
! VEC_safe_push (nonlocalized_var, gc, *nonlocalized_list, &var);
}
static tree
! remap_decls (tree decls, VEC(nonlocalized_var,gc) **nonlocalized_list, copy_body_data *id)
{
tree old_var;
tree new_decls = NULL_TREE;
*************** remap_decls (tree decls, VEC(tree,gc) **
*** 493,499 ****
for (old_var = decls; old_var; old_var = TREE_CHAIN (old_var))
{
tree new_var;
- tree origin_var = DECL_ORIGIN (old_var);
if (can_be_nonlocal (old_var, id))
{
--- 719,724 ----
*************** remap_decls (tree decls, VEC(tree,gc) **
*** 501,510 ****
&& (var_ann (old_var) || !gimple_in_ssa_p (cfun)))
cfun->local_decls = tree_cons (NULL_TREE, old_var,
cfun->local_decls);
! if (debug_info_level > DINFO_LEVEL_TERSE
! && !DECL_IGNORED_P (old_var)
! && nonlocalized_list)
! VEC_safe_push (tree, gc, *nonlocalized_list, origin_var);
continue;
}
--- 726,732 ----
&& (var_ann (old_var) || !gimple_in_ssa_p (cfun)))
cfun->local_decls = tree_cons (NULL_TREE, old_var,
cfun->local_decls);
! declare_nonlocalized_var (nonlocalized_list, old_var, NULL_TREE, id, true);
continue;
}
*************** remap_decls (tree decls, VEC(tree,gc) **
*** 518,529 ****
if (new_var == id->retvar)
;
else if (!new_var)
! {
! if (debug_info_level > DINFO_LEVEL_TERSE
! && !DECL_IGNORED_P (old_var)
! && nonlocalized_list)
! VEC_safe_push (tree, gc, *nonlocalized_list, origin_var);
! }
else
{
gcc_assert (DECL_P (new_var));
--- 740,746 ----
if (new_var == id->retvar)
;
else if (!new_var)
! declare_nonlocalized_var (nonlocalized_list, old_var, NULL_TREE, id, true);
else
{
gcc_assert (DECL_P (new_var));
*************** remap_decls (tree decls, VEC(tree,gc) **
*** 535,540 ****
--- 752,778 ----
return nreverse (new_decls);
}
+ /* Called via walk_tree. See if expression contains some variable that is
+ no longer used in source function. */
+ static tree
+ lookup_dead_vars (tree * tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+ {
+ if (is_gimple_min_invariant (*tp)
+ || TYPE_P (*tp))
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+ if (DECL_P (*tp))
+ {
+ *walk_subtrees = 0;
+ if (TREE_CODE (*tp) == VAR_DECL
+ && !var_ann (*tp))
+ return *tp;
+ }
+ return NULL_TREE;
+ }
+
/* Copy the BLOCK to contain remapped versions of the variables
therein. And hook the new block into the block-tree. */
*************** remap_block (tree *block, copy_body_data
*** 544,549 ****
--- 782,788 ----
tree old_block;
tree new_block;
tree fn;
+ unsigned int i;
/* Make the new block. */
old_block = *block;
*************** remap_block (tree *block, copy_body_data
*** 551,558 ****
TREE_USED (new_block) = TREE_USED (old_block);
BLOCK_ABSTRACT_ORIGIN (new_block) = old_block;
BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block);
! BLOCK_NONLOCALIZED_VARS (new_block)
! = VEC_copy (tree, gc, BLOCK_NONLOCALIZED_VARS (old_block));
*block = new_block;
/* Remap its variables. */
--- 790,813 ----
TREE_USED (new_block) = TREE_USED (old_block);
BLOCK_ABSTRACT_ORIGIN (new_block) = old_block;
BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block);
! for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (old_block); i++)
! {
! tree replacement = BLOCK_NONLOCALIZED_VAR_REPLACEMENT (old_block, i);
!
! /* Copy replacement only if it still contains only live variables. It makes
! no sense doing so otherwise and copy_body_r would also insert dead
! variables back to referenced vars. */
! if (replacement && !walk_tree (&replacement, lookup_dead_vars, id, NULL))
! {
! walk_tree (&replacement, copy_tree_body_r, id, NULL);
! replacement = fold (replacement);
! }
! else if (replacement)
! replacement = NULL;
! declare_nonlocalized_var (&BLOCK_NONLOCALIZED_VARS (new_block),
! BLOCK_NONLOCALIZED_VAR (old_block, i),
! replacement, id, false);
! }
*block = new_block;
/* Remap its variables. */
*************** insert_init_stmt (basic_block bb, gimple
*** 2015,2032 ****
}
}
/* Initialize parameter P with VALUE. If needed, produce init statement
at the end of BB. When BB is NULL, we return init statement to be
output later. */
static gimple
setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
! basic_block bb, tree *vars)
{
gimple init_stmt = NULL;
tree var;
tree rhs = value;
tree def = (gimple_in_ssa_p (cfun)
? gimple_default_def (id->src_cfun, p) : NULL);
if (value
&& value != error_mark_node
--- 2270,2303 ----
}
}
+ /* See if VAR is never set in the function. */
+ static bool
+ no_sets_of_var_fn (struct function *fun, tree var)
+ {
+ unsigned int i;
+ for (i = 0; i < VEC_length (tree, SSANAMES (fun)); i++)
+ {
+ tree name = VEC_index (tree, SSANAMES (fun), i);
+ if (name && SSA_NAME_VAR (name) == var && !SSA_NAME_IS_DEFAULT_DEF (name))
+ return false;
+ }
+ return true;
+ }
+
/* Initialize parameter P with VALUE. If needed, produce init statement
at the end of BB. When BB is NULL, we return init statement to be
output later. */
static gimple
setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
! basic_block bb, tree *vars,
! VEC(nonlocalized_var, gc) **nonlocalized_list)
{
gimple init_stmt = NULL;
tree var;
tree rhs = value;
tree def = (gimple_in_ssa_p (cfun)
? gimple_default_def (id->src_cfun, p) : NULL);
+ bool can_be_replaced_p;
if (value
&& value != error_mark_node
*************** setup_one_parameter (copy_body_data *id,
*** 2064,2073 ****
--- 2335,2371 ----
just test whether fn == current_function_decl. */
&& ! self_inlining_addr_expr (value, fn))
{
+ declare_nonlocalized_var (nonlocalized_list, p, value, id, true);
insert_decl_map (id, p, value);
return NULL;
}
}
+ if (gimple_in_ssa_p (cfun) && !var_ann (p))
+ {
+ declare_nonlocalized_var (nonlocalized_list, p, value, id, true);
+ return NULL;
+ }
+
+ /* If there is no setup required and we are in SSA, take the easy route
+ replacing all SSA names representing the function parameter by the
+ SSA name passed to function. */
+ can_be_replaced_p = (gimple_in_ssa_p (cfun) && rhs && def && is_gimple_reg (p)
+ && optimize
+ && (TREE_CODE (rhs) == SSA_NAME
+ || is_gimple_reg (rhs)
+ || is_gimple_min_invariant (rhs))
+ && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def));
+
+ /* If the variable can be replaced and function argument is never set,
+ go ahead and do full replacement marking argument as optimized out.
+ This save memory otherwise needed for new variable declaration. */
+ if (can_be_replaced_p
+ && no_sets_of_var_fn (id->src_cfun, p))
+ {
+ declare_nonlocalized_var (nonlocalized_list, p, value, id, true);
+ insert_decl_map (id, def, rhs);
+ return NULL;
+ }
/* Make an equivalent VAR_DECL. Note that we must NOT remap the type
here since the type of this decl must be visible to the calling
*************** setup_one_parameter (copy_body_data *id,
*** 2103,2113 ****
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p)))
TREE_READONLY (var) = 0;
! /* If there is no setup required and we are in SSA, take the easy route
! replacing all SSA names representing the function parameter by the
! SSA name passed to function.
!
! We need to construct map for the variable anyway as it might be used
in different SSA names when parameter is set in function.
FIXME: This usually kills the last connection in between inlined
--- 2401,2408 ----
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p)))
TREE_READONLY (var) = 0;
! /* If variable can be replaced but it is modified in function body,
! construct map for the variable anyway as it might be used
in different SSA names when parameter is set in function.
FIXME: This usually kills the last connection in between inlined
*************** setup_one_parameter (copy_body_data *id,
*** 2117,2127 ****
We might want to introduce a notion that single SSA_NAME might
represent multiple variables for purposes of debugging. */
! if (gimple_in_ssa_p (cfun) && rhs && def && is_gimple_reg (p)
! && optimize
! && (TREE_CODE (rhs) == SSA_NAME
! || is_gimple_min_invariant (rhs))
! && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def))
{
insert_decl_map (id, def, rhs);
return NULL;
--- 2412,2418 ----
We might want to introduce a notion that single SSA_NAME might
represent multiple variables for purposes of debugging. */
! if (can_be_replaced_p)
{
insert_decl_map (id, def, rhs);
return NULL;
*************** initialize_inlined_parameters (copy_body
*** 2187,2193 ****
{
tree val;
val = i < gimple_call_num_args (stmt) ? gimple_call_arg (stmt, i) : NULL;
! setup_one_parameter (id, p, val, fn, bb, &vars);
}
/* Initialize the static chain. */
--- 2478,2484 ----
{
tree val;
val = i < gimple_call_num_args (stmt) ? gimple_call_arg (stmt, i) : NULL;
! setup_one_parameter (id, p, val, fn, bb, &vars, &BLOCK_NONLOCALIZED_VARS (id->block));
}
/* Initialize the static chain. */
*************** initialize_inlined_parameters (copy_body
*** 2198,2204 ****
/* No static chain? Seems like a bug in tree-nested.c. */
gcc_assert (static_chain);
! setup_one_parameter (id, p, static_chain, fn, bb, &vars);
}
declare_inline_vars (id->block, vars);
--- 2489,2495 ----
/* No static chain? Seems like a bug in tree-nested.c. */
gcc_assert (static_chain);
! setup_one_parameter (id, p, static_chain, fn, bb, &vars, &BLOCK_NONLOCALIZED_VARS (id->block));
}
declare_inline_vars (id->block, vars);
*************** copy_decl_maybe_to_var (tree decl, copy_
*** 4198,4204 ****
/* Return a copy of the function's argument tree. */
static tree
copy_arguments_for_versioning (tree orig_parm, copy_body_data * id,
! bitmap args_to_skip, tree *vars)
{
tree arg, *parg;
tree new_parm = NULL;
--- 4489,4497 ----
/* Return a copy of the function's argument tree. */
static tree
copy_arguments_for_versioning (tree orig_parm, copy_body_data * id,
! bitmap args_to_skip,
! VEC(nonlocalized_var,gc) **nonlocalized_list,
! tree *vars)
{
tree arg, *parg;
tree new_parm = NULL;
*************** copy_arguments_for_versioning (tree orig
*** 4216,4231 ****
}
else if (!pointer_map_contains (id->decl_map, arg))
{
! /* Make an equivalent VAR_DECL. If the argument was used
! as temporary variable later in function, the uses will be
! replaced by local variable. */
! tree var = copy_decl_to_var (arg, id);
! get_var_ann (var);
! add_referenced_var (var);
! insert_decl_map (id, arg, var);
! /* Declare this new variable. */
! TREE_CHAIN (var) = *vars;
! *vars = var;
}
return new_parm;
}
--- 4509,4529 ----
}
else if (!pointer_map_contains (id->decl_map, arg))
{
! if (no_sets_of_var_fn (id->src_cfun, arg))
! declare_nonlocalized_var (nonlocalized_list, arg, NULL_TREE, id, true);
! else
! {
! /* Make an equivalent VAR_DECL. If the argument was used
! as temporary variable later in function, the uses will be
! replaced by local variable. */
! tree var = copy_decl_to_var (arg, id);
! get_var_ann (var);
! add_referenced_var (var);
! insert_decl_map (id, arg, var);
! /* Declare this new variable. */
! TREE_CHAIN (var) = *vars;
! *vars = var;
! }
}
return new_parm;
}
*************** tree_function_versioning (tree old_decl,
*** 4282,4287 ****
--- 4580,4586 ----
struct ipa_replace_map *replace_info;
basic_block old_entry_block;
VEC (gimple, heap) *init_stmts = VEC_alloc (gimple, heap, 10);
+ VEC(nonlocalized_var, gc) *nonlocalized_vars = NULL;
tree t_step;
tree old_current_function_decl = current_function_decl;
*************** tree_function_versioning (tree old_decl,
*** 4372,4378 ****
init = setup_one_parameter (&id, replace_info->old_tree,
replace_info->new_tree, id.src_fn,
NULL,
! &vars);
if (init)
VEC_safe_push (gimple, heap, init_stmts, init);
}
--- 4671,4677 ----
init = setup_one_parameter (&id, replace_info->old_tree,
replace_info->new_tree, id.src_fn,
NULL,
! &vars, &nonlocalized_vars);
if (init)
VEC_safe_push (gimple, heap, init_stmts, init);
}
*************** tree_function_versioning (tree old_decl,
*** 4381,4394 ****
if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
DECL_ARGUMENTS (new_decl) =
copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id,
! args_to_skip, &vars);
DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
/* Renumber the lexical scoping (non-code) blocks consecutively. */
number_blocks (id.dst_fn);
declare_inline_vars (DECL_INITIAL (new_decl), vars);
if (DECL_STRUCT_FUNCTION (old_decl)->local_decls != NULL_TREE)
/* Add local vars. */
for (t_step = DECL_STRUCT_FUNCTION (old_decl)->local_decls;
--- 4680,4698 ----
if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
DECL_ARGUMENTS (new_decl) =
copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id,
! args_to_skip, &nonlocalized_vars, &vars);
DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
+ for (i = 0; i < VEC_length (nonlocalized_var, nonlocalized_vars); i++)
+ VEC_safe_push (nonlocalized_var, gc,
+ BLOCK_NONLOCALIZED_VARS (DECL_INITIAL (new_decl)),
+ VEC_index (nonlocalized_var, nonlocalized_vars, i));
/* Renumber the lexical scoping (non-code) blocks consecutively. */
number_blocks (id.dst_fn);
declare_inline_vars (DECL_INITIAL (new_decl), vars);
+
if (DECL_STRUCT_FUNCTION (old_decl)->local_decls != NULL_TREE)
/* Add local vars. */
for (t_step = DECL_STRUCT_FUNCTION (old_decl)->local_decls;