const char * string_for_index (struct data_in *data_in, unsigned int loc, unsigned int *rlen) { struct lto_input_block str_tab; unsigned int len; const char *result; if (!loc) { *rlen = 0; return NULL; } /* Get the string stored at location LOC in DATA_IN->STRINGS. */ LTO_INIT_INPUT_BLOCK (str_tab, data_in->strings, loc - 1, data_in->strings_len); len = streamer_read_uhwi (&str_tab); *rlen = len; if (str_tab.p + len > data_in->strings_len) internal_error ("bytecode stream: string too long for the string table"); result = (const char *)(data_in->strings + str_tab.p); return result; }
tree streamer_read_integer_cst (struct lto_input_block *ib, struct data_in *data_in) { tree type = stream_read_tree (ib, data_in); unsigned HOST_WIDE_INT low = streamer_read_uhwi (ib); HOST_WIDE_INT high = streamer_read_hwi (ib); return build_int_cst_wide (type, low, high); }
void input_bb (struct lto_input_block *ib, enum LTO_tags tag, struct data_in *data_in, struct function *fn, int count_materialization_scale) { unsigned int index; basic_block bb; gimple_stmt_iterator bsi; /* This routine assumes that CFUN is set to FN, as it needs to call basic GIMPLE routines that use CFUN. */ gcc_assert (cfun == fn); index = streamer_read_uhwi (ib); bb = BASIC_BLOCK_FOR_FUNCTION (fn, index); bb->count = (streamer_read_hwi (ib) * count_materialization_scale + REG_BR_PROB_BASE / 2) / REG_BR_PROB_BASE; bb->loop_depth = streamer_read_hwi (ib); bb->frequency = streamer_read_hwi (ib); bb->flags = streamer_read_hwi (ib); /* LTO_bb1 has statements. LTO_bb0 does not. */ if (tag == LTO_bb0) return; bsi = gsi_start_bb (bb); tag = streamer_read_record_start (ib); while (tag) { gimple stmt = input_gimple_stmt (ib, data_in, fn, tag); if (!is_gimple_debug (stmt)) find_referenced_vars_in (stmt); gsi_insert_after (&bsi, stmt, GSI_NEW_STMT); /* After the statement, expect a 0 delimiter or the EH region that the previous statement belongs to. */ tag = streamer_read_record_start (ib); lto_tag_check_set (tag, 2, LTO_eh_region, LTO_null); if (tag == LTO_eh_region) { HOST_WIDE_INT region = streamer_read_hwi (ib); gcc_assert (region == (int) region); add_stmt_to_eh_lp (stmt, region); } tag = streamer_read_record_start (ib); } tag = streamer_read_record_start (ib); while (tag) { gimple phi = input_phi (ib, bb, data_in, fn); find_referenced_vars_in (phi); tag = streamer_read_record_start (ib); } }
static gphi * input_phi (struct lto_input_block *ib, basic_block bb, struct data_in *data_in, struct function *fn) { unsigned HOST_WIDE_INT ix; tree phi_result; int i, len; gphi *result; ix = streamer_read_uhwi (ib); phi_result = (*SSANAMES (fn))[ix]; len = EDGE_COUNT (bb->preds); result = create_phi_node (phi_result, bb); /* We have to go through a lookup process here because the preds in the reconstructed graph are generally in a different order than they were in the original program. */ for (i = 0; i < len; i++) { tree def = stream_read_tree (ib, data_in); int src_index = streamer_read_uhwi (ib); bitpack_d bp = streamer_read_bitpack (ib); /* Do not cache a location - we do not have API to get pointer to the location in PHI statement and we may trigger reallocation. */ location_t arg_loc = stream_input_location_now (&bp, data_in); basic_block sbb = BASIC_BLOCK_FOR_FN (fn, src_index); edge e = NULL; int j; for (j = 0; j < len; j++) if (EDGE_PRED (bb, j)->src == sbb) { e = EDGE_PRED (bb, j); break; } add_phi_arg (result, def, e, arg_loc); } return result; }
tree streamer_get_builtin_tree (struct lto_input_block *ib, struct data_in *data_in) { enum built_in_class fclass; enum built_in_function fcode; const char *asmname; tree result; fclass = streamer_read_enum (ib, built_in_class, BUILT_IN_LAST); gcc_assert (fclass == BUILT_IN_NORMAL || fclass == BUILT_IN_MD); fcode = (enum built_in_function) streamer_read_uhwi (ib); if (fclass == BUILT_IN_NORMAL) { if (fcode >= END_BUILTINS) fatal_error (input_location, "machine independent builtin code out of range"); result = builtin_decl_explicit (fcode); if (!result) { if (fcode > BEGIN_CHKP_BUILTINS && fcode < END_CHKP_BUILTINS) { fcode = (enum built_in_function) (fcode - BEGIN_CHKP_BUILTINS - 1); result = builtin_decl_explicit (fcode); result = chkp_maybe_clone_builtin_fndecl (result); } else if (fcode > BEGIN_SANITIZER_BUILTINS && fcode < END_SANITIZER_BUILTINS) { initialize_sanitizer_builtins (); result = builtin_decl_explicit (fcode); } } gcc_assert (result); } else if (fclass == BUILT_IN_MD) { result = targetm.builtin_decl (fcode, true); if (!result || result == error_mark_node) fatal_error (input_location, "target specific builtin not available"); } else gcc_unreachable (); asmname = streamer_read_string (data_in, ib); if (asmname) set_builtin_user_assembler_name (result, asmname); streamer_tree_cache_append (data_in->reader_cache, result, 0); return result; }
static gimple input_phi (struct lto_input_block *ib, basic_block bb, struct data_in *data_in, struct function *fn) { unsigned HOST_WIDE_INT ix; tree phi_result; int i, len; gimple result; ix = streamer_read_uhwi (ib); phi_result = VEC_index (tree, SSANAMES (fn), ix); len = EDGE_COUNT (bb->preds); result = create_phi_node (phi_result, bb); SSA_NAME_DEF_STMT (phi_result) = result; /* We have to go through a lookup process here because the preds in the reconstructed graph are generally in a different order than they were in the original program. */ for (i = 0; i < len; i++) { tree def = stream_read_tree (ib, data_in); int src_index = streamer_read_uhwi (ib); location_t arg_loc = lto_input_location (ib, data_in); basic_block sbb = BASIC_BLOCK_FOR_FUNCTION (fn, src_index); edge e = NULL; int j; for (j = 0; j < len; j++) if (EDGE_PRED (bb, j)->src == sbb) { e = EDGE_PRED (bb, j); break; } add_phi_arg (result, def, e, arg_loc); } return result; }
tree streamer_read_integer_cst (struct lto_input_block *ib, struct data_in *data_in) { tree result, type; HOST_WIDE_INT low, high; bool overflow_p; type = stream_read_tree (ib, data_in); overflow_p = (streamer_read_uchar (ib) != 0); low = streamer_read_uhwi (ib); high = streamer_read_uhwi (ib); result = build_int_cst_wide (type, low, high); /* If the original constant had overflown, build a replica of RESULT to avoid modifying the shared constant returned by build_int_cst_wide. */ if (overflow_p) { result = copy_node (result); TREE_OVERFLOW (result) = 1; } return result; }
tree streamer_get_pickled_tree (struct lto_input_block *ib, struct data_in *data_in) { unsigned HOST_WIDE_INT ix; tree result; enum LTO_tags expected_tag; ix = streamer_read_uhwi (ib); expected_tag = streamer_read_enum (ib, LTO_tags, LTO_NUM_TAGS); result = streamer_tree_cache_get_tree (data_in->reader_cache, ix); gcc_assert (result && TREE_CODE (result) == lto_tag_to_tree_code (expected_tag)); return result; }
static void lto_input_ts_constructor_tree_pointers (struct lto_input_block *ib, struct data_in *data_in, tree expr) { unsigned i, len; len = streamer_read_uhwi (ib); for (i = 0; i < len; i++) { tree index, value; index = stream_read_tree (ib, data_in); value = stream_read_tree (ib, data_in); CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (expr), index, value); } }
static void lto_input_ts_binfo_tree_pointers (struct lto_input_block *ib, struct data_in *data_in, tree expr) { unsigned i, len; tree t; /* Note that the number of slots in EXPR was read in streamer_alloc_tree when instantiating EXPR. However, the vector is empty so we cannot rely on VEC_length to know how many elements to read. So, this list is emitted as a 0-terminated list on the writer side. */ do { t = stream_read_tree (ib, data_in); if (t) VEC_quick_push (tree, BINFO_BASE_BINFOS (expr), t); } while (t); BINFO_OFFSET (expr) = stream_read_tree (ib, data_in); BINFO_VTABLE (expr) = stream_read_tree (ib, data_in); BINFO_VPTR_FIELD (expr) = stream_read_tree (ib, data_in); len = streamer_read_uhwi (ib); if (len > 0) { VEC_reserve_exact (tree, gc, BINFO_BASE_ACCESSES (expr), len); for (i = 0; i < len; i++) { tree a = stream_read_tree (ib, data_in); VEC_quick_push (tree, BINFO_BASE_ACCESSES (expr), a); } } BINFO_INHERITANCE_CHAIN (expr) = stream_read_tree (ib, data_in); BINFO_SUBVTT_INDEX (expr) = stream_read_tree (ib, data_in); BINFO_VPTR_INDEX (expr) = stream_read_tree (ib, data_in); }
const char * streamer_read_indexed_string (struct data_in *data_in, struct lto_input_block *ib, unsigned int *rlen) { return string_for_index (data_in, streamer_read_uhwi (ib), rlen); }
tree streamer_alloc_tree (struct lto_input_block *ib, struct data_in *data_in, enum LTO_tags tag) { enum tree_code code; tree result; #ifdef LTO_STREAMER_DEBUG HOST_WIDE_INT orig_address_in_writer; #endif result = NULL_TREE; #ifdef LTO_STREAMER_DEBUG /* Read the word representing the memory address for the tree as it was written by the writer. This is useful when debugging differences between the writer and reader. */ orig_address_in_writer = streamer_read_hwi (ib); gcc_assert ((intptr_t) orig_address_in_writer == orig_address_in_writer); #endif code = lto_tag_to_tree_code (tag); /* We should never see an SSA_NAME tree. Only the version numbers of SSA names are ever written out. See input_ssa_names. */ gcc_assert (code != SSA_NAME); /* Instantiate a new tree using the header data. */ if (CODE_CONTAINS_STRUCT (code, TS_STRING)) result = streamer_read_string_cst (data_in, ib); else if (CODE_CONTAINS_STRUCT (code, TS_IDENTIFIER)) result = input_identifier (data_in, ib); else if (CODE_CONTAINS_STRUCT (code, TS_VEC)) { HOST_WIDE_INT len = streamer_read_hwi (ib); result = make_tree_vec (len); } else if (CODE_CONTAINS_STRUCT (code, TS_VECTOR)) { HOST_WIDE_INT len = streamer_read_hwi (ib); result = make_vector (len); } else if (CODE_CONTAINS_STRUCT (code, TS_BINFO)) { unsigned HOST_WIDE_INT len = streamer_read_uhwi (ib); result = make_tree_binfo (len); } else if (CODE_CONTAINS_STRUCT (code, TS_INT_CST)) { unsigned HOST_WIDE_INT len = streamer_read_uhwi (ib); unsigned HOST_WIDE_INT ext_len = streamer_read_uhwi (ib); result = make_int_cst (len, ext_len); } else if (code == CALL_EXPR) { unsigned HOST_WIDE_INT nargs = streamer_read_uhwi (ib); return build_vl_exp (CALL_EXPR, nargs + 3); } else if (code == OMP_CLAUSE) { enum omp_clause_code subcode = (enum omp_clause_code) streamer_read_uhwi (ib); return build_omp_clause (UNKNOWN_LOCATION, subcode); } else { /* For all other nodes, materialize the tree with a raw make_node call. */ result = make_node (code); } #ifdef LTO_STREAMER_DEBUG /* Store the original address of the tree as seen by the writer in RESULT's aux field. This is useful when debugging streaming problems. This way, a debugging session can be started on both writer and reader with a breakpoint using this address value in both. */ lto_orig_address_map (result, (intptr_t) orig_address_in_writer); #endif return result; }
static gimple input_gimple_stmt (struct lto_input_block *ib, struct data_in *data_in, struct function *fn, enum LTO_tags tag) { gimple stmt; enum gimple_code code; unsigned HOST_WIDE_INT num_ops; size_t i; struct bitpack_d bp; code = lto_tag_to_gimple_code (tag); /* Read the tuple header. */ bp = streamer_read_bitpack (ib); num_ops = bp_unpack_var_len_unsigned (&bp); stmt = gimple_alloc (code, num_ops); stmt->gsbase.no_warning = bp_unpack_value (&bp, 1); if (is_gimple_assign (stmt)) stmt->gsbase.nontemporal_move = bp_unpack_value (&bp, 1); stmt->gsbase.has_volatile_ops = bp_unpack_value (&bp, 1); stmt->gsbase.subcode = bp_unpack_var_len_unsigned (&bp); /* Read location information. */ gimple_set_location (stmt, lto_input_location (ib, data_in)); /* Read lexical block reference. */ gimple_set_block (stmt, stream_read_tree (ib, data_in)); /* Read in all the operands. */ switch (code) { case GIMPLE_RESX: gimple_resx_set_region (stmt, streamer_read_hwi (ib)); break; case GIMPLE_EH_MUST_NOT_THROW: gimple_eh_must_not_throw_set_fndecl (stmt, stream_read_tree (ib, data_in)); break; case GIMPLE_EH_DISPATCH: gimple_eh_dispatch_set_region (stmt, streamer_read_hwi (ib)); break; case GIMPLE_ASM: { /* FIXME lto. Move most of this into a new gimple_asm_set_string(). */ tree str; stmt->gimple_asm.ni = streamer_read_uhwi (ib); stmt->gimple_asm.no = streamer_read_uhwi (ib); stmt->gimple_asm.nc = streamer_read_uhwi (ib); stmt->gimple_asm.nl = streamer_read_uhwi (ib); str = streamer_read_string_cst (data_in, ib); stmt->gimple_asm.string = TREE_STRING_POINTER (str); } /* 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 < num_ops; i++) { tree op = stream_read_tree (ib, data_in); gimple_set_op (stmt, i, op); if (!op) continue; /* Fixup FIELD_DECLs in COMPONENT_REFs, they are not handled by decl merging. */ if (TREE_CODE (op) == ADDR_EXPR) op = TREE_OPERAND (op, 0); while (handled_component_p (op)) { if (TREE_CODE (op) == COMPONENT_REF) { tree field, type, tem; tree closest_match = NULL_TREE; field = TREE_OPERAND (op, 1); type = DECL_CONTEXT (field); for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) { if (TREE_CODE (tem) != FIELD_DECL) continue; if (tem == field) break; if (DECL_NONADDRESSABLE_P (tem) == DECL_NONADDRESSABLE_P (field) && gimple_compare_field_offset (tem, field)) { if (types_compatible_p (TREE_TYPE (tem), TREE_TYPE (field))) break; else closest_match = tem; } } /* In case of type mismatches across units we can fail to unify some types and thus not find a proper field-decl here. */ if (tem == NULL_TREE) { /* Thus, emit a ODR violation warning. */ if (warning_at (gimple_location (stmt), 0, "use of type %<%E%> with two mismatching " "declarations at field %<%E%>", type, TREE_OPERAND (op, 1))) { if (TYPE_FIELDS (type)) inform (DECL_SOURCE_LOCATION (TYPE_FIELDS (type)), "original type declared here"); inform (DECL_SOURCE_LOCATION (TREE_OPERAND (op, 1)), "field in mismatching type declared here"); if (TYPE_NAME (TREE_TYPE (field)) && (TREE_CODE (TYPE_NAME (TREE_TYPE (field))) == TYPE_DECL)) inform (DECL_SOURCE_LOCATION (TYPE_NAME (TREE_TYPE (field))), "type of field declared here"); if (closest_match && TYPE_NAME (TREE_TYPE (closest_match)) && (TREE_CODE (TYPE_NAME (TREE_TYPE (closest_match))) == TYPE_DECL)) inform (DECL_SOURCE_LOCATION (TYPE_NAME (TREE_TYPE (closest_match))), "type of mismatching field declared here"); } /* And finally fixup the types. */ TREE_OPERAND (op, 0) = build1 (VIEW_CONVERT_EXPR, type, TREE_OPERAND (op, 0)); } else TREE_OPERAND (op, 1) = tem; } op = TREE_OPERAND (op, 0); } } if (is_gimple_call (stmt)) { if (gimple_call_internal_p (stmt)) gimple_call_set_internal_fn (stmt, streamer_read_enum (ib, internal_fn, IFN_LAST)); else gimple_call_set_fntype (stmt, stream_read_tree (ib, data_in)); } break; case GIMPLE_NOP: case GIMPLE_PREDICT: break; case GIMPLE_TRANSACTION: gimple_transaction_set_label (stmt, stream_read_tree (ib, data_in)); break; default: internal_error ("bytecode stream: unknown GIMPLE statement tag %s", lto_tag_name (tag)); } /* Update the properties of symbols, SSA names and labels associated with STMT. */ if (code == GIMPLE_ASSIGN || code == GIMPLE_CALL) { tree lhs = gimple_get_lhs (stmt); if (lhs && TREE_CODE (lhs) == SSA_NAME) SSA_NAME_DEF_STMT (lhs) = stmt; } else if (code == GIMPLE_LABEL) gcc_assert (emit_label_in_global_context_p (gimple_label_label (stmt)) || DECL_CONTEXT (gimple_label_label (stmt)) == fn->decl); else if (code == GIMPLE_ASM) { unsigned i; for (i = 0; i < gimple_asm_noutputs (stmt); i++) { tree op = TREE_VALUE (gimple_asm_output_op (stmt, i)); if (TREE_CODE (op) == SSA_NAME) SSA_NAME_DEF_STMT (op) = stmt; } } /* Reset alias information. */ if (code == GIMPLE_CALL) gimple_call_reset_alias_info (stmt); /* Mark the statement modified so its operand vectors can be filled in. */ gimple_set_modified (stmt, true); return stmt; }
static gimple * input_gimple_stmt (struct lto_input_block *ib, struct data_in *data_in, enum LTO_tags tag) { gimple *stmt; enum gimple_code code; unsigned HOST_WIDE_INT num_ops; size_t i; struct bitpack_d bp; bool has_hist; code = lto_tag_to_gimple_code (tag); /* Read the tuple header. */ bp = streamer_read_bitpack (ib); num_ops = bp_unpack_var_len_unsigned (&bp); stmt = gimple_alloc (code, num_ops); stmt->no_warning = bp_unpack_value (&bp, 1); if (is_gimple_assign (stmt)) stmt->nontemporal_move = bp_unpack_value (&bp, 1); stmt->has_volatile_ops = bp_unpack_value (&bp, 1); has_hist = bp_unpack_value (&bp, 1); stmt->subcode = bp_unpack_var_len_unsigned (&bp); /* Read location information. Caching here makes no sense until streamer cache can handle the following gimple_set_block. */ gimple_set_location (stmt, stream_input_location_now (&bp, data_in)); /* Read lexical block reference. */ gimple_set_block (stmt, stream_read_tree (ib, data_in)); /* Read in all the operands. */ switch (code) { case GIMPLE_RESX: gimple_resx_set_region (as_a <gresx *> (stmt), streamer_read_hwi (ib)); break; case GIMPLE_EH_MUST_NOT_THROW: gimple_eh_must_not_throw_set_fndecl ( as_a <geh_mnt *> (stmt), stream_read_tree (ib, data_in)); break; case GIMPLE_EH_DISPATCH: gimple_eh_dispatch_set_region (as_a <geh_dispatch *> (stmt), streamer_read_hwi (ib)); break; case GIMPLE_ASM: { /* FIXME lto. Move most of this into a new gimple_asm_set_string(). */ gasm *asm_stmt = as_a <gasm *> (stmt); tree str; asm_stmt->ni = streamer_read_uhwi (ib); asm_stmt->no = streamer_read_uhwi (ib); asm_stmt->nc = streamer_read_uhwi (ib); asm_stmt->nl = streamer_read_uhwi (ib); str = streamer_read_string_cst (data_in, ib); asm_stmt->string = TREE_STRING_POINTER (str); } /* 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 < num_ops; i++) { tree *opp, op = stream_read_tree (ib, data_in); gimple_set_op (stmt, i, op); if (!op) continue; opp = gimple_op_ptr (stmt, i); if (TREE_CODE (*opp) == ADDR_EXPR) opp = &TREE_OPERAND (*opp, 0); while (handled_component_p (*opp)) opp = &TREE_OPERAND (*opp, 0); /* At LTO output time we wrap all global decls in MEM_REFs to allow seamless replacement with prevailing decls. Undo this here if the prevailing decl allows for this. ??? Maybe we should simply fold all stmts. */ if (TREE_CODE (*opp) == MEM_REF && TREE_CODE (TREE_OPERAND (*opp, 0)) == ADDR_EXPR && integer_zerop (TREE_OPERAND (*opp, 1)) && (TREE_THIS_VOLATILE (*opp) == TREE_THIS_VOLATILE (TREE_OPERAND (TREE_OPERAND (*opp, 0), 0))) && !TYPE_REF_CAN_ALIAS_ALL (TREE_TYPE (TREE_OPERAND (*opp, 1))) && (TREE_TYPE (*opp) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (*opp, 1)))) && (TREE_TYPE (*opp) == TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*opp, 0), 0)))) *opp = TREE_OPERAND (TREE_OPERAND (*opp, 0), 0); } if (gcall *call_stmt = dyn_cast <gcall *> (stmt)) { if (gimple_call_internal_p (call_stmt)) gimple_call_set_internal_fn (call_stmt, streamer_read_enum (ib, internal_fn, IFN_LAST)); else gimple_call_set_fntype (call_stmt, stream_read_tree (ib, data_in)); } break; case GIMPLE_NOP: case GIMPLE_PREDICT: break; case GIMPLE_TRANSACTION: gimple_transaction_set_label (as_a <gtransaction *> (stmt), stream_read_tree (ib, data_in)); break; default: internal_error ("bytecode stream: unknown GIMPLE statement tag %s", lto_tag_name (tag)); } /* Update the properties of symbols, SSA names and labels associated with STMT. */ if (code == GIMPLE_ASSIGN || code == GIMPLE_CALL) { tree lhs = gimple_get_lhs (stmt); if (lhs && TREE_CODE (lhs) == SSA_NAME) SSA_NAME_DEF_STMT (lhs) = stmt; } else if (code == GIMPLE_ASM) { gasm *asm_stmt = as_a <gasm *> (stmt); unsigned i; for (i = 0; i < gimple_asm_noutputs (asm_stmt); i++) { tree op = TREE_VALUE (gimple_asm_output_op (asm_stmt, i)); if (TREE_CODE (op) == SSA_NAME) SSA_NAME_DEF_STMT (op) = stmt; } } /* Reset alias information. */ if (code == GIMPLE_CALL) gimple_call_reset_alias_info (as_a <gcall *> (stmt)); /* Mark the statement modified so its operand vectors can be filled in. */ gimple_set_modified (stmt, true); if (has_hist) stream_in_histogram_value (ib, stmt); return stmt; }