/* This symbol must be available and cannot be renamed.
Marks the symbol and symbols that reference it. */
static void
-mark_fragile_ref_by_asm (symtab_node* node)
+mark_fragile_ref_by_asm (symtab_node* node, bool maybe_local = false)
{
node->ref_by_asm = true;
/* Local symbols must remain in the same partition with their callers. */
- if (!TREE_PUBLIC (node->decl))
+ if (!TREE_PUBLIC (node->decl) || maybe_local)
{
unsigned j;
ipa_ref *ref;
walk_tree (&l, walk_through_constraints, (void*) &data, NULL);
}
}
+
+
+/* Checks all toplevel assembly contents and compares them with known symbols.
+ Marks those symbols with relevant flags.
+ Heuristics: Detects anything in assembly that looks like an identifer.
+
+ This pass must be in WPA, otherwise we will not see all possibly referenced
+ symbols - if a symbol is only declared, it will not be in the callgraph if
+ it is only referenced from toplevel assembly.
+ However in WPA there may be multiple symbols with the same identifier.
+ The chosen solution is to handle local symbols in LGEN pass first. */
+
+void
+ipa_asm_heuristics ()
+{
+ hash_map<nofree_string_hash, symtab_node *> map;
+ asm_node *anode = symtab->first_asm_symbol ();
+ if (!anode)
+ return;
+
+ symtab_node* snode;
+ if (flag_wpa)
+ {
+ FOR_EACH_SYMBOL (snode)
+ if (TREE_PUBLIC (snode->decl))
+ map.put (snode->asm_name (), snode);
+ }
+ else
+ {
+ FOR_EACH_SYMBOL (snode)
+ map.put (snode->asm_name (), snode);
+ }
+
+ auto_vec<char> ident;
+
+ for (; anode; anode = safe_as_a<asm_node*> (anode->next))
+ {
+ if (TREE_CODE (anode->asm_str) != STRING_CST)
+ continue;
+
+ const char *asm_str = TREE_STRING_POINTER (anode->asm_str);
+ int asm_len = TREE_STRING_LENGTH (anode->asm_str);
+
+ if (dump_file)
+ fprintf (dump_file, "Searching for symbols in toplevel asm:\n%s\n---\n",
+ asm_str);
+
+ for (int i = 0; i < asm_len + 1; ++i)
+ {
+ char c = 0;
+ if (i < asm_len)
+ c = asm_str[i];
+
+ if ('0' <= c && c <= '9')
+ {
+ if (ident.length ())
+ ident.safe_push (c);
+ }
+ else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_')
+ ident.safe_push (c);
+ else if (ident.length ())
+ {
+ ident.safe_push ('\0');
+ symtab_node **n_ = map.get (ident.begin ());
+
+ if (n_)
+ {
+ bool is_label = c == ':';
+
+ if (dump_file)
+ fprintf (dump_file, "Found symbol '%s' (is_label: %d)\n",
+ ident.begin (), is_label);
+
+ mark_fragile_ref_by_asm (*n_, is_label);
+ }
+
+ ident.truncate (0);
+ }
+ }
+ }
+}
+
+namespace {
+
+const pass_data pass_data_ipa_asm =
+{
+ IPA_PASS, /* type */
+ "ipa-asm", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_IPA_LTO_ASM, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_ipa_asm : public ipa_opt_pass_d
+{
+public:
+ pass_ipa_asm (gcc::context *ctxt)
+ : ipa_opt_pass_d (pass_data_ipa_asm, ctxt,
+ NULL, /* generate_summary */
+ NULL, /* write_summary */
+ NULL, /* read_summary */
+ NULL, /* write_optimization_summary */
+ NULL, /* read_optimization_summary */
+ NULL, /* stmt_fixup */
+ 0, /* function_transform_todo_flags_start */
+ NULL, /* function_transform */
+ NULL) /* variable_transform */
+ {}
+
+ /* opt_pass methods: */
+
+ bool gate (function *) final override
+ {
+ return (flag_lto || flag_wpa) && flag_toplevel_asm_heuristics;
+ }
+
+ unsigned int execute (function *) final override
+ {
+ ipa_asm_heuristics ();
+ return 0;
+ }
+
+};
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_asm_lgen (gcc::context *ctxt)
+{
+ return new pass_ipa_asm (ctxt);
+}
+
+ipa_opt_pass_d *
+make_pass_ipa_asm_wpa (gcc::context *ctxt)
+{
+ return new pass_ipa_asm (ctxt);
+}