Line data Source code
1 : /* Toplevel assembly.
2 : Copyright (C) 2025 Free Software Foundation, Inc.
3 : Contributed by Michal Jires <mjires@suse.cz>
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "config.h"
22 : #include "system.h"
23 : #include "coretypes.h"
24 : #include "backend.h"
25 : #include "tree.h"
26 : #include "tree-pass.h"
27 : #include "cgraph.h"
28 :
29 : /* This symbol must be available and cannot be renamed.
30 : Marks the symbol and symbols that reference it. */
31 : static void
32 49 : mark_fragile_ref_by_asm (symtab_node* node, bool maybe_local = false)
33 : {
34 49 : node->ref_by_asm = true;
35 : /* Local symbols must remain in the same partition with their callers. */
36 49 : if (!TREE_PUBLIC (node->decl) || maybe_local)
37 : {
38 35 : unsigned j;
39 35 : ipa_ref *ref;
40 35 : node->must_remain_in_tu_name = true;
41 62 : for (j = 0; node->iterate_referring (j, ref); j++)
42 27 : ref->referring->must_remain_in_tu_body = true;
43 :
44 51 : if (cgraph_node* cnode = dyn_cast <cgraph_node *> (node))
45 31 : for (cgraph_edge *e = cnode->callers; e ; e = e->next_caller)
46 15 : e->caller->must_remain_in_tu_body = true;
47 : }
48 49 : }
49 :
50 : /* Helper struct for walk_through_constraints. */
51 : struct constraint_data {
52 : asm_node *node;
53 : unsigned asm_definition : 1;
54 : };
55 :
56 : /* Mark symbols in constraints. */
57 : static tree
58 1475 : walk_through_constraints (tree* t, int*, void* dat)
59 : {
60 1475 : constraint_data* data = (constraint_data*) dat;
61 1475 : asm_node* anode = data->node;
62 :
63 1475 : if (VAR_OR_FUNCTION_DECL_P (*t))
64 : {
65 389 : symtab_node* node;
66 389 : if (!flag_wpa && !flag_ltrans)
67 : {
68 369 : node = symtab_node::get_create (*t);
69 369 : node->ref_by_asm = true;
70 :
71 : /* Disable implicit definition on static variables defined in asm. */
72 167 : if (data->asm_definition && is_a<varpool_node*> (node)
73 490 : && !TREE_PUBLIC (node->decl))
74 10 : DECL_EXTERNAL (node->decl) = true;
75 :
76 369 : if (data->asm_definition && !TREE_PUBLIC (node->decl) && flag_lto)
77 14 : node->must_remain_in_tu_name = true;
78 : }
79 : else
80 : {
81 20 : node = symtab_node::get (*t);
82 20 : gcc_assert (node);
83 :
84 : /* Local symbols defined in asm cannot be renamed.
85 : LGEN pass is too early to use node->callers.
86 : So we do it in WPA. */
87 20 : if (data->asm_definition && flag_wpa)
88 19 : mark_fragile_ref_by_asm (node);
89 : }
90 389 : anode->symbols_referenced.safe_push (node);
91 : }
92 1475 : return NULL;
93 : }
94 :
95 : /* Analyze constraints of toplevel extended assembly. */
96 : void
97 267899 : analyze_toplevel_extended_asm ()
98 : {
99 267899 : asm_node *anode;
100 280847 : for (anode = symtab->first_asm_symbol (); anode;
101 12948 : anode = safe_as_a<asm_node*> (anode->next))
102 : {
103 12948 : if (TREE_CODE (anode->asm_str) != ASM_EXPR)
104 12777 : continue;
105 171 : struct constraint_data data {anode, false};
106 :
107 398 : for (tree l = ASM_INPUTS (anode->asm_str); l; l = TREE_CHAIN (l))
108 : {
109 227 : tree constraint = TREE_VALUE (TREE_PURPOSE (l));
110 227 : const char* c = TREE_STRING_POINTER (constraint);
111 227 : data.asm_definition = c[0] == ':' && c[1] == 0;
112 227 : walk_tree (&l, walk_through_constraints, (void*) &data, NULL);
113 : }
114 171 : data.asm_definition = false;
115 219 : for (tree l = ASM_OUTPUTS (anode->asm_str); l; l = TREE_CHAIN (l))
116 48 : walk_tree (&l, walk_through_constraints, (void*) &data, NULL);
117 : }
118 267899 : }
119 :
120 :
121 : /* Checks all toplevel assembly contents and compares them with known symbols.
122 : Marks those symbols with relevant flags.
123 : Heuristics: Detects anything in assembly that looks like an identifer.
124 :
125 : This pass must be in WPA, otherwise we will not see all possibly referenced
126 : symbols - if a symbol is only declared, it will not be in the callgraph if
127 : it is only referenced from toplevel assembly.
128 : However in WPA there may be multiple symbols with the same identifier.
129 : The chosen solution is to handle local symbols in LGEN pass first. */
130 :
131 : void
132 18 : ipa_asm_heuristics ()
133 : {
134 18 : hash_map<nofree_string_hash, symtab_node *> map;
135 18 : asm_node *anode = symtab->first_asm_symbol ();
136 18 : if (!anode)
137 3 : return;
138 :
139 15 : symtab_node* snode;
140 15 : if (flag_wpa)
141 : {
142 39 : FOR_EACH_SYMBOL (snode)
143 33 : if (TREE_PUBLIC (snode->decl))
144 30 : map.put (snode->asm_name (), snode);
145 : }
146 : else
147 : {
148 39 : FOR_EACH_SYMBOL (snode)
149 30 : map.put (snode->asm_name (), snode);
150 : }
151 :
152 15 : auto_vec<char> ident;
153 :
154 51 : for (; anode; anode = safe_as_a<asm_node*> (anode->next))
155 : {
156 36 : if (TREE_CODE (anode->asm_str) != STRING_CST)
157 0 : continue;
158 :
159 36 : const char *asm_str = TREE_STRING_POINTER (anode->asm_str);
160 36 : int asm_len = TREE_STRING_LENGTH (anode->asm_str);
161 :
162 36 : if (dump_file)
163 0 : fprintf (dump_file, "Searching for symbols in toplevel asm:\n%s\n---\n",
164 : asm_str);
165 :
166 864 : for (int i = 0; i < asm_len + 1; ++i)
167 : {
168 828 : char c = 0;
169 828 : if (i < asm_len)
170 792 : c = asm_str[i];
171 :
172 828 : if ('0' <= c && c <= '9')
173 : {
174 0 : if (ident.length ())
175 0 : ident.safe_push (c);
176 : }
177 : else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_')
178 660 : ident.safe_push (c);
179 906 : else if (ident.length ())
180 : {
181 78 : ident.safe_push ('\0');
182 156 : symtab_node **n_ = map.get (ident.begin ());
183 :
184 78 : if (n_)
185 : {
186 30 : bool is_label = c == ':';
187 :
188 30 : if (dump_file)
189 0 : fprintf (dump_file, "Found symbol '%s' (is_label: %d)\n",
190 : ident.begin (), is_label);
191 :
192 30 : mark_fragile_ref_by_asm (*n_, is_label);
193 : }
194 :
195 78 : ident.truncate (0);
196 : }
197 : }
198 : }
199 18 : }
200 :
201 : namespace {
202 :
203 : const pass_data pass_data_ipa_asm =
204 : {
205 : IPA_PASS, /* type */
206 : "ipa-asm", /* name */
207 : OPTGROUP_NONE, /* optinfo_flags */
208 : TV_IPA_LTO_ASM, /* tv_id */
209 : 0, /* properties_required */
210 : 0, /* properties_provided */
211 : 0, /* properties_destroyed */
212 : 0, /* todo_flags_start */
213 : 0, /* todo_flags_finish */
214 : };
215 :
216 : class pass_ipa_asm : public ipa_opt_pass_d
217 : {
218 : public:
219 571444 : pass_ipa_asm (gcc::context *ctxt)
220 : : ipa_opt_pass_d (pass_data_ipa_asm, ctxt,
221 : NULL, /* generate_summary */
222 : NULL, /* write_summary */
223 : NULL, /* read_summary */
224 : NULL, /* write_optimization_summary */
225 : NULL, /* read_optimization_summary */
226 : NULL, /* stmt_fixup */
227 : 0, /* function_transform_todo_flags_start */
228 : NULL, /* function_transform */
229 571444 : NULL) /* variable_transform */
230 571444 : {}
231 :
232 : /* opt_pass methods: */
233 :
234 793679 : bool gate (function *) final override
235 : {
236 793679 : return (flag_lto || flag_wpa) && flag_toplevel_asm_heuristics;
237 : }
238 :
239 18 : unsigned int execute (function *) final override
240 : {
241 18 : ipa_asm_heuristics ();
242 18 : return 0;
243 : }
244 :
245 : };
246 :
247 : } // anon namespace
248 :
249 : ipa_opt_pass_d *
250 285722 : make_pass_ipa_asm_lgen (gcc::context *ctxt)
251 : {
252 285722 : return new pass_ipa_asm (ctxt);
253 : }
254 :
255 : ipa_opt_pass_d *
256 285722 : make_pass_ipa_asm_wpa (gcc::context *ctxt)
257 : {
258 285722 : return new pass_ipa_asm (ctxt);
259 : }
|