3536ff2de8
As mentioned in the PR, when compiling valgrind even on fairly small testcase where in one larger function the location keeps oscillating between a small line number and 8000-ish line number in the same file we very quickly run out of all possible location_t numbers and because of that emit non-sensical line numbers in .debug_line. There are ways how to decrease speed of depleting location_t numbers in libcpp, but the main reason of this is that we use stream_input_location_now for streaming in location_t for gimple_location and phi arg locations. libcpp strongly prefers that the locations it is given are sorted by the different files and by line numbers in ascending order, otherwise it depletes quickly no matter what and is much more costly (many extra file changes etc.). The reason for not caching those were the BLOCKs that were streamed immediately after the location and encoded into the locations (and for PHIs we failed to stream the BLOCKs altogether). This patch enhances the location cache to handle also BLOCKs (but not for everything, only for the spots we care about the BLOCKs) and also optimizes the size of the LTO stream by emitting a single bit into a pack whether the BLOCK changed from last case and only streaming the BLOCK tree if it changed. 2020-09-03 Jakub Jelinek <jakub@redhat.com> PR lto/94311 * gimple.h (gimple_location_ptr, gimple_phi_arg_location_ptr): New functions. * streamer-hooks.h (struct streamer_hooks): Add output_location_and_block callback. Fix up formatting for output_location. (stream_output_location_and_block): Define. * lto-streamer.h (class lto_location_cache): Fix comment typo. Add current_block member. (lto_location_cache::input_location_and_block): New method. (lto_location_cache::lto_location_cache): Initialize current_block. (lto_location_cache::cached_location): Add block member. (struct output_block): Add current_block member. (lto_output_location): Formatting fix. (lto_output_location_and_block): Declare. * lto-streamer.c (lto_streamer_hooks_init): Initialize streamer_hooks.output_location_and_block. * lto-streamer-in.c (lto_location_cache::cmp_loc): Also compare block members. (lto_location_cache::apply_location_cache): Handle blocks. (lto_location_cache::accept_location_cache, lto_location_cache::revert_location_cache): Fix up function comments. (lto_location_cache::input_location_and_block): New method. (lto_location_cache::input_location): Implement using input_location_and_block. (input_function): Invoke apply_location_cache after streaming in all bbs. * lto-streamer-out.c (clear_line_info): Set current_block. (lto_output_location_1): New function, moved from lto_output_location, added block handling. (lto_output_location): Implement using lto_output_location_1. (lto_output_location_and_block): New function. * gimple-streamer-in.c (input_phi): Use input_location_and_block to input and cache both location and block. (input_gimple_stmt): Likewise. * gimple-streamer-out.c (output_phi): Use stream_output_location_and_block. (output_gimple_stmt): Likewise.
258 lines
7.4 KiB
C
258 lines
7.4 KiB
C
/* Routines for emitting GIMPLE to a file stream.
|
|
|
|
Copyright (C) 2011-2020 Free Software Foundation, Inc.
|
|
Contributed by Diego Novillo <dnovillo@google.com>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "backend.h"
|
|
#include "tree.h"
|
|
#include "gimple.h"
|
|
#include "gimple-ssa.h"
|
|
#include "gimple-streamer.h"
|
|
#include "tree-eh.h"
|
|
#include "gimple-iterator.h"
|
|
#include "cgraph.h"
|
|
#include "value-prof.h"
|
|
#include "gimple-pretty-print.h"
|
|
|
|
/* Output PHI function PHI to the main stream in OB. */
|
|
|
|
static void
|
|
output_phi (struct output_block *ob, gphi *phi)
|
|
{
|
|
unsigned i, len = gimple_phi_num_args (phi);
|
|
|
|
streamer_write_record_start (ob, lto_gimple_code_to_tag (GIMPLE_PHI));
|
|
streamer_write_uhwi (ob, SSA_NAME_VERSION (PHI_RESULT (phi)));
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
stream_write_tree (ob, gimple_phi_arg_def (phi, i), true);
|
|
streamer_write_uhwi (ob, gimple_phi_arg_edge (phi, i)->src->index);
|
|
bitpack_d bp = bitpack_create (ob->main_stream);
|
|
location_t loc = gimple_phi_arg_location (phi, i);
|
|
stream_output_location_and_block (ob, &bp, loc);
|
|
}
|
|
}
|
|
|
|
|
|
/* Emit statement STMT on the main stream of output block OB. */
|
|
|
|
static void
|
|
output_gimple_stmt (struct output_block *ob, struct function *fn, gimple *stmt)
|
|
{
|
|
unsigned i;
|
|
enum gimple_code code;
|
|
enum LTO_tags tag;
|
|
struct bitpack_d bp;
|
|
histogram_value hist;
|
|
|
|
/* Emit identifying tag. */
|
|
code = gimple_code (stmt);
|
|
tag = lto_gimple_code_to_tag (code);
|
|
streamer_write_record_start (ob, tag);
|
|
|
|
/* Emit the tuple header. */
|
|
bp = bitpack_create (ob->main_stream);
|
|
bp_pack_var_len_unsigned (&bp, gimple_num_ops (stmt));
|
|
bp_pack_value (&bp, gimple_no_warning_p (stmt), 1);
|
|
if (is_gimple_assign (stmt))
|
|
bp_pack_value (&bp,
|
|
gimple_assign_nontemporal_move_p (
|
|
as_a <gassign *> (stmt)),
|
|
1);
|
|
bp_pack_value (&bp, gimple_has_volatile_ops (stmt), 1);
|
|
hist = gimple_histogram_value (fn, stmt);
|
|
bp_pack_value (&bp, hist != NULL, 1);
|
|
bp_pack_var_len_unsigned (&bp, stmt->subcode);
|
|
|
|
/* Emit location information for the statement, including gimple_block. */
|
|
stream_output_location_and_block (ob, &bp, gimple_location (stmt));
|
|
|
|
/* Emit the operands. */
|
|
switch (gimple_code (stmt))
|
|
{
|
|
case GIMPLE_RESX:
|
|
streamer_write_hwi (ob, gimple_resx_region (as_a <gresx *> (stmt)));
|
|
break;
|
|
|
|
case GIMPLE_EH_MUST_NOT_THROW:
|
|
stream_write_tree (ob,
|
|
gimple_eh_must_not_throw_fndecl (
|
|
as_a <geh_mnt *> (stmt)),
|
|
true);
|
|
break;
|
|
|
|
case GIMPLE_EH_DISPATCH:
|
|
streamer_write_hwi (ob,
|
|
gimple_eh_dispatch_region (
|
|
as_a <geh_dispatch *> (stmt)));
|
|
break;
|
|
|
|
case GIMPLE_ASM:
|
|
{
|
|
gasm *asm_stmt = as_a <gasm *> (stmt);
|
|
streamer_write_uhwi (ob, gimple_asm_ninputs (asm_stmt));
|
|
streamer_write_uhwi (ob, gimple_asm_noutputs (asm_stmt));
|
|
streamer_write_uhwi (ob, gimple_asm_nclobbers (asm_stmt));
|
|
streamer_write_uhwi (ob, gimple_asm_nlabels (asm_stmt));
|
|
streamer_write_string (ob, ob->main_stream,
|
|
gimple_asm_string (asm_stmt), true);
|
|
}
|
|
/* Fallthru */
|
|
|
|
case GIMPLE_ASSIGN:
|
|
case GIMPLE_CALL:
|
|
case GIMPLE_RETURN:
|
|
case GIMPLE_SWITCH:
|
|
case GIMPLE_LABEL:
|
|
case GIMPLE_COND:
|
|
case GIMPLE_GOTO:
|
|
case GIMPLE_DEBUG:
|
|
for (i = 0; i < gimple_num_ops (stmt); i++)
|
|
{
|
|
tree op = gimple_op (stmt, i);
|
|
tree *basep = NULL;
|
|
/* Wrap all uses of non-automatic variables inside MEM_REFs
|
|
so that we do not have to deal with type mismatches on
|
|
merged symbols during IL read in. The first operand
|
|
of GIMPLE_DEBUG must be a decl, not MEM_REF, though. */
|
|
if (!flag_wpa && op && (i || !is_gimple_debug (stmt)))
|
|
{
|
|
basep = &op;
|
|
if (TREE_CODE (*basep) == ADDR_EXPR)
|
|
basep = &TREE_OPERAND (*basep, 0);
|
|
while (handled_component_p (*basep))
|
|
basep = &TREE_OPERAND (*basep, 0);
|
|
if (VAR_P (*basep)
|
|
&& !auto_var_in_fn_p (*basep, fn->decl)
|
|
&& !DECL_REGISTER (*basep))
|
|
{
|
|
bool volatilep = TREE_THIS_VOLATILE (*basep);
|
|
tree ptrtype = build_pointer_type (TREE_TYPE (*basep));
|
|
*basep = build2 (MEM_REF, TREE_TYPE (*basep),
|
|
build1 (ADDR_EXPR, ptrtype, *basep),
|
|
build_int_cst (ptrtype, 0));
|
|
TREE_THIS_VOLATILE (*basep) = volatilep;
|
|
}
|
|
else
|
|
basep = NULL;
|
|
}
|
|
stream_write_tree (ob, op, true);
|
|
/* Restore the original base if we wrapped it inside a MEM_REF. */
|
|
if (basep)
|
|
*basep = TREE_OPERAND (TREE_OPERAND (*basep, 0), 0);
|
|
}
|
|
if (is_gimple_call (stmt))
|
|
{
|
|
if (gimple_call_internal_p (stmt))
|
|
streamer_write_enum (ob->main_stream, internal_fn,
|
|
IFN_LAST, gimple_call_internal_fn (stmt));
|
|
else
|
|
stream_write_tree (ob, gimple_call_fntype (stmt), true);
|
|
}
|
|
break;
|
|
|
|
case GIMPLE_NOP:
|
|
case GIMPLE_PREDICT:
|
|
break;
|
|
|
|
case GIMPLE_TRANSACTION:
|
|
{
|
|
gtransaction *txn = as_a <gtransaction *> (stmt);
|
|
gcc_assert (gimple_transaction_body (txn) == NULL);
|
|
stream_write_tree (ob, gimple_transaction_label_norm (txn), true);
|
|
stream_write_tree (ob, gimple_transaction_label_uninst (txn), true);
|
|
stream_write_tree (ob, gimple_transaction_label_over (txn), true);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
if (hist)
|
|
stream_out_histogram_value (ob, hist);
|
|
}
|
|
|
|
|
|
/* Output a basic block BB to the main stream in OB for this FN. */
|
|
|
|
void
|
|
output_bb (struct output_block *ob, basic_block bb, struct function *fn)
|
|
{
|
|
gimple_stmt_iterator bsi = gsi_start_bb (bb);
|
|
|
|
streamer_write_record_start (ob,
|
|
(!gsi_end_p (bsi)) || phi_nodes (bb)
|
|
? LTO_bb1
|
|
: LTO_bb0);
|
|
|
|
streamer_write_uhwi (ob, bb->index);
|
|
bb->count.stream_out (ob);
|
|
streamer_write_hwi (ob, bb->flags);
|
|
streamer_write_hwi (ob, bb->discriminator);
|
|
|
|
if (!gsi_end_p (bsi) || phi_nodes (bb))
|
|
{
|
|
/* Output the statements. The list of statements is terminated
|
|
with a zero. */
|
|
for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
|
|
{
|
|
int region;
|
|
gimple *stmt = gsi_stmt (bsi);
|
|
if (streamer_dump_file)
|
|
{
|
|
fprintf (streamer_dump_file, " Streaming gimple stmt ");
|
|
print_gimple_stmt (streamer_dump_file, stmt, 0, TDF_SLIM);
|
|
}
|
|
|
|
output_gimple_stmt (ob, fn, stmt);
|
|
|
|
/* Emit the EH region holding STMT. */
|
|
region = lookup_stmt_eh_lp_fn (fn, stmt);
|
|
if (region != 0)
|
|
{
|
|
streamer_write_record_start (ob, LTO_eh_region);
|
|
streamer_write_hwi (ob, region);
|
|
}
|
|
else
|
|
streamer_write_record_start (ob, LTO_null);
|
|
}
|
|
|
|
streamer_write_record_start (ob, LTO_null);
|
|
|
|
for (gphi_iterator psi = gsi_start_phis (bb);
|
|
!gsi_end_p (psi);
|
|
gsi_next (&psi))
|
|
{
|
|
gphi *phi = psi.phi ();
|
|
|
|
/* Only emit PHIs for gimple registers. PHI nodes for .MEM
|
|
will be filled in on reading when the SSA form is
|
|
updated. */
|
|
if (!virtual_operand_p (gimple_phi_result (phi)))
|
|
output_phi (ob, phi);
|
|
}
|
|
|
|
streamer_write_record_start (ob, LTO_null);
|
|
}
|
|
}
|