static hashval_t hash_node_by_assembler_name (const void *p) { const symtab_node *n = (const symtab_node *) p; return (hashval_t) decl_assembler_name_hash (DECL_ASSEMBLER_NAME (n->decl)); }
void dump_symtab_base (FILE *f, symtab_node *node) { static const char * const visibility_types[] = { "default", "protected", "hidden", "internal" }; fprintf (f, "%s/%i (%s)", node->asm_name (), node->order, node->name ()); dump_addr (f, " @", (void *)node); fprintf (f, "\n Type: %s", symtab_type_names[node->type]); if (node->definition) fprintf (f, " definition"); if (node->analyzed) fprintf (f, " analyzed"); if (node->alias) fprintf (f, " alias"); if (node->weakref) fprintf (f, " weakref"); if (node->cpp_implicit_alias) fprintf (f, " cpp_implicit_alias"); if (node->alias_target) fprintf (f, " target:%s", DECL_P (node->alias_target) ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->alias_target)) : IDENTIFIER_POINTER (node->alias_target)); fprintf (f, "\n Visibility:"); if (node->in_other_partition) fprintf (f, " in_other_partition"); if (node->used_from_other_partition) fprintf (f, " used_from_other_partition"); if (node->force_output) fprintf (f, " force_output"); if (node->forced_by_abi) fprintf (f, " forced_by_abi"); if (node->externally_visible) fprintf (f, " externally_visible"); if (node->resolution != LDPR_UNKNOWN) fprintf (f, " %s", ld_plugin_symbol_resolution_names[(int)node->resolution]); if (TREE_ASM_WRITTEN (node->decl)) fprintf (f, " asm_written"); if (DECL_EXTERNAL (node->decl)) fprintf (f, " external"); if (TREE_PUBLIC (node->decl)) fprintf (f, " public"); if (DECL_COMMON (node->decl)) fprintf (f, " common"); if (DECL_WEAK (node->decl)) fprintf (f, " weak"); if (DECL_DLLIMPORT_P (node->decl)) fprintf (f, " dll_import"); if (DECL_COMDAT (node->decl)) fprintf (f, " comdat"); if (DECL_COMDAT_GROUP (node->decl)) fprintf (f, " comdat_group:%s", IDENTIFIER_POINTER (DECL_COMDAT_GROUP (node->decl))); if (DECL_ONE_ONLY (node->decl)) fprintf (f, " one_only"); if (DECL_SECTION_NAME (node->decl)) fprintf (f, " section_name:%s", TREE_STRING_POINTER (DECL_SECTION_NAME (node->decl))); if (DECL_VISIBILITY_SPECIFIED (node->decl)) fprintf (f, " visibility_specified"); if (DECL_VISIBILITY (node->decl)) fprintf (f, " visibility:%s", visibility_types [DECL_VISIBILITY (node->decl)]); if (DECL_VIRTUAL_P (node->decl)) fprintf (f, " virtual"); if (DECL_ARTIFICIAL (node->decl)) fprintf (f, " artificial"); if (TREE_CODE (node->decl) == FUNCTION_DECL) { if (DECL_STATIC_CONSTRUCTOR (node->decl)) fprintf (f, " constructor"); if (DECL_STATIC_DESTRUCTOR (node->decl)) fprintf (f, " destructor"); } fprintf (f, "\n"); if (node->same_comdat_group) fprintf (f, " Same comdat group as: %s/%i\n", node->same_comdat_group->asm_name (), node->same_comdat_group->order); if (node->next_sharing_asm_name) fprintf (f, " next sharing asm name: %i\n", node->next_sharing_asm_name->order); if (node->previous_sharing_asm_name) fprintf (f, " previous sharing asm name: %i\n", node->previous_sharing_asm_name->order); if (node->address_taken) fprintf (f, " Address is taken.\n"); if (node->aux) { fprintf (f, " Aux:"); dump_addr (f, " @", (void *)node->aux); } fprintf (f, " References: "); ipa_dump_references (f, &node->ref_list); fprintf (f, " Referring: "); ipa_dump_referring (f, &node->ref_list); if (node->lto_file_data) fprintf (f, " Read from file: %s\n", node->lto_file_data->file_name); }
tree get_decl_assembler_name (tree decl) { return DECL_ASSEMBLER_NAME (decl); }
static void dequeue_and_dump (dump_info_p di) { dump_queue_p dq; splay_tree_node stn; dump_node_info_p dni; tree t; unsigned int index; enum tree_code code; enum tree_code_class code_class; const char* code_name; /* Get the next node from the queue. */ dq = di->queue; stn = dq->node; t = (tree) stn->key; dni = (dump_node_info_p) stn->value; index = dni->index; /* Remove the node from the queue, and put it on the free list. */ di->queue = dq->next; if (!di->queue) di->queue_end = 0; dq->next = di->free_list; di->free_list = dq; /* Print the node index. */ dump_index (di, index); /* And the type of node this is. */ if (dni->binfo_p) code_name = "binfo"; else code_name = tree_code_name[(int) TREE_CODE (t)]; fprintf (di->stream, "%-16s ", code_name); di->column = 25; /* Figure out what kind of node this is. */ code = TREE_CODE (t); code_class = TREE_CODE_CLASS (code); /* Although BINFOs are TREE_VECs, we dump them specially so as to be more informative. */ if (dni->binfo_p) { unsigned ix; tree base; VEC(tree,gc) *accesses = BINFO_BASE_ACCESSES (t); dump_child ("type", BINFO_TYPE (t)); if (BINFO_VIRTUAL_P (t)) dump_string_field (di, "spec", "virt"); dump_int (di, "bases", BINFO_N_BASE_BINFOS (t)); for (ix = 0; BINFO_BASE_ITERATE (t, ix, base); ix++) { tree access = (accesses ? VEC_index (tree, accesses, ix) : access_public_node); const char *string = NULL; if (access == access_public_node) string = "pub"; else if (access == access_protected_node) string = "prot"; else if (access == access_private_node) string = "priv"; else gcc_unreachable (); dump_string_field (di, "accs", string); queue_and_dump_index (di, "binf", base, DUMP_BINFO); } goto done; } /* We can knock off a bunch of expression nodes in exactly the same way. */ if (IS_EXPR_CODE_CLASS (code_class)) { /* If we're dumping children, dump them now. */ queue_and_dump_type (di, t); switch (code_class) { case tcc_unary: dump_child ("op 0", TREE_OPERAND (t, 0)); break; case tcc_binary: case tcc_comparison: dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); break; case tcc_expression: case tcc_reference: case tcc_statement: case tcc_vl_exp: /* These nodes are handled explicitly below. */ break; default: gcc_unreachable (); } } else if (DECL_P (t)) { expanded_location xloc; /* All declarations have names. */ if (DECL_NAME (t)) dump_child ("name", DECL_NAME (t)); if (DECL_ASSEMBLER_NAME_SET_P (t) && DECL_ASSEMBLER_NAME (t) != DECL_NAME (t)) dump_child ("mngl", DECL_ASSEMBLER_NAME (t)); if (DECL_ABSTRACT_ORIGIN (t)) dump_child ("orig", DECL_ABSTRACT_ORIGIN (t)); /* And types. */ queue_and_dump_type (di, t); dump_child ("scpe", DECL_CONTEXT (t)); /* And a source position. */ xloc = expand_location (DECL_SOURCE_LOCATION (t)); if (xloc.file) { const char *filename = strrchr (xloc.file, '/'); if (!filename) filename = xloc.file; else /* Skip the slash. */ ++filename; dump_maybe_newline (di); fprintf (di->stream, "srcp: %s:%-6d ", filename, xloc.line); di->column += 6 + strlen (filename) + 8; } /* And any declaration can be compiler-generated. */ if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_DECL_COMMON) && DECL_ARTIFICIAL (t)) dump_string_field (di, "note", "artificial"); if (TREE_CHAIN (t) && !dump_flag (di, TDF_SLIM, NULL)) dump_child ("chan", TREE_CHAIN (t)); } else if (code_class == tcc_type) { /* All types have qualifiers. */ int quals = lang_hooks.tree_dump.type_quals (t); if (quals != TYPE_UNQUALIFIED) { fprintf (di->stream, "qual: %c%c%c ", (quals & TYPE_QUAL_CONST) ? 'c' : ' ', (quals & TYPE_QUAL_VOLATILE) ? 'v' : ' ', (quals & TYPE_QUAL_RESTRICT) ? 'r' : ' '); di->column += 14; } /* All types have associated declarations. */ dump_child ("name", TYPE_NAME (t)); /* All types have a main variant. */ if (TYPE_MAIN_VARIANT (t) != t) dump_child ("unql", TYPE_MAIN_VARIANT (t)); /* And sizes. */ dump_child ("size", TYPE_SIZE (t)); /* All types have alignments. */ dump_int (di, "algn", TYPE_ALIGN (t)); } else if (code_class == tcc_constant) /* All constants can have types. */ queue_and_dump_type (di, t); /* Give the language-specific code a chance to print something. If it's completely taken care of things, don't bother printing anything more ourselves. */ if (lang_hooks.tree_dump.dump_tree (di, t)) goto done; /* Now handle the various kinds of nodes. */ switch (code) { int i; case IDENTIFIER_NODE: dump_string_field (di, "strg", IDENTIFIER_POINTER (t)); dump_int (di, "lngt", IDENTIFIER_LENGTH (t)); break; case TREE_LIST: dump_child ("purp", TREE_PURPOSE (t)); dump_child ("valu", TREE_VALUE (t)); dump_child ("chan", TREE_CHAIN (t)); break; case STATEMENT_LIST: { tree_stmt_iterator it; for (i = 0, it = tsi_start (t); !tsi_end_p (it); tsi_next (&it), i++) { char buffer[32]; sprintf (buffer, "%u", i); dump_child (buffer, tsi_stmt (it)); } } break; case TREE_VEC: dump_int (di, "lngt", TREE_VEC_LENGTH (t)); for (i = 0; i < TREE_VEC_LENGTH (t); ++i) { char buffer[32]; sprintf (buffer, "%u", i); dump_child (buffer, TREE_VEC_ELT (t, i)); } break; case INTEGER_TYPE: case ENUMERAL_TYPE: dump_int (di, "prec", TYPE_PRECISION (t)); dump_string_field (di, "sign", TYPE_UNSIGNED (t) ? "unsigned": "signed"); dump_child ("min", TYPE_MIN_VALUE (t)); dump_child ("max", TYPE_MAX_VALUE (t)); if (code == ENUMERAL_TYPE) dump_child ("csts", TYPE_VALUES (t)); break; case REAL_TYPE: dump_int (di, "prec", TYPE_PRECISION (t)); break; case FIXED_POINT_TYPE: dump_int (di, "prec", TYPE_PRECISION (t)); dump_string_field (di, "sign", TYPE_UNSIGNED (t) ? "unsigned": "signed"); dump_string_field (di, "saturating", TYPE_SATURATING (t) ? "saturating": "non-saturating"); break; case POINTER_TYPE: dump_child ("ptd", TREE_TYPE (t)); break; case REFERENCE_TYPE: dump_child ("refd", TREE_TYPE (t)); break; case METHOD_TYPE: dump_child ("clas", TYPE_METHOD_BASETYPE (t)); /* Fall through. */ case FUNCTION_TYPE: dump_child ("retn", TREE_TYPE (t)); dump_child ("prms", TYPE_ARG_TYPES (t)); break; case ARRAY_TYPE: dump_child ("elts", TREE_TYPE (t)); dump_child ("domn", TYPE_DOMAIN (t)); break; case RECORD_TYPE: case UNION_TYPE: if (TREE_CODE (t) == RECORD_TYPE) dump_string_field (di, "tag", "struct"); else dump_string_field (di, "tag", "union"); dump_child ("flds", TYPE_FIELDS (t)); dump_child ("fncs", TYPE_METHODS (t)); queue_and_dump_index (di, "binf", TYPE_BINFO (t), DUMP_BINFO); break; case CONST_DECL: dump_child ("cnst", DECL_INITIAL (t)); break; case SYMBOL_MEMORY_TAG: case NAME_MEMORY_TAG: break; case VAR_DECL: case PARM_DECL: case FIELD_DECL: case RESULT_DECL: if (TREE_CODE (t) == PARM_DECL) dump_child ("argt", DECL_ARG_TYPE (t)); else dump_child ("init", DECL_INITIAL (t)); dump_child ("size", DECL_SIZE (t)); dump_int (di, "algn", DECL_ALIGN (t)); if (TREE_CODE (t) == FIELD_DECL) { if (DECL_FIELD_OFFSET (t)) dump_child ("bpos", bit_position (t)); } else if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL) { dump_int (di, "used", TREE_USED (t)); if (DECL_REGISTER (t)) dump_string_field (di, "spec", "register"); } break; case FUNCTION_DECL: dump_child ("args", DECL_ARGUMENTS (t)); if (DECL_EXTERNAL (t)) dump_string_field (di, "body", "undefined"); if (TREE_PUBLIC (t)) dump_string_field (di, "link", "extern"); else dump_string_field (di, "link", "static"); if (DECL_SAVED_TREE (t) && !dump_flag (di, TDF_SLIM, t)) dump_child ("body", DECL_SAVED_TREE (t)); break; case INTEGER_CST: if (TREE_INT_CST_HIGH (t)) dump_int (di, "high", TREE_INT_CST_HIGH (t)); dump_int (di, "low", TREE_INT_CST_LOW (t)); break; case STRING_CST: fprintf (di->stream, "strg: %-7s ", TREE_STRING_POINTER (t)); dump_int (di, "lngt", TREE_STRING_LENGTH (t)); break; case REAL_CST: dump_real (di, "valu", TREE_REAL_CST_PTR (t)); break; case FIXED_CST: dump_fixed (di, "valu", TREE_FIXED_CST_PTR (t)); break; case TRUTH_NOT_EXPR: case ADDR_EXPR: case INDIRECT_REF: case ALIGN_INDIRECT_REF: case MISALIGNED_INDIRECT_REF: case CLEANUP_POINT_EXPR: case SAVE_EXPR: case REALPART_EXPR: case IMAGPART_EXPR: /* These nodes are unary, but do not have code class `1'. */ dump_child ("op 0", TREE_OPERAND (t, 0)); break; case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: case INIT_EXPR: case MODIFY_EXPR: case COMPOUND_EXPR: case PREDECREMENT_EXPR: case PREINCREMENT_EXPR: case POSTDECREMENT_EXPR: case POSTINCREMENT_EXPR: /* These nodes are binary, but do not have code class `2'. */ dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); break; case COMPONENT_REF: dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); dump_child ("op 2", TREE_OPERAND (t, 2)); break; case ARRAY_REF: case ARRAY_RANGE_REF: dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); dump_child ("op 2", TREE_OPERAND (t, 2)); dump_child ("op 3", TREE_OPERAND (t, 3)); break; case COND_EXPR: dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); dump_child ("op 2", TREE_OPERAND (t, 2)); break; case TRY_FINALLY_EXPR: dump_child ("op 0", TREE_OPERAND (t, 0)); dump_child ("op 1", TREE_OPERAND (t, 1)); break; case CALL_EXPR: { int i = 0; tree arg; call_expr_arg_iterator iter; dump_child ("fn", CALL_EXPR_FN (t)); FOR_EACH_CALL_EXPR_ARG (arg, iter, t) { char buffer[32]; sprintf (buffer, "%u", i); dump_child (buffer, arg); i++; } } break; case CONSTRUCTOR: { unsigned HOST_WIDE_INT cnt; tree index, value; dump_int (di, "lngt", VEC_length (constructor_elt, CONSTRUCTOR_ELTS (t))); FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), cnt, index, value) { dump_child ("idx", index); dump_child ("val", value); } }
static void check_call (funct_state local, gimple call, bool ipa) { int flags = gimple_call_flags (call); tree callee_t = gimple_call_fndecl (call); bool possibly_throws = stmt_could_throw_p (call); bool possibly_throws_externally = (possibly_throws && stmt_can_throw_external (call)); if (possibly_throws) { unsigned int i; for (i = 0; i < gimple_num_ops (call); i++) if (gimple_op (call, i) && tree_could_throw_p (gimple_op (call, i))) { if (possibly_throws && cfun->can_throw_non_call_exceptions) { if (dump_file) fprintf (dump_file, " operand can throw; looping\n"); local->looping = true; } if (possibly_throws_externally) { if (dump_file) fprintf (dump_file, " operand can throw externally\n"); local->can_throw = true; } } } /* The const and pure flags are set by a variety of places in the compiler (including here). If someone has already set the flags for the callee, (such as for some of the builtins) we will use them, otherwise we will compute our own information. Const and pure functions have less clobber effects than other functions so we process these first. Otherwise if it is a call outside the compilation unit or an indirect call we punt. This leaves local calls which will be processed by following the call graph. */ if (callee_t) { enum pure_const_state_e call_state; bool call_looping; if (special_builtin_state (&call_state, &call_looping, callee_t)) { worse_state (&local->pure_const_state, &local->looping, call_state, call_looping); return; } /* When bad things happen to bad functions, they cannot be const or pure. */ if (setjmp_call_p (callee_t)) { if (dump_file) fprintf (dump_file, " setjmp is not const/pure\n"); local->looping = true; local->pure_const_state = IPA_NEITHER; } if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (callee_t)) { case BUILT_IN_LONGJMP: case BUILT_IN_NONLOCAL_GOTO: if (dump_file) fprintf (dump_file, " longjmp and nonlocal goto is not const/pure\n"); local->pure_const_state = IPA_NEITHER; local->looping = true; break; default: break; } } /* When not in IPA mode, we can still handle self recursion. */ if (!ipa && callee_t && recursive_call_p (current_function_decl, callee_t)) { if (dump_file) fprintf (dump_file, " Recursive call can loop.\n"); local->looping = true; } /* Either callee is unknown or we are doing local analysis. Look to see if there are any bits available for the callee (such as by declaration or because it is builtin) and process solely on the basis of those bits. */ else if (!ipa) { enum pure_const_state_e call_state; bool call_looping; if (possibly_throws && cfun->can_throw_non_call_exceptions) { if (dump_file) fprintf (dump_file, " can throw; looping\n"); local->looping = true; } if (possibly_throws_externally) { if (dump_file) { fprintf (dump_file, " can throw externally to lp %i\n", lookup_stmt_eh_lp (call)); if (callee_t) fprintf (dump_file, " callee:%s\n", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t))); } local->can_throw = true; } if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " checking flags for call:"); state_from_flags (&call_state, &call_looping, flags, ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)) || (!flag_exceptions && (flags & ECF_NORETURN))); worse_state (&local->pure_const_state, &local->looping, call_state, call_looping); } /* Direct functions calls are handled by IPA propagation. */ }
static void lto_cgraph_replace_node (struct cgraph_node *node, struct cgraph_node *prevailing_node) { struct cgraph_edge *e, *next; bool compatible_p; if (symtab->dump_file) { fprintf (symtab->dump_file, "Replacing cgraph node %s/%i by %s/%i" " for symbol %s\n", node->name (), node->order, prevailing_node->name (), prevailing_node->order, IDENTIFIER_POINTER ((*targetm.asm_out.mangle_assembler_name) (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl))))); } /* Merge node flags. */ if (node->force_output) prevailing_node->mark_force_output (); if (node->forced_by_abi) prevailing_node->forced_by_abi = true; if (node->address_taken) { gcc_assert (!prevailing_node->global.inlined_to); prevailing_node->mark_address_taken (); } if (node->definition && prevailing_node->definition && DECL_COMDAT (node->decl) && DECL_COMDAT (prevailing_node->decl)) prevailing_node->merged_comdat = true; /* Redirect all incoming edges. */ compatible_p = types_compatible_p (TREE_TYPE (TREE_TYPE (prevailing_node->decl)), TREE_TYPE (TREE_TYPE (node->decl))); for (e = node->callers; e; e = next) { next = e->next_caller; e->redirect_callee (prevailing_node); /* If there is a mismatch between the supposed callee return type and the real one do not attempt to inline this function. ??? We really need a way to match function signatures for ABI compatibility and perform related promotions at inlining time. */ if (!compatible_p) e->call_stmt_cannot_inline_p = 1; } /* Redirect incomming references. */ prevailing_node->clone_referring (node); /* Fix instrumentation references. */ if (node->instrumented_version) { gcc_assert (node->instrumentation_clone == prevailing_node->instrumentation_clone); node->instrumented_version->instrumented_version = prevailing_node; if (!prevailing_node->instrumented_version) prevailing_node->instrumented_version = node->instrumented_version; /* Need to reset node->instrumented_version to NULL, otherwise node removal code would reset node->instrumented_version->instrumented_version. */ node->instrumented_version = NULL; } lto_free_function_in_decl_state_for_node (node); if (node->decl != prevailing_node->decl) node->release_body (); /* Finally remove the replaced node. */ node->remove (); }
static void add_type_duplicate (odr_type val, tree type) { if (!val->types_set) val->types_set = pointer_set_create (); /* See if this duplicate is new. */ if (!pointer_set_insert (val->types_set, type)) { bool merge = true; bool base_mismatch = false; gcc_assert (in_lto_p); vec_safe_push (val->types, type); unsigned int i,j; /* First we compare memory layout. */ if (!types_compatible_p (val->type, type)) { merge = false; if (BINFO_VTABLE (TYPE_BINFO (val->type)) && warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0, "type %qD violates one definition rule ", type)) inform (DECL_SOURCE_LOCATION (TYPE_NAME (val->type)), "a type with the same name but different layout is " "defined in another translation unit"); if (cgraph_dump_file) { fprintf (cgraph_dump_file, "ODR violation or merging or ODR type bug?\n"); print_node (cgraph_dump_file, "", val->type, 0); putc ('\n',cgraph_dump_file); print_node (cgraph_dump_file, "", type, 0); putc ('\n',cgraph_dump_file); } } /* Next sanity check that bases are the same. If not, we will end up producing wrong answers. */ for (j = 0, i = 0; i < BINFO_N_BASE_BINFOS (TYPE_BINFO (type)); i++) if (polymorphic_type_binfo_p (BINFO_BASE_BINFO (TYPE_BINFO (type), i))) { odr_type base = get_odr_type (BINFO_TYPE (BINFO_BASE_BINFO (TYPE_BINFO (type), i)), true); if (val->bases.length () <= j || val->bases[j] != base) base_mismatch = true; j++; } if (base_mismatch) { merge = false; if (warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0, "type %qD violates one definition rule ", type)) inform (DECL_SOURCE_LOCATION (TYPE_NAME (val->type)), "a type with the same name but different bases is " "defined in another translation unit"); if (cgraph_dump_file) { fprintf (cgraph_dump_file, "ODR bse violation or merging bug?\n"); print_node (cgraph_dump_file, "", val->type, 0); putc ('\n',cgraph_dump_file); print_node (cgraph_dump_file, "", type, 0); putc ('\n',cgraph_dump_file); } } /* Regularize things a little. During LTO same types may come with different BINFOs. Either because their virtual table was not merged by tree merging and only later at decl merging or because one type comes with external vtable, while other with internal. We want to merge equivalent binfos to conserve memory and streaming overhead. The external vtables are more harmful: they contain references to external declarations of methods that may be defined in the merged LTO unit. For this reason we absolutely need to remove them and replace by internal variants. Not doing so will lead to incomplete answers from possible_polymorphic_call_targets. */ if (!flag_ltrans && merge) { tree master_binfo = TYPE_BINFO (val->type); tree v1 = BINFO_VTABLE (master_binfo); tree v2 = BINFO_VTABLE (TYPE_BINFO (type)); if (TREE_CODE (v1) == POINTER_PLUS_EXPR) { gcc_assert (TREE_CODE (v2) == POINTER_PLUS_EXPR && operand_equal_p (TREE_OPERAND (v1, 1), TREE_OPERAND (v2, 1), 0)); v1 = TREE_OPERAND (TREE_OPERAND (v1, 0), 0); v2 = TREE_OPERAND (TREE_OPERAND (v2, 0), 0); } gcc_assert (DECL_ASSEMBLER_NAME (v1) == DECL_ASSEMBLER_NAME (v2)); if (DECL_EXTERNAL (v1) && !DECL_EXTERNAL (v2)) { unsigned int i; TYPE_BINFO (val->type) = TYPE_BINFO (type); for (i = 0; i < val->types->length (); i++) { if (TYPE_BINFO ((*val->types)[i]) == master_binfo) TYPE_BINFO ((*val->types)[i]) = TYPE_BINFO (type); } } else TYPE_BINFO (type) = master_binfo; } } }
void gimple_init_edge_profiler (void) { tree interval_profiler_fn_type; tree pow2_profiler_fn_type; tree one_value_profiler_fn_type; tree gcov_type_ptr; tree ic_profiler_fn_type; tree average_profiler_fn_type; if (!gcov_type_node) { gcov_type_node = get_gcov_type (); gcov_type_ptr = build_pointer_type (gcov_type_node); /* void (*) (gcov_type *, gcov_type, int, unsigned) */ interval_profiler_fn_type = build_function_type_list (void_type_node, gcov_type_ptr, gcov_type_node, integer_type_node, unsigned_type_node, NULL_TREE); tree_interval_profiler_fn = build_fn_decl ("__gcov_interval_profiler", interval_profiler_fn_type); TREE_NOTHROW (tree_interval_profiler_fn) = 1; DECL_ATTRIBUTES (tree_interval_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_interval_profiler_fn)); /* void (*) (gcov_type *, gcov_type) */ pow2_profiler_fn_type = build_function_type_list (void_type_node, gcov_type_ptr, gcov_type_node, NULL_TREE); tree_pow2_profiler_fn = build_fn_decl ("__gcov_pow2_profiler", pow2_profiler_fn_type); TREE_NOTHROW (tree_pow2_profiler_fn) = 1; DECL_ATTRIBUTES (tree_pow2_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_pow2_profiler_fn)); /* void (*) (gcov_type *, gcov_type) */ one_value_profiler_fn_type = build_function_type_list (void_type_node, gcov_type_ptr, gcov_type_node, NULL_TREE); tree_one_value_profiler_fn = build_fn_decl ("__gcov_one_value_profiler", one_value_profiler_fn_type); TREE_NOTHROW (tree_one_value_profiler_fn) = 1; DECL_ATTRIBUTES (tree_one_value_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_one_value_profiler_fn)); init_ic_make_global_vars (); /* void (*) (gcov_type *, gcov_type, void *, void *) */ ic_profiler_fn_type = build_function_type_list (void_type_node, gcov_type_ptr, gcov_type_node, ptr_void, ptr_void, NULL_TREE); tree_indirect_call_profiler_fn = build_fn_decl ("__gcov_indirect_call_profiler", ic_profiler_fn_type); TREE_NOTHROW (tree_indirect_call_profiler_fn) = 1; DECL_ATTRIBUTES (tree_indirect_call_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_indirect_call_profiler_fn)); /* void (*) (gcov_type *, gcov_type) */ average_profiler_fn_type = build_function_type_list (void_type_node, gcov_type_ptr, gcov_type_node, NULL_TREE); tree_average_profiler_fn = build_fn_decl ("__gcov_average_profiler", average_profiler_fn_type); TREE_NOTHROW (tree_average_profiler_fn) = 1; DECL_ATTRIBUTES (tree_average_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_average_profiler_fn)); tree_ior_profiler_fn = build_fn_decl ("__gcov_ior_profiler", average_profiler_fn_type); TREE_NOTHROW (tree_ior_profiler_fn) = 1; DECL_ATTRIBUTES (tree_ior_profiler_fn) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_ior_profiler_fn)); /* LTO streamer needs assembler names. Because we create these decls late, we need to initialize them by hand. */ DECL_ASSEMBLER_NAME (tree_interval_profiler_fn); DECL_ASSEMBLER_NAME (tree_pow2_profiler_fn); DECL_ASSEMBLER_NAME (tree_one_value_profiler_fn); DECL_ASSEMBLER_NAME (tree_indirect_call_profiler_fn); DECL_ASSEMBLER_NAME (tree_average_profiler_fn); DECL_ASSEMBLER_NAME (tree_ior_profiler_fn); } }
// make_decl_llvm - Create the DECL_RTL for a VAR_DECL or FUNCTION_DECL. DECL // should have static storage duration. In other words, it should not be an // automatic variable, including PARM_DECLs. // // There is, however, one exception: this function handles variables explicitly // placed in a particular register by the user. // // This function corresponds to make_decl_rtl in varasm.c, and is implicitly // called by DECL_LLVM if a decl doesn't have an LLVM set. // void make_decl_llvm(tree decl) { #ifdef ENABLE_CHECKING // Check that we are not being given an automatic variable. // A weak alias has TREE_PUBLIC set but not the other bits. if (TREE_CODE(decl) == PARM_DECL || TREE_CODE(decl) == RESULT_DECL || (TREE_CODE(decl) == VAR_DECL && !TREE_STATIC(decl) && !TREE_PUBLIC(decl) && !DECL_EXTERNAL(decl) && !DECL_REGISTER(decl))) abort(); // And that we were not given a type or a label. */ else if (TREE_CODE(decl) == TYPE_DECL || TREE_CODE(decl) == LABEL_DECL) abort (); #endif // For a duplicate declaration, we can be called twice on the // same DECL node. Don't discard the LLVM already made. if (DECL_LLVM_SET_P(decl)) return; if (errorcount || sorrycount) return; // Do not process broken code. // Global register variable with asm name, e.g.: // register unsigned long esp __asm__("ebp"); if (TREE_CODE(decl) != FUNCTION_DECL && DECL_REGISTER(decl)) { // This just verifies that the variable is ok. The actual "load/store" // code paths handle accesses to the variable. ValidateRegisterVariable(decl); return; } timevar_push(TV_LLVM_GLOBALS); const char *Name = ""; if (DECL_NAME(decl)) if (tree AssemblerName = DECL_ASSEMBLER_NAME(decl)) Name = IDENTIFIER_POINTER(AssemblerName); // Now handle ordinary static variables and functions (in memory). // Also handle vars declared register invalidly. if (Name[0] == 1) { #ifdef REGISTER_PREFIX if (strlen (REGISTER_PREFIX) != 0) { int reg_number = decode_reg_name(Name); if (reg_number >= 0 || reg_number == -3) error("%Jregister name given for non-register variable %qD", decl, decl); } #endif } // Specifying a section attribute on a variable forces it into a // non-.bss section, and thus it cannot be common. if (TREE_CODE(decl) == VAR_DECL && DECL_SECTION_NAME(decl) != NULL_TREE && DECL_INITIAL(decl) == NULL_TREE && DECL_COMMON(decl)) DECL_COMMON(decl) = 0; // Variables can't be both common and weak. if (TREE_CODE(decl) == VAR_DECL && DECL_WEAK(decl)) DECL_COMMON(decl) = 0; // Okay, now we need to create an LLVM global variable or function for this // object. Note that this is quite possibly a forward reference to the // object, so its type may change later. if (TREE_CODE(decl) == FUNCTION_DECL) { assert(Name[0] && "Function with empty name!"); // If this function has already been created, reuse the decl. This happens // when we have something like __builtin_memset and memset in the same file. Function *FnEntry = TheModule->getFunction(Name); if (FnEntry == 0) { unsigned CC; const FunctionType *Ty = TheTypeConverter->ConvertFunctionType(TREE_TYPE(decl), decl, NULL, CC); FnEntry = new Function(Ty, Function::ExternalLinkage, Name, TheModule); FnEntry->setCallingConv(CC); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) FnEntry->setLinkage(Function::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(FnEntry,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) FnEntry->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) FnEntry->setVisibility(GlobalValue::ProtectedVisibility); } assert(FnEntry->getName() == Name &&"Preexisting fn with the same name!"); } SET_DECL_LLVM(decl, FnEntry); } else { assert((TREE_CODE(decl) == VAR_DECL || TREE_CODE(decl) == CONST_DECL) && "Not a function or var decl?"); const Type *Ty = ConvertType(TREE_TYPE(decl)); GlobalVariable *GV ; // If we have "extern void foo", make the global have type {} instead of // type void. if (Ty == Type::VoidTy) Ty = StructType::get(std::vector<const Type*>(), false); if (Name[0] == 0) { // Global has no name. GV = new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage, 0, "", TheModule); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) GV->setLinkage(GlobalValue::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(GV,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GV->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GV->setVisibility(GlobalValue::ProtectedVisibility); } } else { // If the global has a name, prevent multiple vars with the same name from // being created. GlobalVariable *GVE = TheModule->getGlobalVariable(Name); if (GVE == 0) { GV = new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage,0, Name, TheModule); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) GV->setLinkage(GlobalValue::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(GV,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GV->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GV->setVisibility(GlobalValue::ProtectedVisibility); } // If GV got renamed, then there is already an object with this name in // the symbol table. If this happens, the old one must be a forward // decl, just replace it with a cast of the new one. if (GV->getName() != Name) { Function *F = TheModule->getFunction(Name); assert(F && F->isDeclaration() && "A function turned into a global?"); // Replace any uses of "F" with uses of GV. Value *FInNewType = ConstantExpr::getBitCast(GV, F->getType()); F->replaceAllUsesWith(FInNewType); // Update the decl that points to F. changeLLVMValue(F, FInNewType); // Now we can give GV the proper name. GV->takeName(F); // F is now dead, nuke it. F->eraseFromParent(); } } else { GV = GVE; // Global already created, reuse it. } } if ((TREE_READONLY(decl) && !TREE_SIDE_EFFECTS(decl)) || TREE_CODE(decl) == CONST_DECL) { if (DECL_EXTERNAL(decl)) { // Mark external globals constant even though they could be marked // non-constant in the defining translation unit. The definition of the // global determines whether the global is ultimately constant or not, // marking this constant will allow us to do some extra (legal) // optimizations that we would otherwise not be able to do. (In C++, // any global that is 'C++ const' may not be readonly: it could have a // dynamic initializer. // GV->setConstant(true); } else { // Mark readonly globals with constant initializers constant. if (DECL_INITIAL(decl) != error_mark_node && // uninitialized? DECL_INITIAL(decl) && (TREE_CONSTANT(DECL_INITIAL(decl)) || TREE_CODE(DECL_INITIAL(decl)) == STRING_CST)) GV->setConstant(true); } } // Set thread local (TLS) if (TREE_CODE(decl) == VAR_DECL && DECL_THREAD_LOCAL(decl)) GV->setThreadLocal(true); SET_DECL_LLVM(decl, GV); } timevar_pop(TV_LLVM_GLOBALS); }
tree poplevel (int keep, int functionbody) { /* Points to a BLOCK tree node. This is the BLOCK node constructed for the binding level that we are about to exit and which is returned by this routine. */ tree block_node = NULL_TREE; tree decl_chain = current_binding_level->names; tree subblock_chain = current_binding_level->blocks; tree subblock_node; /* If there were any declarations in the current binding level, or if this binding level is a function body, or if there are any nested blocks then create a BLOCK node to record them for the life of this function. */ if (keep || functionbody) block_node = build_block (keep ? decl_chain : 0, subblock_chain, 0, 0); /* Record the BLOCK node just built as the subblock its enclosing scope. */ for (subblock_node = subblock_chain; subblock_node; subblock_node = BLOCK_CHAIN (subblock_node)) BLOCK_SUPERCONTEXT (subblock_node) = block_node; /* Clear out the meanings of the local variables of this level. */ for (subblock_node = decl_chain; subblock_node; subblock_node = DECL_CHAIN (subblock_node)) if (DECL_NAME (subblock_node) != 0) /* If the identifier was used or addressed via a local extern decl, don't forget that fact. */ if (DECL_EXTERNAL (subblock_node)) { if (TREE_USED (subblock_node)) TREE_USED (DECL_NAME (subblock_node)) = 1; if (TREE_ADDRESSABLE (subblock_node)) TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (subblock_node)) = 1; } /* Pop the current level. */ current_binding_level = current_binding_level->level_chain; if (functionbody) /* This is the top level block of a function. */ DECL_INITIAL (current_function_decl) = block_node; else if (current_binding_level == global_binding_level) /* When using gfc_start_block/gfc_finish_block from middle-end hooks, don't add newly created BLOCKs as subblocks of global_binding_level. */ ; else if (block_node) { current_binding_level->blocks = block_chainon (current_binding_level->blocks, block_node); } /* If we did not make a block for the level just exited, any blocks made for inner levels (since they cannot be recorded as subblocks in that level) must be carried forward so they will later become subblocks of something else. */ else if (subblock_chain) current_binding_level->blocks = block_chainon (current_binding_level->blocks, subblock_chain); if (block_node) TREE_USED (block_node) = 1; return block_node; }
void use_thunk (tree thunk_fndecl, bool emit_p) { tree function, alias; tree virtual_offset; HOST_WIDE_INT fixed_offset, virtual_value; bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl); /* We should have called finish_thunk to give it a name. */ my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127); /* We should never be using an alias, always refer to the aliased thunk. */ my_friendly_assert (!THUNK_ALIAS (thunk_fndecl), 20031023); if (TREE_ASM_WRITTEN (thunk_fndecl)) return; function = THUNK_TARGET (thunk_fndecl); if (DECL_RESULT (thunk_fndecl)) /* We already turned this thunk into an ordinary function. There's no need to process this thunk again. */ return; if (DECL_THUNK_P (function)) /* The target is itself a thunk, process it now. */ use_thunk (function, emit_p); /* Thunks are always addressable; they only appear in vtables. */ TREE_ADDRESSABLE (thunk_fndecl) = 1; /* Figure out what function is being thunked to. It's referenced in this translation unit. */ TREE_ADDRESSABLE (function) = 1; mark_used (function); if (!emit_p) return; #ifdef ASM_OUTPUT_DEF alias = make_alias_for_thunk (function); #else alias = function; #endif fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl); virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl); if (virtual_offset) { if (!this_adjusting) virtual_offset = BINFO_VPTR_FIELD (virtual_offset); virtual_value = tree_low_cst (virtual_offset, /*pos=*/0); my_friendly_assert (virtual_value, 20021026); } else virtual_value = 0; /* And, if we need to emit the thunk, it's used. */ mark_used (thunk_fndecl); /* This thunk is actually defined. */ DECL_EXTERNAL (thunk_fndecl) = 0; /* The linkage of the function may have changed. FIXME in linkage rewrite. */ TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function); DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function); if (flag_syntax_only) { TREE_ASM_WRITTEN (thunk_fndecl) = 1; return; } push_to_top_level (); #if defined (ASM_OUTPUT_DEF) \ && !defined (TARGET_IS_PE_COFF) if (targetm.have_named_sections) { resolve_unique_section (function, 0, flag_function_sections); if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function)) { resolve_unique_section (thunk_fndecl, 0, flag_function_sections); /* Output the thunk into the same section as function. */ DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function); } } #endif /* The back-end expects DECL_INITIAL to contain a BLOCK, so we create one. */ DECL_INITIAL (thunk_fndecl) = make_node (BLOCK); BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = DECL_ARGUMENTS (thunk_fndecl); if (this_adjusting && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, virtual_value, alias)) { const char *fnname; current_function_decl = thunk_fndecl; DECL_RESULT (thunk_fndecl) = build_decl (RESULT_DECL, 0, integer_type_node); fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0); init_function_start (thunk_fndecl); current_function_is_thunk = 1; assemble_start_function (thunk_fndecl, fnname); targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, fixed_offset, virtual_value, alias); assemble_end_function (thunk_fndecl, fnname); current_function_decl = 0; cfun = 0; /* Because init_function_start increments this, we must decrement it. */ immediate_size_expand--; TREE_ASM_WRITTEN (thunk_fndecl) = 1; } else { /* If this is a covariant thunk, or we don't have the necessary code for efficient thunks, generate a thunk function that just makes a call to the real function. Unfortunately, this doesn't work for varargs. */ tree a, t; if (varargs_function_p (function)) error ("generic thunk code fails for method `%#D' which uses `...'", function); /* Set up cloned argument trees for the thunk. */ t = NULL_TREE; for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a)) { tree x = copy_node (a); TREE_CHAIN (x) = t; DECL_CONTEXT (x) = thunk_fndecl; SET_DECL_RTL (x, NULL_RTX); t = x; } a = nreverse (t); DECL_ARGUMENTS (thunk_fndecl) = a; DECL_RESULT (thunk_fndecl) = NULL_TREE; start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED); /* We don't bother with a body block for thunks. */ /* There's no need to check accessibility inside the thunk body. */ push_deferring_access_checks (dk_no_check); t = a; if (this_adjusting) t = thunk_adjust (t, /*this_adjusting=*/1, fixed_offset, virtual_offset); /* Build up the call to the real function. */ t = tree_cons (NULL_TREE, t, NULL_TREE); for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a)) t = tree_cons (NULL_TREE, a, t); t = nreverse (t); t = build_call (alias, t); CALL_FROM_THUNK_P (t) = 1; t = force_target_expr (TREE_TYPE (t), t); if (!this_adjusting) t = thunk_adjust (t, /*this_adjusting=*/0, fixed_offset, virtual_offset); if (VOID_TYPE_P (TREE_TYPE (t))) finish_expr_stmt (t); else finish_return_stmt (t); /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ mark_referenced (DECL_ASSEMBLER_NAME (thunk_fndecl)); /* But we don't want debugging information about it. */ DECL_IGNORED_P (thunk_fndecl) = 1; /* Re-enable access control. */ pop_deferring_access_checks (); expand_body (finish_function (0)); } pop_from_top_level (); }
void sdbout_symbol (tree decl, int local) { tree type = TREE_TYPE (decl); tree context = NULL_TREE; rtx value; int regno = -1; const char *name; /* If we are called before sdbout_init is run, just save the symbol for later. */ if (!sdbout_initialized) { preinit_symbols = tree_cons (0, decl, preinit_symbols); return; } sdbout_one_type (type); switch (TREE_CODE (decl)) { case CONST_DECL: /* Enum values are defined by defining the enum type. */ return; case FUNCTION_DECL: /* Don't mention a nested function under its parent. */ context = decl_function_context (decl); if (context == current_function_decl) return; /* Check DECL_INITIAL to distinguish declarations from definitions. Don't output debug info here for declarations; they will have a DECL_INITIAL value of 0. */ if (! DECL_INITIAL (decl)) return; if (!MEM_P (DECL_RTL (decl)) || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) return; PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0)); PUT_SDB_SCL (TREE_PUBLIC (decl) ? C_EXT : C_STAT); break; case TYPE_DECL: /* Done with tagged types. */ if (DECL_NAME (decl) == 0) return; if (DECL_IGNORED_P (decl)) return; /* Don't output intrinsic types. GAS chokes on SDB .def statements that contain identifiers with embedded spaces (eg "unsigned long"). */ if (DECL_IS_BUILTIN (decl)) return; /* Output typedef name. */ if (template_name_p (DECL_NAME (decl))) PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); else PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl))); PUT_SDB_SCL (C_TPDEF); break; case PARM_DECL: /* Parm decls go in their own separate chains and are output by sdbout_reg_parms and sdbout_parms. */ gcc_unreachable (); case VAR_DECL: /* Don't mention a variable that is external. Let the file that defines it describe it. */ if (DECL_EXTERNAL (decl)) return; /* Ignore __FUNCTION__, etc. */ if (DECL_IGNORED_P (decl)) return; /* If there was an error in the declaration, don't dump core if there is no RTL associated with the variable doesn't exist. */ if (!DECL_RTL_SET_P (decl)) return; SET_DECL_RTL (decl, eliminate_regs (DECL_RTL (decl), VOIDmode, NULL_RTX)); #ifdef LEAF_REG_REMAP if (crtl->uses_only_leaf_regs) leaf_renumber_regs_insn (DECL_RTL (decl)); #endif value = DECL_RTL (decl); /* Don't mention a variable at all if it was completely optimized into nothingness. If DECL was from an inline function, then its rtl is not identically the rtl that was used in this particular compilation. */ if (REG_P (value)) { regno = REGNO (value); if (regno >= FIRST_PSEUDO_REGISTER) return; } else if (GET_CODE (value) == SUBREG) { while (GET_CODE (value) == SUBREG) value = SUBREG_REG (value); if (REG_P (value)) { if (REGNO (value) >= FIRST_PSEUDO_REGISTER) return; } regno = REGNO (alter_subreg (&value, true)); SET_DECL_RTL (decl, value); } /* Don't output anything if an auto variable gets RTL that is static. GAS version 2.2 can't handle such output. */ else if (MEM_P (value) && CONSTANT_P (XEXP (value, 0)) && ! TREE_STATIC (decl)) return; /* Emit any structure, union, or enum type that has not been output. This occurs for tag-less structs (et al) used to declare variables within functions. */ if (TREE_CODE (type) == ENUMERAL_TYPE || TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) { if (COMPLETE_TYPE_P (type) /* not a forward reference */ && KNOWN_TYPE_TAG (type) == 0) /* not yet declared */ sdbout_one_type (type); } /* Defer SDB information for top-level initialized variables! */ if (! local && MEM_P (value) && DECL_INITIAL (decl)) return; /* C++ in 2.3 makes nameless symbols. That will be fixed later. For now, avoid crashing. */ if (DECL_NAME (decl) == NULL_TREE) return; /* Record the name for, starting a symtab entry. */ if (local) name = IDENTIFIER_POINTER (DECL_NAME (decl)); else name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); if (MEM_P (value) && GET_CODE (XEXP (value, 0)) == SYMBOL_REF) { PUT_SDB_DEF (name); if (TREE_PUBLIC (decl)) { PUT_SDB_VAL (XEXP (value, 0)); PUT_SDB_SCL (C_EXT); } else { PUT_SDB_VAL (XEXP (value, 0)); PUT_SDB_SCL (C_STAT); } } else if (regno >= 0) { PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (regno)); PUT_SDB_SCL (C_REG); } else if (MEM_P (value) && (MEM_P (XEXP (value, 0)) || (REG_P (XEXP (value, 0)) && REGNO (XEXP (value, 0)) != HARD_FRAME_POINTER_REGNUM && REGNO (XEXP (value, 0)) != STACK_POINTER_REGNUM))) /* If the value is indirect by memory or by a register that isn't the frame pointer then it means the object is variable-sized and address through that register or stack slot. COFF has no way to represent this so all we can do is output the variable as a pointer. */ { PUT_SDB_DEF (name); if (REG_P (XEXP (value, 0))) { PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (XEXP (value, 0)))); PUT_SDB_SCL (C_REG); } else { /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))). We want the value of that CONST_INT. */ /* Encore compiler hates a newline in a macro arg, it seems. */ PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (XEXP (value, 0), 0))); PUT_SDB_SCL (C_AUTO); } /* Effectively do build_pointer_type, but don't cache this type, since it might be temporary whereas the type it points to might have been saved for inlining. */ /* Don't use REFERENCE_TYPE because dbx can't handle that. */ type = make_node (POINTER_TYPE); TREE_TYPE (type) = TREE_TYPE (decl); } else if (MEM_P (value) && ((GET_CODE (XEXP (value, 0)) == PLUS && REG_P (XEXP (XEXP (value, 0), 0)) && CONST_INT_P (XEXP (XEXP (value, 0), 1))) /* This is for variables which are at offset zero from the frame pointer. This happens on the Alpha. Non-frame pointer registers are excluded above. */ || (REG_P (XEXP (value, 0))))) { /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))) or (MEM (REG...)). We want the value of that CONST_INT or zero. */ PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (value, 0))); PUT_SDB_SCL (C_AUTO); } else { /* It is something we don't know how to represent for SDB. */ return; } break; default: break; } PUT_SDB_TYPE (plain_type (type)); PUT_SDB_ENDEF; }
static void sdbout_one_type (tree type) { if (current_function_decl != NULL_TREE && DECL_SECTION_NAME (current_function_decl) != NULL_TREE) ; /* Don't change section amid function. */ else switch_to_section (text_section); switch (TREE_CODE (type)) { case RECORD_TYPE: case UNION_TYPE: case QUAL_UNION_TYPE: case ENUMERAL_TYPE: type = TYPE_MAIN_VARIANT (type); /* Don't output a type twice. */ if (TREE_ASM_WRITTEN (type)) /* James said test TREE_ASM_BEING_WRITTEN here. */ return; /* Output nothing if type is not yet defined. */ if (!COMPLETE_TYPE_P (type)) return; TREE_ASM_WRITTEN (type) = 1; /* This is reputed to cause trouble with the following case, but perhaps checking TYPE_SIZE above will fix it. */ /* Here is a testcase: struct foo { struct badstr *bbb; } forwardref; typedef struct intermediate { int aaaa; } intermediate_ref; typedef struct badstr { int ccccc; } badtype; */ /* This change, which ought to make better output, used to make the COFF assembler unhappy. Changes involving KNOWN_TYPE_TAG may fix the problem. */ /* Before really doing anything, output types we want to refer to. */ /* Note that in version 1 the following two lines are not used if forward references are in use. */ if (TREE_CODE (type) != ENUMERAL_TYPE) sdbout_field_types (type); /* Output a structure type. */ { int size = int_size_in_bytes (type); int member_scl = 0; tree tem; /* Record the type tag, but not in its permanent place just yet. */ sdbout_record_type_name (type); PUT_SDB_DEF (KNOWN_TYPE_TAG (type)); switch (TREE_CODE (type)) { case UNION_TYPE: case QUAL_UNION_TYPE: PUT_SDB_SCL (C_UNTAG); PUT_SDB_TYPE (T_UNION); member_scl = C_MOU; break; case RECORD_TYPE: PUT_SDB_SCL (C_STRTAG); PUT_SDB_TYPE (T_STRUCT); member_scl = C_MOS; break; case ENUMERAL_TYPE: PUT_SDB_SCL (C_ENTAG); PUT_SDB_TYPE (T_ENUM); member_scl = C_MOE; break; default: break; } PUT_SDB_SIZE (size); PUT_SDB_ENDEF; /* Print out the base class information with fields named after the types they hold. */ /* This is only relevant to aggregate types. TYPE_BINFO is used for other purposes in an ENUMERAL_TYPE, so we must exclude that case. */ if (TREE_CODE (type) != ENUMERAL_TYPE && TYPE_BINFO (type)) { int i; tree binfo, child; for (binfo = TYPE_BINFO (type), i = 0; BINFO_BASE_ITERATE (binfo, i, child); i++) { tree child_type = BINFO_TYPE (child); tree child_type_name; if (TYPE_NAME (child_type) == 0) continue; if (TREE_CODE (TYPE_NAME (child_type)) == IDENTIFIER_NODE) child_type_name = TYPE_NAME (child_type); else if (TREE_CODE (TYPE_NAME (child_type)) == TYPE_DECL) { child_type_name = DECL_NAME (TYPE_NAME (child_type)); if (child_type_name && template_name_p (child_type_name)) child_type_name = DECL_ASSEMBLER_NAME (TYPE_NAME (child_type)); } else continue; PUT_SDB_DEF (IDENTIFIER_POINTER (child_type_name)); PUT_SDB_INT_VAL (tree_low_cst (BINFO_OFFSET (child), 0)); PUT_SDB_SCL (member_scl); sdbout_type (BINFO_TYPE (child)); PUT_SDB_ENDEF; } } /* Output the individual fields. */ if (TREE_CODE (type) == ENUMERAL_TYPE) { for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem)) { tree value = TREE_VALUE (tem); if (TREE_CODE (value) == CONST_DECL) value = DECL_INITIAL (value); if (host_integerp (value, 0)) { PUT_SDB_DEF (IDENTIFIER_POINTER (TREE_PURPOSE (tem))); PUT_SDB_INT_VAL (tree_low_cst (value, 0)); PUT_SDB_SCL (C_MOE); PUT_SDB_TYPE (T_MOE); PUT_SDB_ENDEF; } } } else /* record or union type */ for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) /* Output the name, type, position (in bits), size (in bits) of each field. */ /* Omit here the nameless fields that are used to skip bits. Also omit fields with variable size or position. Also omit non FIELD_DECL nodes that GNU C++ may put here. */ if (TREE_CODE (tem) == FIELD_DECL && DECL_NAME (tem) && DECL_SIZE (tem) && host_integerp (DECL_SIZE (tem), 1) && host_integerp (bit_position (tem), 0)) { const char *name; name = IDENTIFIER_POINTER (DECL_NAME (tem)); PUT_SDB_DEF (name); if (DECL_BIT_FIELD_TYPE (tem)) { PUT_SDB_INT_VAL (int_bit_position (tem)); PUT_SDB_SCL (C_FIELD); sdbout_type (DECL_BIT_FIELD_TYPE (tem)); PUT_SDB_SIZE (tree_low_cst (DECL_SIZE (tem), 1)); } else { PUT_SDB_INT_VAL (int_bit_position (tem) / BITS_PER_UNIT); PUT_SDB_SCL (member_scl); sdbout_type (TREE_TYPE (tem)); } PUT_SDB_ENDEF; } /* Output end of a structure,union, or enumeral definition. */ PUT_SDB_PLAIN_DEF ("eos"); PUT_SDB_INT_VAL (size); PUT_SDB_SCL (C_EOS); PUT_SDB_TAG (KNOWN_TYPE_TAG (type)); PUT_SDB_SIZE (size); PUT_SDB_ENDEF; break; } default: break; } }
DEBUG_FUNCTION bool verify_symtab_base (symtab_node node) { bool error_found = false; symtab_node hashed_node; if (symtab_function_p (node)) { if (TREE_CODE (node->symbol.decl) != FUNCTION_DECL) { error ("function symbol is not function"); error_found = true; } } else if (symtab_variable_p (node)) { if (TREE_CODE (node->symbol.decl) != VAR_DECL) { error ("variable symbol is not variable"); error_found = true; } } else { error ("node has unknown type"); error_found = true; } hashed_node = symtab_get_node (node->symbol.decl); if (!hashed_node) { error ("node not found in symtab decl hashtable"); error_found = true; } if (assembler_name_hash) { hashed_node = symtab_node_for_asm (DECL_ASSEMBLER_NAME (node->symbol.decl)); if (hashed_node && hashed_node->symbol.previous_sharing_asm_name) { error ("assembler name hash list corrupted"); error_found = true; } while (hashed_node) { if (hashed_node == node) break; hashed_node = hashed_node->symbol.next_sharing_asm_name; } if (!hashed_node) { error ("node not found in symtab assembler name hash"); error_found = true; } } if (node->symbol.previous_sharing_asm_name && node->symbol.previous_sharing_asm_name->symbol.next_sharing_asm_name != node) { error ("double linked list of assembler names corrupted"); } if (node->symbol.same_comdat_group) { symtab_node n = node->symbol.same_comdat_group; if (!DECL_ONE_ONLY (n->symbol.decl)) { error ("non-DECL_ONE_ONLY node in a same_comdat_group list"); error_found = true; } if (n->symbol.type != node->symbol.type) { error ("mixing different types of symbol in same comdat groups is not supported"); error_found = true; } if (n == node) { error ("node is alone in a comdat group"); error_found = true; } do { if (!n->symbol.same_comdat_group) { error ("same_comdat_group is not a circular list"); error_found = true; break; } n = n->symbol.same_comdat_group; } while (n != node); } return error_found; }
static unsigned int on_execute_pass(void) { basic_block bb; gimple_stmt_iterator gsi; const char* name; const char* file = EXPR_FILENAME(cfun->decl); const unsigned int line = EXPR_LINENO(cfun->decl); TRACE(); if (DECL_ASSEMBLER_NAME(cfun->decl) == NULL) { printf("--- skipping anonymous function\n"); return 0; } name = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(cfun->decl)); #if 0 /* debug */ printf("--- passing on function: %s\n", name); #endif track_pragmed_func(file, line, name); FOR_EACH_BB(bb) { for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { const_gimple stmt = gsi_stmt(gsi); const enum gimple_code code = gimple_code(stmt); if (code == GIMPLE_CALL) { const char* const name = get_called_name(stmt); const tracked_func_t* const tf = find_tracked_func(name); printf("CALL%s: %s()\n", tf ? "_TASK" : "", name); if (tf != NULL) handle_task_call(gsi, tf); } #if 0 /* debug */ if (gimple_has_location(stmt)) { const location_t loc = gimple_location(stmt); const char* type = "STMT"; if (code == GIMPLE_CALL) printf ( "%s locus: .%s/%u.\n", type, LOCATION_FILE(loc), LOCATION_LINE(loc) ); } #endif /* debug */ } } return 0; }