From patchwork Thu Aug 19 17:00:30 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 62204 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 5F18DB70EB for ; Fri, 20 Aug 2010 03:00:40 +1000 (EST) Received: (qmail 9701 invoked by alias); 19 Aug 2010 17:00:38 -0000 Received: (qmail 9452 invoked by uid 22791); 19 Aug 2010 17:00:28 -0000 X-SWARE-Spam-Status: No, hits=-4.6 required=5.0 tests=AWL, BAYES_05, KAM_STOCKGEN, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_BJ, TW_JC, TW_TM, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 19 Aug 2010 17:00:09 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o7JH07nr031168 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 19 Aug 2010 13:00:07 -0400 Received: from tyan-ft48-01.lab.bos.redhat.com (tyan-ft48-01.lab.bos.redhat.com [10.16.42.4]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o7JH064F007191 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 19 Aug 2010 13:00:07 -0400 Received: from tyan-ft48-01.lab.bos.redhat.com (tyan-ft48-01.lab.bos.redhat.com [127.0.0.1]) by tyan-ft48-01.lab.bos.redhat.com (8.14.4/8.14.4) with ESMTP id o7JH0Vgn015877; Thu, 19 Aug 2010 19:00:31 +0200 Received: (from jakub@localhost) by tyan-ft48-01.lab.bos.redhat.com (8.14.4/8.14.4/Submit) id o7JH0Vw0015876; Thu, 19 Aug 2010 19:00:31 +0200 Date: Thu, 19 Aug 2010 19:00:30 +0200 From: Jakub Jelinek To: Jason Merrill , gcc-patches@gcc.gnu.org Cc: Roland McGrath Subject: [PATCH] Debug info extensions to support optimized out parameters Message-ID: <20100819170030.GY702@tyan-ft48-01.lab.bos.redhat.com> Reply-To: Jakub Jelinek MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-12-10) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi! This is an implementation of something I've sent earlier this week to Dwarf-Discuss. I'm attaching patch, two small testcases that can show what are these extensions useful for and the two proposals sent to Dwarf-Discuss. The patch implements this as vendor extension instead, as DWARF5 is probably quite far away. If possible, it would be nice if the vendor codes just matched what will end up in DWARF5, then we'd just switch to using the standard codes. The DW_OP_GNU_entry_value special location op can be useful either together with the DW_TAG_GNU_call_site DIEs, or e.g. for non-interactive debugging (e.g. systemtap, where you know beforehand you'll need to query value at some PC, and can insert breakpoint at the start of routine and remember the value there). DW_TAG_GNU_call_site can be useful also for more useful backtraces (where the debugger can print you the values that were actually passed to the function, rather than whatever the parameters contain now), for virtual backtraces when tail calls are involved, for doing static call graph analysis etc. The patch is on top of the DW_OP_GNU_implicit_pointer patch. Bootstrapped/regtested on x86_64-linux and i686-linux. Jakub 2010-08-19 Jakub Jelinek * final.c (final_scan_insn): Handle NOTE_INSN_CALL_ARG_LOCATION. Call var_location debug hook even on CALL_INSNs. (rest_of_clean_state): Don't print NOTE_INSN_CALL_ARG_LOCATION. * rtl.def (ENTRY_VALUE): New. * dwarf2out.c: Include cfglayout.h. (dwarf_stack_op_name, size_of_loc_descr, output_loc_operands, output_loc_operands_raw): Handle DW_OP_GNU_entry_value. (struct call_arg_loc_node): New type. (call_arg_locations, call_arg_loc_last, block_map, call_site_count, tail_call_site_count): New variables. (dwarf_tag_name): Handle DW_TAG_GNU_call_site and DW_TAG_GNU_call_site_parameter. (dwarf_attr_name): Handle DW_AT_GNU_call_site_value, DW_AT_GNU_call_site_data_value, DW_AT_GNU_call_site_target, DW_AT_GNU_call_site_target_clobbered, DW_AT_GNU_tail_call, DW_AT_GNU_all_tail_call_sites, DW_AT_GNU_all_call_sites and DW_AT_GNU_all_source_call_sites. (const_ok_for_output_1): Don't complain about TLS UNSPECs. (mem_loc_descriptor): Handle ENTRY_VALUE. (add_src_coords_attributes): Don't add enything if DECL_SOURCE_LOCATION is UNKNOWN_LOCATION. (dwarf2out_abstract_function): Save and clear call_arg_location, call_site_count and tail_call_site_count around dwarf2out_decl call. (gen_call_site_die): New function. (gen_subprogram_die): Emit DW_TAG_GNU_call_site DIEs for call sites. (gen_lexical_block_die, gen_inlined_subroutine_die): Update block_map. (dwarf2out_function_decl): Clear call_arg_locations, call_arg_loc_last, set call_site_count and tail_call_site_count to -1 and free block_map. (dwarf2out_var_location): Handle NOTE_INSN_CALL_ARG_LOCATION and CALL_INSNs. Add NOTE_DURING_CALL_P var location notes even when not followed by any real instructions. (dwarf2out_begin_function): Set call_site_count and tail_call_site_count to 0. (resolve_addr): If DW_AT_abstract_origin of DW_TAG_GNU_call_site is dw_val_class_addr, attempt to look it up again, for DECL_EXTERNAL attempt to force a DIE for it and worst case remove the attribute. (resolve_one_addr): For TREE_CONSTANT_POOL_ADDRESS_P SYMBOL_REFs check TREE_ASM_WRITTEN of DECL_INITIAL of the decl instead of the decl itself. * var-tracking.c: Include tm_p.h. (vt_stack_adjustments): For calls call note_register_arguments. (argument_reg_set): New variable. (add_stores): For MO_VAL_SET of non-tracked regs from argument_reg_set ensure the VALUE is resolved. (call_arguments): New variable. (prepare_call_arguments): New function. (add_with_sets): For MO_CALL set u.loc from call_arguments and clear it. (struct expand_loc_callback_data): Add ignore_cur_loc field. (vt_expand_loc_callback): If ignore_cur_loc, don't look at cur_loc and always use the best expression. (vt_expand_loc): Add ignore_cur_loc argument. (vt_expand_loc_dummy): Clear ignore_cur_loc field. (emit_note_insn_var_location): Adjust vt_expand_loc callers. (emit_notes_in_bb) : Add NOTE_INSN_CALL_ARG_LOCATION note for all calls. (vt_add_function_parameters): Use cselib_lookup_from_insn. If dv is a VALUE, enter into hash table also ENTRY_VALUE for the argument. Don't call cselib_preserve_only_values and cselib_reset_table. (note_register_arguments): New function. (vt_initialize): Compute argument_reg_set. Call vt_add_function_parameters before processing basic blocks instead of afterwards. For calls call prepare_call_arguments before calling cselib_process_insn. * print-rtl.c (print_rtx): Handle NOTE_INSN_CALL_ARG_LOCATION. * Makefile.in (dwarf2out.o): Depend on $(CFGLAYOUT_H). (var-tracking.o): Depend on $(TM_P_H). * cfglayout.h (insn_scope): New prototype. * gengtype.c (adjust_field_rtx_def): Handle NOTE_INSN_CALL_ARG_LOCATION. * cfglayout.c (insn_scope): No longer static. * insn-notes.def (CALL_ARG_LOCATION): New. * calls.c (expand_call, emit_library_call_value_1): Put USEs for MEM arguments into CALL_INSN_FUNCTION_USAGE unconditionally. cp/ * cp-objcp-common.c (cp_function_decl_explicit_p): Don't crash if DECL_LANG_SPECIFIC is NULL. include/ * dwarf2.h (DW_TAG_GNU_call_site, DW_TAG_GNU_call_site_parameter, DW_AT_GNU_call_site_value, DW_AT_GNU_call_site_data_value, DW_AT_GNU_call_site_target, DW_AT_GNU_call_site_target_clobbered, DW_AT_GNU_tail_call, DW_AT_GNU_all_tail_call_sites, DW_AT_GNU_all_call_sites,, DW_AT_GNU_all_source_call_sites, DW_OP_GNU_entry_value): New. extern void fn1 (long int, long int, long int); __attribute__((noinline)) long int fn2 (long int a, long int b, long int c) { long int q = 2 * a; fn1 (5, 6, 7); return 0; } long int fn3 (long int x, long int (*fn4) (long int *)) { long int v, w, w2, z; w = (*fn4) (&w2); v = (*fn4) (&w2); z = fn2 (1, v + 1, w); { int v1 = v + 4; z += fn2 (w, v * 2, x); } return z; } subroutine foo (i, j) integer :: i, j, k, l k = i l = j i = i * 10 i = i + j call baz (i) i = i + j end subroutine subroutine bar (i, j) integer :: i, j, k, l k = i l = j i = i * 10 i = i + j call baz (i) i = i + j end subroutine program entryval interface bar subroutine bar (i, j) integer :: i, j end subroutine end interface integer :: i i = 6 call foo (i, 7) i = 8 call bar (i, 7) i = 10 call foo (i, 7) i = 12 call bar (i, 7) end Tracking of values passed as arguments to functions in debug information ======================================================================== Overview -------- Many architectures pass arguments in registers and quite often the register in which an argument has been passed is quickly reused for something else. In that case all the debugger can say is that a value has been optimized out. If the argument is never modified in the function, often the value can be discovered through extra effort. This could be by setting preemptive breakpoints to collect argument values at function entry, or by unwinding in the debugger to the caller and seeing what value has been passed to the function. A companion proposal addresses unwinding to a caller to determine argument values for such cases. Proposed changes to DWARF ------------------------- New DWARF expression operation DW_OP_entry_value 0xa1 2 ULEB128 size followed by DWARF expression block of that size 2.5.1.6 Change "one special operation" into "two special operations". Add 2. DW_OP_entry_value The DW_OP_entry_value operation pushes a value that had a known location upon entering the current subprogram. It uses two operands: an unsigned LEB128 length, followed by a block containing a DWARF expression or a simple register location description. The length gives the length in bytes of the block. If the block contains a register location description, DW_OP_entry_value pushes the value that register had upon entering the current subprogram. If the block contains a DWARF expression, the DWARF expression is evaluated in a separate DWARF stack from the currently used one as if it has been evaluated upon entering the current subprogram. The new DWARF stack is initially empty. DW_OP_push_object_address is not meaningful inside of this DWARF expression. The DW_OP_entry_value operation then pushes the value from the top of the new stack to the previous stack, everything from the separate DWARF stack is afterwards discarded. 2.6.1.3 Add DW_OP_entry_value 1 DW_OP_reg5 DW_OP_stack_value DW_OP_entry_value 2 DW_OP_breg5 0 DW_OP_stack_value Both of these location descriptions mean the value register 5 had upon entering of the current subprogram. DW_OP_breg2 0 DW_OP_entry_value 1 DW_OP_reg5 DW_OP_add DW_OP_stack_value The value register 5 had upon entering of the current subprogram plus the value register 2 currently has. DW_OP_entry_value 3 DW_OP_breg4 16 DW_OP_deref DW_OP_stack_value DW_OP_entry_value 6 DW_OP_entry_value 1 DW_OP_reg4 DW_OP_plus_uconst 16 DW_OP_deref DW_OP_stack_value These two location expressions do the same thing, push the value memory location with size of an address pointed to by value of register 4 upon entering current subprogram plus 16 had upon entering of the current subprogram. 7.7.1 Add DW_OP_entry_value 0xa1 2 ULEB128 size followed by DWARF expression block of that size to figure 24. Change History -------------- Aug 11, 2010 - use 0xa1 instead of 0xa0, as 0xa0 is in DW_OP_implicit_pointer proposal May 4, 2010 - change DW_OP_entry_value argument to DWARF expression from location description, add simple register location description as more compact alternative (similar to DW_AT_frame_base) April 28, 2010 - the whole location expression in DW_OP_entry_value is to be evaluated in the context upon entry of current subprogram. April 27, 2010 - split into separate DW_OP_entry_value and DW_TAG_call_site proposals. April 26, 2010 - remove DW_AT_tail_call_count, add instead DW_AT_call_site_count. April 15, 2010 - add optimized out parameters that aren't passed at all. April 13, 2010 - initial draft. Representation of call sites in the debugging information ========================================================= Overview -------- Many architectures pass arguments in registers and quite often the register in which an argument has been passed is quickly reused for something else, in that case all the debugger can say is that a value has been optimized out. If the argument is never modified in the function, often the value can be discovered through extra effort, by unwinding in the debugger to the caller and seeing what value has been passed to the function. If a constant is passed to the function, or the argument is loaded from a call preserved register or call preserved memory, then that is the value of the argument in the callee. For arguments passed in stack slots this is needed less often, as the stack slot in which the value has been passed is usually not reused for something else, but it could be in some cases. For backtraces it is often worthwhile to print what value has been passed to an argument at the time a function has been called, rather than what the argument currently has. E.g. for void foo (char *p) { /* some code */ p = strchr (p, '\0'); bar (); /* some further code */ } if a backtrace is done from within bar, argument p will be printed as "", which isn't much useful, more interesting is the string before it. The debugger then could annotate the values in the backtrace whether they mean the value passed to the function on function entry, the current value of the parameter, that the value is known to be the same in both places, print both values, etc. The proposed extensions involve adding optional information about call sites in the programs which say at which location what other function (if known which one) is called and what values are passed to its arguments and also a new DWARF expression opcode that can be used to push the value a register argument or some memory location had on entry of the current function. If tail calls are involved, the proposed extensions allow this fact to be detected. In that case the return address of a callee is not the function that actually called it, but a caller of the function that did the tail call and call target in the call site at return address doesn't match the current subprogram. In some cases where the tail call sequence reaching current subprogram is unambiguous, the debug information consumer might print a virtual backtrace including the tail calls. Even when the call site target matches the current subprogram tail calls still might be involved - if the current function could possibly indirectly tail call itself. The extensions allow that case to be detected. The debugging information consumer should be conservative with that check and assume that is possible unless proven otherwise. Proposed changes to DWARF ------------------------- New DWARF tags DW_TAG_call_site 0x44 Allowable attributes: DW_AT_abstract_origin DW_AT_call_column DW_AT_call_file DW_AT_call_line DW_AT_call_site_target DW_AT_call_site_target_clobbered DW_AT_low_pc DW_AT_sibling DW_AT_tail_call DW_AT_type DW_TAG_call_site_parameter 0x45 Allowable attributes: DW_AT_abstract_origin DW_AT_call_site_data_value DW_AT_call_site_value DW_AT_data_location DW_AT_location DW_AT_name DW_AT_sibling DW_AT_type New DWARF attributes DW_AT_call_site_value 0x6f exprloc DW_AT_call_site_data_value 0x70 exprloc DW_AT_call_site_target 0x71 exprloc DW_AT_call_site_target_clobbered 0x72 exprloc DW_AT_tail_call 0x73 flag DW_AT_all_tail_call_sites 0x74 flag DW_AT_all_call_sites 0x75 flag DW_AT_all_source_call_sites 0x76 flag 2.2 Add DW_TAG_call_site and DW_TAG_call_site_parameter to figure 1. Add: DW_AT_call_site_value Value passed to a function argument DW_AT_call_site_data_value Value pointed to by passed function argument DW_AT_call_site_target Address of called subroutine DW_AT_call_site_target_clobbered Callee address value, which may use call clobbered registers or memory locations DW_AT_tail_call Call site is a tail call DW_AT_all_tail_call_sites All tail call sites in a subprogram have corresponding DW_TAG_call_site entries DW_AT_all_call_sites All normal and tail call sites in a subprogram have corresponding DW_TAG_call_site entries DW_AT_all_source_call_sites All normal and tail call sites and all inline calls have DW_TAG_call_site resp. DW_TAG_inlined_subroutine entries to figure 2. 3.3.1 Add: A subprogram entry may have DW_AT_all_tail_call_sites, DW_AT_all_call_sites or DW_AT_all_source_call_sites attributes which are flags. The DW_AT_all_tail_call_sites flag indicates that no DW_TAG_call_site entries with DW_AT_tail_call flag set are missing for this subprogram entry. The DW_AT_all_call_sites flag indicates that no DW_TAG_call_site entries are missing for this subprogram entry. If this attribute is set, the DW_AT_all_tail_call_sites attribute is superfluous. The DW_AT_all_source_call_sites attribute indicates that no DW_TAG_call_site nor DW_TAG_inlined_subroutine entries are missing for this subprogram entry. If this attribute is set, the DW_AT_all_tail_call_sites and DW_AT_all_call_sites attributes are superfluous. DW_TAG_call_site entries represent normal and tail call sites in the subprogram or subroutines inlined into it, the flags cover entries owned by the subroutine and entry point entries. If the flags aren't set, some or all DW_TAG_call_site resp. DW_TAG_inlined_subroutines entries might be missing. Entries owned by subroutine or entry point entries are children of the subprogram or entry point entry they are attributes of and any of its DW_TAG_lexical_block, DW_TAG_inlined_subroutine, DW_TAG_try_block or DW_TAG_catch_block child entries, but not children of other nested DW_TAG_subprogram or DW_TAG_entry_point entries. New section: 3.8 Call site entries A call site is a way to represent the static or dynamic call graph in the debugging information. A call site is represented by a debugging information entry with the tag DW_TAG_call_site. The entry for a call site should be owned by the debugging information entry representing the scope within which the call is present in the source program. A source call can be compiled into different types of machine code: Normal calls are call-like instructions which transfer control to the start of some subprogram and leave the call site location address somewhere where unwind information sees it. Tail calls are jump-like instructions which transfer control to the start of some subprogram, but the call site location address isn't visible in the unwind information. Tail recursion is a call to the current function which is compiled as a loop into the middle of the current function. Optimized out call is a call that is in unreachable code that hasn't been emitted, like if (0) foo ();. Inline call is a call to inlined subprogram, where at least one instruction has the location of the inlined subprogram or any of its blocks or inlined subprograms. Optimized out inline call is a call to inlined subprogram which either didn't expand to any instructions or only parts of instructions belong to it and for debug information those instructions are given location in the caller. The DW_TAG_call_site entries describe normal and tail calls. The call site entry has a DW_AT_low_pc attribute which is the return address after the call. This corresponds to the return address computed by CFI in the called function (6.4). On many architectures this is the address immediately following the call instruction, but e.g. on architectures with delay slots it might be an address after the delay slot of the call. If the call site is a tail call optimized through tail call optimization into a jump instead of call, that doesn't leave traces of the function call in the unwind information, it should have the DW_AT_tail_call attribute, which is a flag. The call site may have a DW_AT_abstract_origin attribute which is a reference. For direct calls or jumps where the called subprogram is known it should be a reference to the called subprogram's debugging information entry. For indirect calls it may be a reference to a DW_TAG_variable, DW_TAG_formal_parameter or DW_TAG_member entry representing the subroutine pointer that is called. The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at compile time which subprogram will be called the expression computes the address of the subprogram that will be called. The DWARF expression should not use register or memory locations that might be clobbered by the call. The call site may have a DW_AT_call_site_target_clobbered attribute which is a DWARF expression. For indirect calls or jumps where the address is not computable without use of registers or memory locations that might be clobbered by the call this attribute may be used instead of the DW_AT_call_site_target attribute. The call site entry may have a DW_AT_type attribute referencing a debugging information entry of the type of the called function. When DW_AT_abstract_origin is present, DW_AT_type is usually omitted. The call site entry may have DW_AT_call_file, DW_AT_call_line and DW_AT_call_column attributes, each of whose value is an integer constant. These attributes represent the source file, source line number, and source column number, respectively, of the first character of the call statement or expression. The call file, call line, and call column attributes are interpreted in the same way as the declaration file, declaration line, and declaration column attributes, respectively (see Section 2.14). The call file, call line and call column coordinates do not describe the coordinates of the subroutine declaration that was inlined, rather they describe the coordinates of the call. The call site entry may own DW_TAG_call_site_parameter debugging information entries representing the parameters passed to the call. Each such DW_TAG_call_site_parameter debugging information entry should have a DW_AT_location attribute which is a location expression. This location expression shall describe where the parameter is passed in (usually either some register, or a memory at stack register plus some offset). Each DW_TAG_call_site_parameter entry may have a DW_AT_call_site_value attribute which is a DWARF expression. This expression computes the value passed to that parameter. The expression should not use registers or memory locations that might be clobbered by the call, as it might be evaluated after unwinding from the called function back to the caller. For parameters passed by reference, where the code passes a pointer to a location which contains the parameter, or for reference type parameters the DW_TAG_call_site_parameter entry may also have DW_AT_data_location which is a location expression and DW_AT_call_site_data_value attribute which is a DWARF expression. The DW_AT_data_location attribute describes where the referenced value lives during the call. If it is just DW_OP_push_object_address, it may be left out. The DW_AT_call_site_data_value attribute describes the value in that location. The expression should not use registers or memory locations that might be clobbered by the call, as it might be evaluated after unwinding from the called function back to the caller. Each DW_TAG_call_site_parameter entry may also have a DW_AT_abstract_origin attribute which contains a reference to a DW_TAG_formal_parameter entry, DW_AT_type attribute referencing the type of the parameter and/or DW_AT_name attribute describing the parameter's name. 7.5.4 Add DW_TAG_call_site 0x44 DW_TAG_call_site_parameter 0x45 to figure 18. Add DW_AT_call_site_value 0x6f exprloc DW_AT_call_site_data_value 0x70 exprloc DW_AT_call_site_target 0x71 exprloc DW_AT_call_site_target_clobbered 0x72 exprloc DW_AT_tail_call 0x73 flag DW_AT_all_tail_call_sites 0x74 flag DW_AT_all_call_sites 0x75 flag DW_AT_all_source_call_sites 0x76 flag to figure 20. Appendix A Add DW_AT_all_tail_call_sites, DW_AT_all_call_sites and DW_AT_all_source_call_sites as allowable attribute to DW_TAG_subprogram and DW_TAG_entry_point. Add: DW_TAG_call_site DW_AT_abstract_origin DW_AT_call_column DW_AT_call_file DW_AT_call_line DW_AT_call_site_target DW_AT_call_site_target_clobbered DW_AT_low_pc DW_AT_sibling DW_AT_tail_call DW_AT_type DW_TAG_call_site_parameter DW_AT_abstract_origin DW_AT_call_site_data_value DW_AT_call_site_value DW_AT_data_location DW_AT_location DW_AT_name DW_AT_sibling DW_AT_type entries. New section: D.13 Call Site Examples The following examples use a hypothetical machine which passes first argument in register 0, second in register 1, third in register 2, the stack pointer is register 3 and the machine has one call preserved register 4. Return value from function is passed in register 0. /* C source */ extern void fn1 (long int, long int, long int); long int fn2 (long int a, long int b, long int c) { long int q = 2 * a; fn1 (5, 6, 7); return 0; } long int fn3 (long int x, long int (*fn4) (long int *)) { long int v, w, w2, z; w = (*fn4) (&w2); v = (*fn4) (&w2); z = fn2 (1, v + 1, w); { int v1 = v + 4; z += fn2 (w, v * 2, x); } return z; } /* Assembly */ fn2: L1: %reg2 = 7 %reg1 = 6 %reg0 = 5 L2: call fn1 %reg0 = 0 return L3: fn3: %reg3 = %reg3 - 32 [%reg3] = %reg4 [%reg3 + 8] = %reg0 [%reg3 + 16] = %reg1 %reg0 = %reg3 + 24 call %reg1 L6: %reg2 = [%reg3 + 16] [%reg3 + 16] = %reg0 %reg0 = %reg3 + 24 call %reg2 L7: %reg4 = %reg0 %reg2 = [%reg3 + 16] %reg1 = %reg4 + 1 %reg0 = 1 call fn2 L4: %reg2 = [%reg3 + 8] [%reg3 + 8] = %reg0 %reg0 = [%reg3 + 16] %reg1 = %reg4 + %reg4 call fn2 L5: %reg2 = [%reg3 + 8] %reg0 = %reg0 + %reg2 L8: %reg4 = [%reg3] %reg3 = %reg3 + 32 return The location list for variable a in fn2 then might be: DW_OP_reg0 DW_OP_entry_value 1 DW_OP_reg0 DW_OP_stack_value <0, 0> variable q in fn2 then might have location list: DW_OP_lit2 DW_OP_breg0 0 DW_OP_mul DW_OP_stack_value DW_OP_lit2 DW_OP_entry_value 1 DW_OP_reg0 DW_OP_mul DW_OP_stack_value <0, 0> Variables b and c would be similar location list to variable a, except for different label in between the two ranges and would use DW_OP_reg1 resp. DW_OP_reg2 instead of DW_OP_reg0 and DW_OP_breg1 resp. DW_OP_breg2 instead of DW_OP_breg0. The call sites for all the calls in fn3 would be children of the DW_TAG_subprogram (or its DW_TAG_lexical_block if it has any for the whole function): DW_TAG_call_site DW_AT_low_pc(L6) DW_AT_call_site_target(DW_OP_breg3 16 DW_OP_deref) DW_TAG_call_site_parameter DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_breg3 24) DW_TAG_call_site DW_AT_low_pc(L7) DW_AT_call_site_target(DW_OP_entry_value 1 DW_OP_reg1) DW_TAG_call_site_parameter DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_breg3 24) DW_TAG_call_site DW_AT_low_pc(L4) DW_AT_abstract_origin(reference to fn2 DW_TAG_subprogram) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter a in fn2 subprogram) DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_lit1) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter b in fn2 subprogram) DW_AT_location(DW_OP_reg1) DW_AT_call_site_value(DW_OP_breg4 1) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter c in fn2 subprogram) DW_AT_location(DW_OP_reg2) DW_AT_call_site_value(DW_OP_breg3 16 DW_OP_deref) DW_TAG_lexical_block DW_AT_low_pc(L4) DW_AT_high_pc(L8) DW_TAG_variable DW_AT_name("v1") DW_AT_type(reference to int) DW_AT_location(DW_OP_breg4 4) DW_TAG_call_site DW_AT_low_pc(L5) DW_AT_call_site_target(reference to fn2 DW_TAG_subprogram) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter a in fn2 subprogram) DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_breg3 16 DW_OP_deref) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter b in fn2 subprogram) DW_AT_location(DW_OP_reg1) DW_AT_call_site_value(DW_OP_lit2 DW_OP_breg4 0 DW_OP_mul) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter c in fn2 subprogram) DW_AT_location(DW_OP_reg2) DW_AT_call_site_value(DW_OP_entry_value 1 DW_OP_reg0) ! Fortran source to show passing parameters by reference. subroutine fn4 (n) integer :: n, x x = n n = n / 2 call fn6 end subroutine subroutine fn5 (n) interface fn4 subroutine fn4 (n) integer :: n end subroutine end interface fn4 integer :: n, x call fn4 (n) x = 5 call fn4 (x) end subroutine fn5 /* Assembly */ fn4: %reg2 = [%reg0] %reg2 = %reg2 / 2 [%reg0] = %reg2 call fn6 return fn5: %reg3 = %reg3 - 8 call fn4 L9: [%reg3] = 5 %reg0 = %reg3 call fn4 L10: %reg3 = %reg3 + 8 return The location description for x in fn4 might be DW_OP_entry_value 4 DW_OP_breg0 0 DW_OP_deref_size 4 DW_OP_stack_value. The call sites in fn5 might be: DW_TAG_call_site DW_AT_low_pc(L9) DW_AT_abstract_origin(reference to fn4 DW_TAG_subprogram) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter n in fn4 subprogram) DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_entry_value 1 DW_OP_reg0) ! DW_AT_data_location(DW_OP_push_object_address) ! left out DW_AT_call_site_data_value(DW_OP_entry_value 4 DW_OP_breg0 0 DW_OP_deref_size 4) DW_TAG_call_site DW_AT_low_pc(L10) DW_AT_abstract_origin(reference to fn4 DW_TAG_subprogram) DW_TAG_call_site_parameter DW_AT_abstract_origin(reference to DW_TAG_formal_parameter n in fn4 subprogram) DW_AT_location(DW_OP_reg0) DW_AT_call_site_value(DW_OP_breg3 0) ! DW_AT_data_location(DW_OP_push_object_address) ! left out DW_AT_call_site_data_value(DW_OP_lit5) Change History -------------- May 28, 2010 - allow DW_AT_call_{file,line,column} instead of DW_AT_decl_{file,line,column} on DW_TAG_call_site, disallow the latter on DW_TAG_call_site_parameter, other minor changes May 21, 2010 - introduced DW_TAG_call_site_parameter, removed DW_AT_call_site_count, added DW_AT_call_site_target_clobbered, DW_AT_all_tail_call_sites, DW_AT_all_call_sites, DW_AT_all_source_call_sites. Further additions to appendix D May 6, 2010 - add assembly code for the appendix D example April 27, 2010 - split into separate DW_OP_entry_value and DW_TAG_call_site proposals. April 26, 2010 - remove DW_AT_tail_call_count, add instead DW_AT_call_site_count. April 15, 2010 - add optimized out parameters that aren't passed at all. April 13, 2010 - initial draft. --- gcc/cp/cp-objcp-common.c.jj 2010-07-28 10:35:56.000000000 +0200 +++ gcc/cp/cp-objcp-common.c 2010-08-13 12:44:17.000000000 +0200 @@ -161,6 +161,7 @@ cp_function_decl_explicit_p (tree decl) { return (decl && FUNCTION_FIRST_USER_PARMTYPE (decl) != void_list_node + && DECL_LANG_SPECIFIC (STRIP_TEMPLATE (decl)) && DECL_NONCONVERTING_P (decl)); } --- gcc/Makefile.in.jj 2010-08-13 10:15:19.000000000 +0200 +++ gcc/Makefile.in 2010-08-16 14:53:24.000000000 +0200 @@ -2951,7 +2951,7 @@ dwarf2out.o : dwarf2out.c $(CONFIG_H) $( $(LIBFUNCS_H) $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) dwarf2out.h reload.h \ $(GGC_H) $(EXCEPT_H) dwarf2asm.h $(TM_P_H) langhooks.h $(HASHTAB_H) \ gt-dwarf2out.h $(TARGET_H) $(CGRAPH_H) $(MD5_H) $(INPUT_H) $(FUNCTION_H) \ - $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) tree-pretty-print.h + $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) $(CFGLAYOUT_H) tree-pretty-print.h dwarf2asm.o : dwarf2asm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(FLAGS_H) $(RTL_H) $(TREE_H) output.h dwarf2asm.h $(TM_P_H) $(GGC_H) \ gt-dwarf2asm.h $(DWARF2_H) $(SPLAY_TREE_H) $(TARGET_H) @@ -3178,7 +3178,7 @@ var-tracking.o : var-tracking.c $(CONFIG $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \ $(REGS_H) $(EXPR_H) $(TIMEVAR_H) $(TREE_PASS_H) $(TREE_FLOW_H) \ cselib.h $(TARGET_H) $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) $(PARAMS_H) $(DIAGNOSTIC_H) pointer-set.h \ - $(RECOG_H) tree-pretty-print.h + $(RECOG_H) $(TM_P_H) tree-pretty-print.h profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) $(FUNCTION_H) $(BASIC_BLOCK_H) \ $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) $(COVERAGE_H) $(TREE_FLOW_H) value-prof.h cfghooks.h \ --- gcc/print-rtl.c.jj 2010-08-13 10:18:21.000000000 +0200 +++ gcc/print-rtl.c 2010-08-13 12:44:17.000000000 +0200 @@ -297,6 +297,7 @@ print_rtx (const_rtx in_rtx) } case NOTE_INSN_VAR_LOCATION: + case NOTE_INSN_CALL_ARG_LOCATION: #ifndef GENERATOR_FILE fputc (' ', outfile); print_rtx (NOTE_VAR_LOCATION (in_rtx)); --- gcc/rtl.def.jj 2010-08-13 10:18:21.000000000 +0200 +++ gcc/rtl.def 2010-08-13 12:44:17.000000000 +0200 @@ -715,6 +715,10 @@ DEF_RTL_EXPR(VAR_LOCATION, "var_location addressable. */ DEF_RTL_EXPR(DEBUG_IMPLICIT_PTR, "debug_implicit_ptr", "t", RTX_OBJ) +/* Represents value that argument had on function entry. Should + be only used in VAR_LOCATION location expression. */ +DEF_RTL_EXPR(ENTRY_VALUE, "entry_value", "e", RTX_OBJ) + /* All expressions from this point forward appear only in machine descriptions. */ #ifdef GENERATOR_FILE --- gcc/final.c.jj 2010-08-13 10:15:19.000000000 +0200 +++ gcc/final.c 2010-08-13 12:44:17.000000000 +0200 @@ -1999,6 +1999,7 @@ final_scan_insn (rtx insn, FILE *file, i break; case NOTE_INSN_VAR_LOCATION: + case NOTE_INSN_CALL_ARG_LOCATION: if (!DECL_IGNORED_P (current_function_decl)) debug_hooks->var_location (insn); break; @@ -2670,6 +2671,8 @@ final_scan_insn (rtx insn, FILE *file, i if (t) assemble_external (t); } + if (!DECL_IGNORED_P (current_function_decl)) + debug_hooks->var_location (insn); } /* Output assembler code from the template. */ @@ -4395,6 +4398,7 @@ rest_of_clean_state (void) if (final_output && (!NOTE_P (insn) || (NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION + && NOTE_KIND (insn) != NOTE_INSN_CALL_ARG_LOCATION && NOTE_KIND (insn) != NOTE_INSN_BLOCK_BEG && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END && NOTE_KIND (insn) != NOTE_INSN_CFA_RESTORE_STATE))) --- gcc/var-tracking.c.jj 2010-07-22 11:35:37.000000000 +0200 +++ gcc/var-tracking.c 2010-08-16 17:37:38.000000000 +0200 @@ -115,6 +115,7 @@ #include "tree-pretty-print.h" #include "pointer-set.h" #include "recog.h" +#include "tm_p.h" /* var-tracking.c assumes that tree code with the same value as VALUE rtx code has no chance to appear in REG_EXPR/MEM_EXPRs and isn't a decl. @@ -408,6 +409,7 @@ static void stack_adjust_offset_pre_post static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, HOST_WIDE_INT *); static bool vt_stack_adjustments (void); +static void note_register_arguments (rtx); static rtx compute_cfa_pointer (HOST_WIDE_INT); static hashval_t variable_htab_hash (const void *); static int variable_htab_eq (const void *, const void *); @@ -660,11 +662,15 @@ vt_stack_adjustments (void) for (insn = BB_HEAD (dest); insn != NEXT_INSN (BB_END (dest)); insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - { - insn_stack_adjust_offset_pre_post (insn, &pre, &post); - offset += pre + post; - } + { + if (INSN_P (insn)) + { + insn_stack_adjust_offset_pre_post (insn, &pre, &post); + offset += pre + post; + } + if (CALL_P (insn)) + note_register_arguments (insn); + } VTI (dest)->out.stack_adjust = offset; @@ -4993,6 +4999,9 @@ log_op_type (rtx x, basic_block bb, rtx /* All preserved VALUEs. */ static VEC (rtx, heap) *preserved_values; +/* Registers used in the current function for passing parameters. */ +static HARD_REG_SET argument_reg_set; + /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes. */ static void @@ -5342,6 +5351,15 @@ add_stores (rtx loc, const_rtx expr, voi { mo.type = MO_CLOBBER; mo.u.loc = loc; + if (GET_CODE (expr) == SET + && SET_DEST (expr) == loc + && REGNO (loc) < FIRST_PSEUDO_REGISTER + && TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc)) + && find_use_val (loc, mode, cui)) + { + gcc_checking_assert (type == MO_VAL_SET); + mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr)); + } } else { @@ -5558,6 +5576,195 @@ add_stores (rtx loc, const_rtx expr, voi VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo); } +/* Arguments to the call. */ +static rtx call_arguments; + +/* Compute call_arguments. */ + +static void +prepare_call_arguments (basic_block bb, rtx insn) +{ + rtx link, x; + rtx prev, cur, next; + rtx call = PATTERN (insn); + tree type = NULL_TREE, t; + CUMULATIVE_ARGS args_so_far; + + memset (&args_so_far, 0, sizeof (args_so_far)); + if (GET_CODE (call) == PARALLEL) + call = XVECEXP (call, 0, 0); + if (GET_CODE (call) == SET) + call = SET_SRC (call); + if (GET_CODE (call) == CALL + && MEM_P (XEXP (call, 0)) + && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF) + { + rtx symbol = XEXP (XEXP (call, 0), 0); + if (SYMBOL_REF_DECL (symbol) + && TREE_CODE (SYMBOL_REF_DECL (symbol)) == FUNCTION_DECL + && TYPE_ARG_TYPES (TREE_TYPE (SYMBOL_REF_DECL (symbol)))) + { + type = TREE_TYPE (SYMBOL_REF_DECL (symbol)); + for (t = TYPE_ARG_TYPES (type); t && t != void_list_node; + t = TREE_CHAIN (t)) + if (TREE_CODE (TREE_VALUE (t)) == REFERENCE_TYPE + && INTEGRAL_TYPE_P (TREE_TYPE (TREE_VALUE (t)))) + break; + if (t == NULL || t == void_list_node) + type = NULL; + else + INIT_CUMULATIVE_ARGS (args_so_far, type, NULL_RTX, + SYMBOL_REF_DECL (symbol), + list_length (TYPE_ARG_TYPES (type))); + } + } + t = type ? TYPE_ARG_TYPES (type) : NULL_TREE; + + for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) + if (GET_CODE (XEXP (link, 0)) == USE) + { + rtx item = NULL_RTX; + x = XEXP (XEXP (link, 0), 0); + if (REG_P (x)) + { + cselib_val *val = cselib_lookup (x, GET_MODE (x), 0); + if (val && cselib_preserved_value_p (val)) + item = gen_rtx_CONCAT (GET_MODE (x), x, val->val_rtx); + else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) + { + enum machine_mode mode = GET_MODE (x); + + while ((mode = GET_MODE_WIDER_MODE (mode)) != VOIDmode + && GET_MODE_BITSIZE (mode) <= BITS_PER_WORD) + { + rtx reg = simplify_subreg (mode, x, GET_MODE (x), 0); + + if (reg == NULL_RTX || !REG_P (reg)) + continue; + val = cselib_lookup (reg, mode, 0); + if (val && cselib_preserved_value_p (val)) + { + item = gen_rtx_CONCAT (GET_MODE (x), x, + lowpart_subreg (GET_MODE (x), + val->val_rtx, + mode)); + break; + } + } + } + } + else if (MEM_P (x)) + { + rtx mem = x; + cselib_val *val; + + if (!frame_pointer_needed) + { + struct adjust_mem_data amd; + amd.mem_mode = VOIDmode; + amd.stack_adjust = -VTI (bb)->out.stack_adjust; + amd.side_effects = NULL_RTX; + amd.store = true; + mem = simplify_replace_fn_rtx (mem, NULL_RTX, adjust_mems, + &amd); + gcc_assert (amd.side_effects == NULL_RTX); + } + val = cselib_lookup (mem, GET_MODE (mem), 0); + if (val && cselib_preserved_value_p (val)) + item = gen_rtx_CONCAT (GET_MODE (x), copy_rtx (x), val->val_rtx); + } + if (item) + call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item, call_arguments); + if (t && t != void_list_node) + { + enum machine_mode mode = TYPE_MODE (TREE_VALUE (t)); + rtx reg = targetm.calls.function_arg (&args_so_far, mode, + TREE_VALUE (t), true); + if (TREE_CODE (TREE_VALUE (t)) == REFERENCE_TYPE + && INTEGRAL_TYPE_P (TREE_TYPE (TREE_VALUE (t))) + && reg + && REG_P (reg) + && GET_MODE (reg) == mode + && GET_MODE_CLASS (mode) == MODE_INT + && REG_P (x) + && REGNO (x) == REGNO (reg) + && GET_MODE (x) == mode + && item) + { + enum machine_mode indmode + = TYPE_MODE (TREE_TYPE (TREE_VALUE (t))); + rtx mem = gen_rtx_MEM (indmode, x); + cselib_val *val = cselib_lookup (mem, indmode, 0); + if (val && cselib_preserved_value_p (val)) + { + item = gen_rtx_CONCAT (indmode, mem, val->val_rtx); + call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item, + call_arguments); + } + else + { + struct elt_loc_list *l; + tree initial; + + /* Try harder, when passing address of a constant + pool integer it can be easily read back. */ + val = CSELIB_VAL_PTR (XEXP (item, 1)); + for (l = val->locs; l; l = l->next) + if (GET_CODE (l->loc) == SYMBOL_REF + && TREE_CONSTANT_POOL_ADDRESS_P (l->loc) + && SYMBOL_REF_DECL (l->loc) + && DECL_INITIAL (SYMBOL_REF_DECL (l->loc))) + { + initial = DECL_INITIAL (SYMBOL_REF_DECL (l->loc)); + if (host_integerp (initial, 0)) + { + item = GEN_INT (tree_low_cst (initial, 0)); + item = gen_rtx_CONCAT (indmode, mem, item); + call_arguments + = gen_rtx_EXPR_LIST (VOIDmode, item, + call_arguments); + } + break; + } + } + } + targetm.calls.function_arg_advance (&args_so_far, mode, + TREE_VALUE (t), true); + t = TREE_CHAIN (t); + } + } + + /* Reverse call_arguments chain. */ + prev = NULL_RTX; + for (cur = call_arguments; cur; cur = next) + { + next = XEXP (cur, 1); + XEXP (cur, 1) = prev; + prev = cur; + } + call_arguments = prev; + + x = PATTERN (insn); + if (GET_CODE (x) == PARALLEL) + x = XVECEXP (x, 0, 0); + if (GET_CODE (x) == SET) + x = SET_SRC (x); + if (GET_CODE (x) == CALL && MEM_P (XEXP (x, 0))) + { + x = XEXP (XEXP (x, 0), 0); + if (GET_CODE (x) != SYMBOL_REF) + { + cselib_val *val = cselib_lookup (x, GET_MODE (x), 0); + if (val && cselib_preserved_value_p (val)) + { + x = gen_rtx_CONCAT (GET_MODE (x), pc_rtx, val->val_rtx); + call_arguments + = gen_rtx_EXPR_LIST (VOIDmode, x, call_arguments); + } + } + } +} + /* Callback for cselib_record_sets_hook, that records as micro operations uses and stores in an insn after cselib_record_sets has analyzed the sets in an insn, but before it modifies the stored @@ -5627,7 +5834,8 @@ add_with_sets (rtx insn, struct cselib_s mo.type = MO_CALL; mo.insn = insn; - mo.u.loc = NULL_RTX; + mo.u.loc = call_arguments; + call_arguments = NULL_RTX; if (dump_file && (dump_flags & TDF_DETAILS)) log_op_type (PATTERN (insn), bb, insn, mo.type, dump_file); @@ -6943,6 +7151,10 @@ struct expand_loc_callback_data whose cur_loc has been already recomputed during current emit_notes_for_changes call. */ bool cur_loc_changed; + + /* True if cur_loc should be ignored and any possible location + returned. */ + bool ignore_cur_loc; }; /* Callback for cselib_expand_value, that looks for expressions @@ -6956,6 +7168,7 @@ vt_expand_loc_callback (rtx x, bitmap re = (struct expand_loc_callback_data *) data; bool dummy = elcd->dummy; bool cur_loc_changed = elcd->cur_loc_changed; + rtx cur_loc; decl_or_value dv; variable var; location_chain loc; @@ -7030,7 +7243,7 @@ vt_expand_loc_callback (rtx x, bitmap re VALUE_RECURSED_INTO (x) = true; result = NULL; - if (var->var_part[0].cur_loc) + if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc) { if (dummy) { @@ -7045,12 +7258,16 @@ vt_expand_loc_callback (rtx x, bitmap re vt_expand_loc_callback, data); if (result) set_dv_changed (dv, false); + cur_loc = var->var_part[0].cur_loc; } - if (!result && dv_changed_p (dv)) + else + cur_loc = NULL_RTX; + if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc)) { - set_dv_changed (dv, false); + if (!elcd->ignore_cur_loc) + set_dv_changed (dv, false); for (loc = var->var_part[0].loc_chain; loc; loc = loc->next) - if (loc->loc == var->var_part[0].cur_loc) + if (loc->loc == cur_loc) continue; else if (dummy) { @@ -7072,7 +7289,8 @@ vt_expand_loc_callback (rtx x, bitmap re } if (dummy && (result || var->var_part[0].cur_loc)) var->cur_loc_changed = true; - var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX; + if (!elcd->ignore_cur_loc) + var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX; } if (dummy) { @@ -7093,7 +7311,7 @@ vt_expand_loc_callback (rtx x, bitmap re tables. */ static rtx -vt_expand_loc (rtx loc, htab_t vars) +vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc) { struct expand_loc_callback_data data; @@ -7103,6 +7321,7 @@ vt_expand_loc (rtx loc, htab_t vars) data.vars = vars; data.dummy = false; data.cur_loc_changed = false; + data.ignore_cur_loc = ignore_cur_loc; loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 8, vt_expand_loc_callback, &data); @@ -7124,6 +7343,7 @@ vt_expand_loc_dummy (rtx loc, htab_t var data.vars = vars; data.dummy = true; data.cur_loc_changed = false; + data.ignore_cur_loc = false; ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 8, vt_expand_loc_callback, &data); *pcur_loc_changed = data.cur_loc_changed; @@ -7201,7 +7421,7 @@ emit_note_insn_var_location (void **varp complete = false; continue; } - loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars); + loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false); if (!loc2) { complete = false; @@ -7231,7 +7451,7 @@ emit_note_insn_var_location (void **varp && mode == GET_MODE (var->var_part[j].cur_loc) && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts])) && last_limit == var->var_part[j].offset - && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars)) + && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false)) && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)) { rtx new_loc = NULL; @@ -7692,6 +7912,34 @@ emit_notes_in_bb (basic_block bb, datafl case MO_CALL: dataflow_set_clear_at_call (set); emit_notes_for_changes (insn, EMIT_NOTE_AFTER_CALL_INSN, set->vars); + { + rtx arguments = mo->u.loc, *p = &arguments, note; + while (*p) + { + XEXP (XEXP (*p, 0), 1) + = vt_expand_loc (XEXP (XEXP (*p, 0), 1), + shared_hash_htab (set->vars), true); + /* If expansion is successful, keep it in the list. */ + if (XEXP (XEXP (*p, 0), 1)) + p = &XEXP (*p, 1); + /* Otherwise, if the following item is data_value for it, + drop it too too. */ + else if (XEXP (*p, 1) + && REG_P (XEXP (XEXP (*p, 0), 0)) + && MEM_P (XEXP (XEXP (XEXP (*p, 1), 0), 0)) + && REG_P (XEXP (XEXP (XEXP (XEXP (*p, 1), 0), 0), + 0)) + && REGNO (XEXP (XEXP (*p, 0), 0)) + == REGNO (XEXP (XEXP (XEXP (XEXP (*p, 1), 0), + 0), 0))) + *p = XEXP (XEXP (*p, 1), 1); + /* Just drop this item. */ + else + *p = XEXP (*p, 1); + } + note = emit_note_after (NOTE_INSN_CALL_ARG_LOCATION, insn); + NOTE_VAR_LOCATION (note) = arguments; + } break; case MO_USE: @@ -8139,7 +8387,8 @@ vt_add_function_parameters (void) if (offset) continue; - val = cselib_lookup (var_lowpart (mode, incoming), mode, true); + val = cselib_lookup_from_insn (var_lowpart (mode, incoming), mode, + true, get_insns ()); /* ??? Float-typed values in memory are not handled by cselib. */ @@ -8160,6 +8409,36 @@ vt_add_function_parameters (void) incoming); set_variable_part (out, incoming, dv, offset, VAR_INIT_STATUS_INITIALIZED, NULL, INSERT); + if (dv_is_value_p (dv)) + { + cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv)); + struct elt_loc_list *el; + el = (struct elt_loc_list *) + ggc_alloc_cleared_atomic (sizeof (*el)); + el->next = val->locs; + el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (incoming), incoming); + el->setting_insn = get_insns (); + val->locs = el; + if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE + && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm)))) + { + enum machine_mode indmode + = TYPE_MODE (TREE_TYPE (TREE_TYPE (parm))); + rtx mem = gen_rtx_MEM (indmode, incoming); + val = cselib_lookup_from_insn (mem, indmode, true, + get_insns ()); + if (val) + { + preserve_value (val); + el = (struct elt_loc_list *) + ggc_alloc_cleared_atomic (sizeof (*el)); + el->next = val->locs; + el->loc = gen_rtx_ENTRY_VALUE (indmode, mem); + el->setting_insn = get_insns (); + val->locs = el; + } + } + } } else if (MEM_P (incoming)) { @@ -8168,13 +8447,6 @@ vt_add_function_parameters (void) VAR_INIT_STATUS_INITIALIZED, NULL, INSERT); } } - - if (MAY_HAVE_DEBUG_INSNS) - { - cselib_preserve_only_values (); - cselib_reset_table (cselib_get_next_uid ()); - } - } /* Return true if INSN in the prologue initializes hard_frame_pointer_rtx. */ @@ -8202,6 +8474,23 @@ fp_setter (rtx insn) return false; } +/* Gather all registers used for passing arguments to other functions + called from the current routine. */ + +static void +note_register_arguments (rtx insn) +{ + rtx link, x; + + for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) + if (GET_CODE (XEXP (link, 0)) == USE) + { + x = XEXP (XEXP (link, 0), 0); + if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) + SET_HARD_REG_BIT (argument_reg_set, REGNO (x)); + } +} + /* Initialize cfa_base_rtx, create a preserved VALUE for it and ensure it isn't flushed during cselib_reset_table. Can be called only if frame_pointer_rtx resp. arg_pointer_rtx @@ -8297,6 +8586,8 @@ vt_initialize (void) valvar_pool = NULL; } + CLEAR_HARD_REG_SET (argument_reg_set); + if (!frame_pointer_needed) { rtx reg, elim; @@ -8343,9 +8634,18 @@ vt_initialize (void) prologue_bb = single_succ (ENTRY_BLOCK_PTR); } } + if (frame_pointer_needed) + { + rtx insn; + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (CALL_P (insn)) + note_register_arguments (insn); + } hard_frame_pointer_adjustment = -1; + vt_add_function_parameters (); + FOR_EACH_BB (bb) { rtx insn; @@ -8406,6 +8706,8 @@ vt_initialize (void) adjust_insn (bb, insn); if (MAY_HAVE_DEBUG_INSNS) { + if (CALL_P (insn)) + prepare_call_arguments (bb, insn); cselib_process_insn (insn); if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -8456,7 +8758,6 @@ vt_initialize (void) hard_frame_pointer_adjustment = -1; VTI (ENTRY_BLOCK_PTR)->flooded = true; - vt_add_function_parameters (); cfa_base_rtx = NULL_RTX; return true; } --- gcc/calls.c.jj 2010-07-22 11:35:37.000000000 +0200 +++ gcc/calls.c 2010-08-13 12:44:17.000000000 +0200 @@ -2756,9 +2756,7 @@ expand_call (tree exp, rtx target, int i sibcall_failure = 1; } - if (((flags & ECF_CONST) - || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS)) - && args[i].stack) + if (args[i].stack) call_fusage = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, args[i].stack), @@ -3647,6 +3645,8 @@ emit_library_call_value_1 (int retval, r if (! (reg != 0 && partial == 0)) { + rtx use; + if (ACCUMULATE_OUTGOING_ARGS) { /* If this is being stored into a pre-allocated, fixed-size, @@ -3717,28 +3717,22 @@ emit_library_call_value_1 (int retval, r NO_DEFER_POP; - if ((flags & ECF_CONST) - || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS)) - { - rtx use; - - /* Indicate argument access so that alias.c knows that these - values are live. */ - if (argblock) - use = plus_constant (argblock, - argvec[argnum].locate.offset.constant); - else - /* When arguments are pushed, trying to tell alias.c where - exactly this argument is won't work, because the - auto-increment causes confusion. So we merely indicate - that we access something with a known mode somewhere on - the stack. */ - use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx, - gen_rtx_SCRATCH (Pmode)); - use = gen_rtx_MEM (argvec[argnum].mode, use); - use = gen_rtx_USE (VOIDmode, use); - call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage); - } + /* Indicate argument access so that alias.c knows that these + values are live. */ + if (argblock) + use = plus_constant (argblock, + argvec[argnum].locate.offset.constant); + else + /* When arguments are pushed, trying to tell alias.c where + exactly this argument is won't work, because the + auto-increment causes confusion. So we merely indicate + that we access something with a known mode somewhere on + the stack. */ + use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx, + gen_rtx_SCRATCH (Pmode)); + use = gen_rtx_MEM (argvec[argnum].mode, use); + use = gen_rtx_USE (VOIDmode, use); + call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage); } } --- gcc/cfglayout.h.jj 2010-03-31 13:11:56.000000000 +0200 +++ gcc/cfglayout.h 2010-08-13 12:44:17.000000000 +0200 @@ -27,6 +27,7 @@ extern GTY(()) rtx cfg_layout_function_h extern void cfg_layout_initialize (unsigned int); extern void cfg_layout_finalize (void); +extern tree insn_scope (const_rtx); extern void reemit_insn_block_notes (void); extern bool can_copy_bbs_p (basic_block *, unsigned); extern void copy_bbs (basic_block *, unsigned, basic_block *, --- gcc/insn-notes.def.jj 2010-03-31 13:11:55.000000000 +0200 +++ gcc/insn-notes.def 2010-08-13 12:44:17.000000000 +0200 @@ -61,6 +61,9 @@ INSN_NOTE (EH_REGION_END) /* The location of a variable. */ INSN_NOTE (VAR_LOCATION) +/* The values passed to callee. */ +INSN_NOTE (CALL_ARG_LOCATION) + /* Record the struct for the following basic block. Uses NOTE_BASIC_BLOCK. FIXME: Redundant with the basic block pointer now included in every insn. */ --- gcc/gengtype.c.jj 2010-08-11 21:08:06.000000000 +0200 +++ gcc/gengtype.c 2010-08-13 12:44:17.000000000 +0200 @@ -1047,6 +1047,7 @@ adjust_field_rtx_def (type_p t, options_ break; case NOTE_INSN_VAR_LOCATION: + case NOTE_INSN_CALL_ARG_LOCATION: note_flds = create_field (note_flds, rtx_tp, "rt_rtx"); break; --- gcc/dwarf2out.c.jj 2010-08-13 10:18:21.000000000 +0200 +++ gcc/dwarf2out.c 2010-08-16 17:51:53.000000000 +0200 @@ -92,6 +92,7 @@ along with GCC; see the file COPYING3. #include "gimple.h" #include "tree-pass.h" #include "tree-flow.h" +#include "cfglayout.h" static void dwarf2out_source_line (unsigned int, const char *, int, bool); static rtx last_var_location_insn; @@ -4700,6 +4701,8 @@ dwarf_stack_op_name (unsigned int op) return "DW_OP_GNU_encoded_addr"; case DW_OP_GNU_implicit_pointer: return "DW_OP_GNU_implicit_pointer"; + case DW_OP_GNU_entry_value: + return "DW_OP_GNU_entry_value"; default: return "OP_"; @@ -4806,6 +4809,8 @@ loc_list_plus_const (dw_loc_list_ref lis #define DWARF_REF_SIZE \ (dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE) +static unsigned long size_of_locs (dw_loc_descr_ref); + /* Return the size of a location descriptor. */ static unsigned long @@ -4921,6 +4926,12 @@ size_of_loc_descr (dw_loc_descr_ref loc) case DW_OP_GNU_implicit_pointer: size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); break; + case DW_OP_GNU_entry_value: + { + unsigned long op_size = size_of_locs (loc->dw_loc_oprnd1.v.val_loc); + size += size_of_uleb128 (op_size) + op_size; + break; + } default: break; } @@ -4959,6 +4970,8 @@ size_of_locs (dw_loc_descr_ref loc) static HOST_WIDE_INT extract_int (const unsigned char *, unsigned); static void get_ref_die_offset_label (char *, dw_die_ref); +static void output_loc_sequence (dw_loc_descr_ref); + /* Output location description stack opcode's operands (if any). */ static void @@ -5188,6 +5201,11 @@ output_loc_operands (dw_loc_descr_ref lo } break; + case DW_OP_GNU_entry_value: + dw2_asm_output_data_uleb128 (size_of_locs (val1->v.val_loc), NULL); + output_loc_sequence (val1->v.val_loc); + break; + default: /* Other codes have no operands. */ break; @@ -5327,6 +5345,7 @@ output_loc_operands_raw (dw_loc_descr_re break; case DW_OP_GNU_implicit_pointer: + case DW_OP_GNU_entry_value: gcc_unreachable (); break; @@ -5932,10 +5951,33 @@ struct GTY (()) var_loc_list_def { }; typedef struct var_loc_list_def var_loc_list; +/* Call argument location list. */ +struct GTY ((chain_next ("%h.next"))) call_arg_loc_node { + rtx GTY (()) call_arg_loc_note; + const char * GTY (()) label; + tree GTY (()) block; + bool tail_call_p; + rtx GTY (()) symbol_ref; + struct call_arg_loc_node * GTY (()) next; +}; + /* Table of decl location linked lists. */ static GTY ((param_is (var_loc_list))) htab_t decl_loc_table; +/* Head and tail of call_arg_loc chain. */ +static GTY (()) struct call_arg_loc_node *call_arg_locations; +static struct call_arg_loc_node *call_arg_loc_last; + +/* Number of call sites in the current function. */ +static int call_site_count = -1; +/* Number of tail call sites in the current function. */ +static int tail_call_site_count = -1; + +/* Vector mapping block numbers to DW_TAG_{lexical_block,inlined_subroutine} + DIEs. */ +static VEC (dw_die_ref, heap) *block_map; + /* A pointer to the base of a list of references to DIE's that are uniquely identified by their tag, presence/absence of children DIE's, and list of attribute/value pairs. */ @@ -6700,6 +6742,10 @@ dwarf_tag_name (unsigned int tag) return "DW_TAG_GNU_EINCL"; case DW_TAG_GNU_template_template_param: return "DW_TAG_GNU_template_template_param"; + case DW_TAG_GNU_call_site: + return "DW_TAG_GNU_call_site"; + case DW_TAG_GNU_call_site_parameter: + return "DW_TAG_GNU_call_site_parameter"; default: return "DW_TAG_"; } @@ -6944,6 +6990,22 @@ dwarf_attr_name (unsigned int attr) return "DW_AT_GNU_odr_signature"; case DW_AT_GNU_template_name: return "DW_AT_GNU_template_name"; + case DW_AT_GNU_call_site_value: + return "DW_AT_GNU_call_site_value"; + case DW_AT_GNU_call_site_data_value: + return "DW_AT_GNU_call_site_data_value"; + case DW_AT_GNU_call_site_target: + return "DW_AT_GNU_call_site_target"; + case DW_AT_GNU_call_site_target_clobbered: + return "DW_AT_GNU_call_site_target_clobbered"; + case DW_AT_GNU_tail_call: + return "DW_AT_GNU_tail_call"; + case DW_AT_GNU_all_tail_call_sites: + return "DW_AT_GNU_all_tail_call_sites"; + case DW_AT_GNU_all_call_sites: + return "DW_AT_GNU_all_call_sites"; + case DW_AT_GNU_all_source_call_sites: + return "DW_AT_GNU_all_source_call_sites"; case DW_AT_VMS_rtnbeg_pd_address: return "DW_AT_VMS_rtnbeg_pd_address"; @@ -13441,11 +13503,18 @@ const_ok_for_output_1 (rtx *rtlp, void * /* If delegitimize_address couldn't do anything with the UNSPEC, assume we can't express it in the debug info. */ #ifdef ENABLE_CHECKING - inform (current_function_decl - ? DECL_SOURCE_LOCATION (current_function_decl) - : UNKNOWN_LOCATION, - "non-delegitimized UNSPEC %d found in variable location", - XINT (rtl, 1)); + if (XVECLEN (rtl, 0) != 1 + || GET_CODE (XVECEXP (rtl, 0, 0)) != SYMBOL_REF + || SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0)) == NULL + || TREE_CODE (SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0))) != VAR_DECL + || !DECL_THREAD_LOCAL_P (SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0)))) + /* Don't complain about TLS UNSPECs, those are just too hard to + delegitimize. */ + inform (current_function_decl + ? DECL_SOURCE_LOCATION (current_function_decl) + : UNKNOWN_LOCATION, + "non-delegitimized UNSPEC %d found in variable location", + XINT (rtl, 1)); #endif expansion_failed (NULL_TREE, rtl, "UNSPEC hasn't been delegitimized.\n"); @@ -13695,6 +13764,26 @@ mem_loc_descriptor (rtx rtl, enum machin "CONCAT/CONCATN/VAR_LOCATION is handled only by loc_descriptor"); return 0; + case ENTRY_VALUE: + mem_loc_result = new_loc_descr (DW_OP_GNU_entry_value, 0, 0); + mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_loc; + if (REG_P (XEXP (rtl, 0))) + mem_loc_result->dw_loc_oprnd1.v.val_loc + = one_reg_loc_descriptor (dbx_reg_number (XEXP (rtl, 0)), + VAR_INIT_STATUS_INITIALIZED); + else if (MEM_P (XEXP (rtl, 0)) && REG_P (XEXP (XEXP (rtl, 0), 0))) + { + dw_loc_descr_ref ref + = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl), + VAR_INIT_STATUS_INITIALIZED); + if (ref == NULL) + return NULL; + mem_loc_result->dw_loc_oprnd1.v.val_loc = ref; + } + else + gcc_unreachable (); + return mem_loc_result; + case PRE_MODIFY: /* Extract the PLUS expression nested inside and fall into PLUS code below. */ @@ -17531,8 +17620,11 @@ add_linkage_attr (dw_die_ref die, tree d static void add_src_coords_attributes (dw_die_ref die, tree decl) { - expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl)); + expanded_location s; + if (DECL_SOURCE_LOCATION (decl) == UNKNOWN_LOCATION) + return; + s = expand_location (DECL_SOURCE_LOCATION (decl)); add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file)); add_AT_unsigned (die, DW_AT_decl_line, s.line); } @@ -18545,6 +18637,8 @@ dwarf2out_abstract_function (tree decl) tree context; int was_abstract; htab_t old_decl_loc_table; + int old_call_site_count, old_tail_call_site_count; + struct call_arg_loc_node *old_call_arg_locations; /* Make sure we have the actual abstract inline, not a clone. */ decl = DECL_ORIGIN (decl); @@ -18559,6 +18653,12 @@ dwarf2out_abstract_function (tree decl) get locations in abstract instantces. */ old_decl_loc_table = decl_loc_table; decl_loc_table = NULL; + old_call_arg_locations = call_arg_locations; + call_arg_locations = NULL; + old_call_site_count = call_site_count; + call_site_count = -1; + old_tail_call_site_count = tail_call_site_count; + tail_call_site_count = -1; /* Be sure we've emitted the in-class declaration DIE (if any) first, so we don't get confused by DECL_ABSTRACT. */ @@ -18583,6 +18683,9 @@ dwarf2out_abstract_function (tree decl) current_function_decl = save_fn; decl_loc_table = old_decl_loc_table; + call_arg_locations = old_call_arg_locations; + call_site_count = old_call_site_count; + tail_call_site_count = old_tail_call_site_count; pop_cfun (); } @@ -18658,6 +18761,43 @@ premark_types_used_by_global_vars (void) premark_types_used_by_global_vars_helper, NULL); } +/* Generate a DW_TAG_GNU_call_site DIE in function DECL under SUBR_DIE + for CA_LOC call arg loc node. */ + +static dw_die_ref +gen_call_site_die (tree decl, dw_die_ref subr_die, + struct call_arg_loc_node *ca_loc) +{ + dw_die_ref stmt_die = NULL, die; + tree block = ca_loc->block; + + while (block + && block != DECL_INITIAL (decl) + && TREE_CODE (block) == BLOCK) + { + if (VEC_length (dw_die_ref, block_map) > BLOCK_NUMBER (block)) + stmt_die = VEC_index (dw_die_ref, block_map, BLOCK_NUMBER (block)); + if (stmt_die) + break; + block = BLOCK_SUPERCONTEXT (block); + } + if (stmt_die == NULL) + stmt_die = subr_die; + die = new_die (DW_TAG_GNU_call_site, stmt_die, NULL_TREE); + add_AT_lbl_id (die, DW_AT_low_pc, ca_loc->label); + if (ca_loc->tail_call_p) + add_AT_flag (die, DW_AT_GNU_tail_call, 1); + if (ca_loc->symbol_ref) + { + dw_die_ref tdie = lookup_decl_die (SYMBOL_REF_DECL (ca_loc->symbol_ref)); + if (tdie) + add_AT_die_ref (die, DW_AT_abstract_origin, tdie); + else + add_AT_addr (die, DW_AT_abstract_origin, ca_loc->symbol_ref); + } + return die; +} + /* Generate a DIE to represent a declared function (either file-scope or block-local). */ @@ -19036,6 +19176,9 @@ gen_subprogram_die (tree decl, dw_die_re constructor function. */ if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) { + int call_site_note_count = 0; + int tail_call_site_note_count = 0; + /* 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); @@ -19054,6 +19197,104 @@ gen_subprogram_die (tree decl, dw_die_re } } #endif + + if (call_arg_locations) + { + struct call_arg_loc_node *ca_loc; + for (ca_loc = call_arg_locations; ca_loc; ca_loc = ca_loc->next) + { + dw_die_ref die = NULL; + rtx tloc = NULL_RTX; + rtx arg, next_arg; + + for (arg = NOTE_VAR_LOCATION (ca_loc->call_arg_loc_note); + arg; arg = next_arg) + { + dw_loc_descr_ref reg, val; + enum machine_mode mode = GET_MODE (XEXP (XEXP (arg, 0), 1)); + dw_die_ref cdie; + + next_arg = XEXP (arg, 1); + if (REG_P (XEXP (XEXP (arg, 0), 0)) + && next_arg + && MEM_P (XEXP (XEXP (next_arg, 0), 0)) + && REG_P (XEXP (XEXP (XEXP (next_arg, 0), 0), 0)) + && REGNO (XEXP (XEXP (arg, 0), 0)) + == REGNO (XEXP (XEXP (XEXP (next_arg, 0), 0), 0))) + next_arg = XEXP (next_arg, 1); + if (mode == VOIDmode) + mode = GET_MODE (XEXP (XEXP (arg, 0), 0)); + if (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE) + continue; + if (XEXP (XEXP (arg, 0), 0) == pc_rtx) + { + gcc_assert (ca_loc->symbol_ref == NULL_RTX); + tloc = XEXP (XEXP (arg, 0), 1); + continue; + } + if (REG_P (XEXP (XEXP (arg, 0), 0))) + reg = reg_loc_descriptor (XEXP (XEXP (arg, 0), 0), + VAR_INIT_STATUS_INITIALIZED); + else if (MEM_P (XEXP (XEXP (arg, 0), 0))) + reg = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 0), + 0), 0), mode, + VAR_INIT_STATUS_INITIALIZED); + else + continue; + if (reg == NULL) + continue; + val = mem_loc_descriptor (XEXP (XEXP (arg, 0), 1), VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (val == NULL) + continue; + if (die == NULL) + die = gen_call_site_die (decl, subr_die, ca_loc); + cdie = new_die (DW_TAG_GNU_call_site_parameter, die, + NULL_TREE); + add_AT_loc (cdie, DW_AT_location, reg); + add_AT_loc (cdie, DW_AT_GNU_call_site_value, val); + if (next_arg != XEXP (arg, 1)) + { + val = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 1), + 0), 1), VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (val != NULL) + add_AT_loc (cdie, DW_AT_GNU_call_site_data_value, val); + } + } + if (die == NULL + && (ca_loc->symbol_ref || tloc)) + die = gen_call_site_die (decl, subr_die, ca_loc); + if (die != NULL && tloc != NULL_RTX) + { + dw_loc_descr_ref tval + = mem_loc_descriptor (tloc, VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (tval) + add_AT_loc (die, DW_AT_GNU_call_site_target, tval); + } + if (die != NULL) + { + call_site_note_count++; + if (ca_loc->tail_call_p) + tail_call_site_note_count++; + } + } + call_arg_locations = NULL; + call_arg_loc_last = NULL; + } + if (tail_call_site_count >= 0 + && tail_call_site_count == tail_call_site_note_count) + { + if (call_site_count >= 0 + && call_site_count == call_site_note_count) + add_AT_flag (subr_die, DW_AT_GNU_all_call_sites, 1); + else + add_AT_flag (subr_die, DW_AT_GNU_all_tail_call_sites, 1); + } + call_site_count = -1; + tail_call_site_count = -1; } /* Add the calling convention attribute if requested. */ add_calling_convention_attribute (subr_die, decl); @@ -19442,6 +19683,14 @@ gen_lexical_block_die (tree stmt, dw_die { dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt); + if (call_arg_locations) + { + if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt)) + VEC_safe_grow_cleared (dw_die_ref, heap, block_map, + BLOCK_NUMBER (stmt) + 1); + VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), stmt_die); + } + if (! BLOCK_ABSTRACT (stmt) && TREE_ASM_WRITTEN (stmt)) add_high_low_attributes (stmt, stmt_die); @@ -19472,6 +19721,13 @@ gen_inlined_subroutine_die (tree stmt, d dw_die_ref subr_die = new_die (DW_TAG_inlined_subroutine, context_die, stmt); + if (call_arg_locations) + { + if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt)) + VEC_safe_grow_cleared (dw_die_ref, heap, block_map, + BLOCK_NUMBER (stmt) + 1); + VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), subr_die); + } add_abstract_origin_attribute (subr_die, decl); if (TREE_ASM_WRITTEN (stmt)) add_high_low_attributes (stmt, subr_die); @@ -21027,7 +21283,11 @@ static void dwarf2out_function_decl (tree decl) { dwarf2out_decl (decl); - + call_arg_locations = NULL; + call_arg_loc_last = NULL; + call_site_count = -1; + tail_call_site_count = -1; + VEC_free (dw_die_ref, heap, block_map); htab_empty (decl_loc_table); } @@ -21381,16 +21641,35 @@ dwarf2out_var_location (rtx loc_note) static const char *last_postcall_label; static bool last_in_cold_section_p; tree decl; + bool var_loc_p; + + if (!NOTE_P (loc_note)) + { + if (CALL_P (loc_note)) + { + call_site_count++; + if (SIBLING_CALL_P (loc_note)) + tail_call_site_count++; + } + return; + } - if (!DECL_P (NOTE_VAR_LOCATION_DECL (loc_note))) + var_loc_p = NOTE_KIND (loc_note) == NOTE_INSN_VAR_LOCATION; + if (var_loc_p && !DECL_P (NOTE_VAR_LOCATION_DECL (loc_note))) return; next_real = next_real_insn (loc_note); + /* If there are no instructions which would be affected by this note, don't do anything. */ - if (next_real == NULL_RTX && !NOTE_DURING_CALL_P (loc_note)) + if (var_loc_p + && next_real == NULL_RTX + && !NOTE_DURING_CALL_P (loc_note)) return; + if (next_real == NULL_RTX) + next_real = get_last_insn (); + /* If there were any real insns between note we processed last time and this note (or if it is the first note), clear last_{,postcall_}label so that they are not reused this time. */ @@ -21402,12 +21681,20 @@ dwarf2out_var_location (rtx loc_note) last_postcall_label = NULL; } - decl = NOTE_VAR_LOCATION_DECL (loc_note); - newloc = add_var_loc_to_decl (decl, loc_note, - NOTE_DURING_CALL_P (loc_note) - ? last_postcall_label : last_label); - if (newloc == NULL) - return; + if (var_loc_p) + { + decl = NOTE_VAR_LOCATION_DECL (loc_note); + newloc = add_var_loc_to_decl (decl, loc_note, + NOTE_DURING_CALL_P (loc_note) + ? last_postcall_label : last_label); + if (newloc == NULL) + return; + } + else + { + decl = NULL_TREE; + newloc = NULL; + } /* If there were no real insns between note we processed last time and this note, use the label we emitted last time. Otherwise @@ -21420,7 +21707,43 @@ dwarf2out_var_location (rtx loc_note) last_label = ggc_strdup (loclabel); } - if (!NOTE_DURING_CALL_P (loc_note)) + if (!var_loc_p) + { + struct call_arg_loc_node *ca_loc + = ggc_alloc_cleared_call_arg_loc_node (); + rtx prev = prev_real_insn (loc_note), x; + ca_loc->call_arg_loc_note = loc_note; + ca_loc->next = NULL; + ca_loc->label = last_label; + gcc_assert (prev + && (CALL_P (prev) + || (NONJUMP_INSN_P (prev) + && GET_CODE (PATTERN (prev)) == SEQUENCE + && CALL_P (XVECEXP (PATTERN (prev), 0, 0))))); + if (!CALL_P (prev)) + prev = XVECEXP (PATTERN (prev), 0, 0); + ca_loc->tail_call_p = SIBLING_CALL_P (prev); + x = PATTERN (prev); + if (GET_CODE (x) == PARALLEL) + x = XVECEXP (x, 0, 0); + if (GET_CODE (x) == SET) + x = SET_SRC (x); + if (GET_CODE (x) == CALL && MEM_P (XEXP (x, 0))) + { + x = XEXP (XEXP (x, 0), 0); + if (GET_CODE (x) == SYMBOL_REF + && SYMBOL_REF_DECL (x) + && TREE_CODE (SYMBOL_REF_DECL (x)) == FUNCTION_DECL) + ca_loc->symbol_ref = x; + } + ca_loc->block = insn_scope (prev); + if (call_arg_locations) + call_arg_loc_last->next = ca_loc; + else + call_arg_locations = ca_loc; + call_arg_loc_last = ca_loc; + } + else if (!NOTE_DURING_CALL_P (loc_note)) newloc->label = last_label; else { @@ -21448,6 +21771,8 @@ dwarf2out_begin_function (tree fun) have_multiple_function_sections = true; dwarf2out_note_section_used (); + call_site_count = 0; + tail_call_site_count = 0; } /* Output a label to mark the beginning of a source code line entry @@ -22225,9 +22550,16 @@ resolve_one_addr (rtx *addr, void *data } if (GET_CODE (rtl) == SYMBOL_REF - && SYMBOL_REF_DECL (rtl) - && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl))) - return 1; + && SYMBOL_REF_DECL (rtl)) + { + if (TREE_CONSTANT_POOL_ADDRESS_P (rtl)) + { + if (!TREE_ASM_WRITTEN (DECL_INITIAL (SYMBOL_REF_DECL (rtl)))) + return 1; + } + else if (!TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl))) + return 1; + } if (GET_CODE (rtl) == CONST && for_each_rtx (&XEXP (rtl, 0), resolve_one_addr, NULL)) @@ -22319,6 +22651,28 @@ resolve_addr (dw_die_ref die) remove_AT (die, a->dw_attr); ix--; } + if (die->die_tag == DW_TAG_GNU_call_site + && a->dw_attr == DW_AT_abstract_origin) + { + tree tdecl = SYMBOL_REF_DECL (a->dw_attr_val.v.val_addr); + dw_die_ref tdie = lookup_decl_die (tdecl); + if (tdie == NULL && DECL_EXTERNAL (tdecl)) + { + force_decl_die (tdecl); + tdie = lookup_decl_die (tdecl); + } + if (tdie) + { + a->dw_attr_val.val_class = dw_val_class_die_ref; + a->dw_attr_val.v.val_die_ref.die = tdie; + a->dw_attr_val.v.val_die_ref.external = 0; + } + else + { + remove_AT (die, a->dw_attr); + ix--; + } + } break; default: break; --- gcc/cfglayout.c.jj 2010-06-07 11:25:05.000000000 +0200 +++ gcc/cfglayout.c 2010-08-13 12:44:17.000000000 +0200 @@ -54,7 +54,6 @@ static void change_scope (rtx, tree, tre void verify_insn_chain (void); static void fixup_fallthru_exit_predecessor (void); -static tree insn_scope (const_rtx); rtx unlink_insn_chain (rtx first, rtx last) @@ -499,7 +498,7 @@ locator_scope (int loc) } /* Return lexical scope block insn belongs to. */ -static tree +tree insn_scope (const_rtx insn) { return locator_scope (INSN_LOCATOR (insn)); --- include/dwarf2.h.jj 2010-08-13 10:18:27.000000000 +0200 +++ include/dwarf2.h 2010-08-13 12:44:22.000000000 +0200 @@ -228,6 +228,8 @@ enum dwarf_tag are properly part of DWARF 5. */ DW_TAG_GNU_template_parameter_pack = 0x4107, DW_TAG_GNU_formal_parameter_pack = 0x4108, + DW_TAG_GNU_call_site = 0x4109, + DW_TAG_GNU_call_site_parameter = 0x410a, /* Extensions for UPC. See: http://upc.gwu.edu/~upc. */ DW_TAG_upc_shared_type = 0x8765, DW_TAG_upc_strict_type = 0x8766, @@ -438,6 +440,14 @@ enum dwarf_attribute /* Template template argument name. See http://gcc.gnu.org/wiki/TemplateParmsDwarf . */ DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, /* VMS extensions. */ DW_AT_VMS_rtnbeg_pd_address = 0x2201, /* GNAT extensions. */ @@ -623,6 +633,7 @@ enum dwarf_location_atom DW_OP_GNU_uninit = 0xf0, DW_OP_GNU_encoded_addr = 0xf1, DW_OP_GNU_implicit_pointer = 0xf2, + DW_OP_GNU_entry_value = 0xf3, /* HP extensions. */ DW_OP_HP_unknown = 0xe0, /* Ouch, the same as GNU_push_tls_address. */ DW_OP_HP_is_value = 0xe1,