| /* Routines for emitting GIMPLE to a file stream. |
| |
| Copyright (C) 2011-2022 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); |
| } |
| } |