]>
Commit | Line | Data |
---|---|---|
5fd1486c | 1 | /* brig-code-entry-handler.cc -- brig function directive handling |
8d9254fc | 2 | Copyright (C) 2016-2020 Free Software Foundation, Inc. |
5fd1486c PJ |
3 | Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com> |
4 | for General Processor Tech. | |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 3, or (at your option) any later | |
11 | version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GCC; see the file COPYING3. If not see | |
20 | <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #include <sstream> | |
23 | #include <iomanip> | |
24 | ||
25 | #include "brig-code-entry-handler.h" | |
26 | ||
27 | #include "brig-machine.h" | |
28 | #include "stringpool.h" | |
29 | #include "tree-iterator.h" | |
30 | #include "gimple-expr.h" | |
31 | #include "function.h" | |
32 | #include "phsa.h" | |
33 | ||
34 | #include "tree-pretty-print.h" | |
35 | #include "print-tree.h" | |
36 | ||
37 | extern int gccbrig_verbose; | |
38 | ||
39 | size_t | |
40 | brig_directive_function_handler::operator () (const BrigBase *base) | |
41 | { | |
d4b7f2ee PJ |
42 | if (!m_parent.m_analyzing) |
43 | m_parent.finish_function (); | |
5fd1486c PJ |
44 | |
45 | size_t bytes_consumed = base->byteCount; | |
46 | ||
47 | const BrigDirectiveExecutable *exec = (const BrigDirectiveExecutable *) base; | |
48 | ||
49 | if (gccbrig_verbose) | |
50 | { | |
51 | printf ("brig: function name %s\n", | |
52 | m_parent.get_string (exec->name).c_str()); | |
53 | printf ("brig: inargs %d outargs %d name offset %d\n", exec->inArgCount, | |
54 | exec->outArgCount, exec->name); | |
55 | } | |
56 | ||
57 | const bool is_definition | |
58 | = exec->modifier & BRIG_EXECUTABLE_DEFINITION; | |
59 | ||
60 | const bool is_kernel = base->kind == BRIG_KIND_DIRECTIVE_KERNEL; | |
61 | ||
62 | /* There doesn't seem to be actual use cases for kernel declarations | |
63 | as they cannot be called by the program. Ignore them until there's | |
64 | a reason not to. */ | |
65 | if (is_kernel && !is_definition) | |
66 | return bytes_consumed; | |
67 | ||
d4b7f2ee PJ |
68 | std::string func_name = m_parent.get_mangled_name (exec); |
69 | if (is_kernel) | |
70 | /* The generated kernel function is not the one that should be | |
71 | called by the host. */ | |
72 | func_name = std::string ("_") + func_name; | |
73 | ||
5fd1486c | 74 | m_parent.m_cf = new brig_function (exec, &m_parent); |
d4b7f2ee PJ |
75 | m_parent.m_cf->m_name = func_name; |
76 | m_parent.m_cf->m_is_kernel = is_kernel; | |
5fd1486c | 77 | |
d4b7f2ee PJ |
78 | /* During the analyze step, the above information is all we need per |
79 | function. */ | |
80 | if (m_parent.m_analyzing) | |
81 | return bytes_consumed; | |
5fd1486c | 82 | |
1e25c5a9 PJ |
83 | /* There can be multiple forward declarations of the same function. |
84 | Skip all but the first one. */ | |
85 | if (!is_definition && m_parent.function_decl (func_name) != NULL_TREE) | |
86 | return bytes_consumed; | |
5fd1486c PJ |
87 | tree fndecl; |
88 | tree ret_value = NULL_TREE; | |
89 | ||
90 | tree stmt_list = alloc_stmt_list (); | |
91 | ||
92 | /* Add a function scope BIND_EXPR using which we can push local variables that | |
93 | represent HSAIL registers. */ | |
94 | tree bind_expr = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, NULL); | |
95 | ||
080dc243 PJ |
96 | tree restrict_char_ptr |
97 | = build_qualified_type (build_pointer_type (char_type_node), | |
98 | TYPE_QUAL_RESTRICT); | |
99 | tree restrict_void_ptr | |
100 | = build_qualified_type (build_pointer_type (void_type_node), | |
101 | TYPE_QUAL_RESTRICT); | |
102 | ||
103 | tree restrict_const_char_ptr | |
104 | = build_qualified_type (build_pointer_type | |
105 | (build_qualified_type (char_type_node, | |
106 | TYPE_QUAL_CONST)), | |
107 | TYPE_QUAL_RESTRICT); | |
108 | ||
109 | tree restrict_const_void_ptr | |
110 | = build_qualified_type (build_pointer_type | |
111 | (build_qualified_type (void_type_node, | |
112 | TYPE_QUAL_CONST)), | |
113 | TYPE_QUAL_RESTRICT); | |
114 | ||
5fd1486c PJ |
115 | if (is_kernel) |
116 | { | |
5fd1486c PJ |
117 | tree name_identifier |
118 | = get_identifier_with_length (func_name.c_str (), func_name.size ()); | |
119 | ||
120 | /* The generated kernel functions take the following arguments: | |
121 | ||
122 | 1) a char* which is a starting address of the argument segment where | |
123 | the call's arguments are stored by the launcher. | |
124 | 2) a void* parameter that points to a phsail-finalizer context object | |
125 | which passes the hsa kernel packet etc. | |
126 | 3) a void* parameter that contains the first flat address of the group | |
127 | region allocated to the current work-group. */ | |
128 | ||
5fd1486c PJ |
129 | fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier, |
130 | build_function_type_list (void_type_node, | |
080dc243 PJ |
131 | restrict_const_char_ptr, |
132 | restrict_void_ptr, | |
133 | restrict_char_ptr, NULL_TREE)); | |
5fd1486c PJ |
134 | |
135 | SET_DECL_ASSEMBLER_NAME (fndecl, name_identifier); | |
136 | ||
137 | tree resdecl | |
138 | = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node); | |
139 | ||
140 | tree typelist = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); | |
141 | tree argtype = TREE_VALUE (typelist); | |
142 | TYPE_ADDR_SPACE (argtype) | |
143 | = gccbrig_get_target_addr_space_id (BRIG_SEGMENT_KERNARG); | |
144 | ||
145 | tree arg_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL, | |
080dc243 PJ |
146 | get_identifier ("__args"), |
147 | restrict_const_char_ptr); | |
5fd1486c | 148 | DECL_ARGUMENTS (fndecl) = arg_arg; |
080dc243 | 149 | DECL_ARG_TYPE (arg_arg) = restrict_const_char_ptr; |
5fd1486c PJ |
150 | DECL_CONTEXT (arg_arg) = fndecl; |
151 | DECL_ARTIFICIAL (arg_arg) = 1; | |
152 | TREE_READONLY (arg_arg) = 1; | |
153 | TREE_USED (arg_arg) = 1; | |
154 | ||
155 | DECL_RESULT (fndecl) = resdecl; | |
156 | DECL_CONTEXT (resdecl) = fndecl; | |
157 | DECL_EXTERNAL (fndecl) = 0; | |
637f3cde PJ |
158 | |
159 | /* Aggressive inlining to the kernel function is usually a good | |
160 | idea with offlined functionality to enchance SIMD execution on | |
161 | GPUs and vector units. */ | |
162 | ||
163 | DECL_ATTRIBUTES (fndecl) | |
164 | = tree_cons (get_identifier ("flatten"), NULL, | |
165 | DECL_ATTRIBUTES (fndecl)); | |
5fd1486c PJ |
166 | } |
167 | else | |
168 | { | |
169 | /* Build a regular function fingerprint to enable targets to optimize | |
170 | the calling convention as they see fit. */ | |
171 | tree name_identifier | |
172 | = get_identifier_with_length (func_name.c_str (), func_name.size ()); | |
173 | ||
174 | m_parent.m_cf->m_arg_variables.clear (); | |
175 | ||
176 | brig_directive_variable_handler arg_handler (m_parent); | |
177 | ||
178 | vec<tree, va_gc> *args; | |
179 | vec_alloc (args, 4); | |
180 | ||
181 | tree arg_decls = NULL_TREE; | |
182 | ||
183 | tree ret_type = void_type_node; | |
184 | if (exec->outArgCount == 1) | |
185 | { | |
186 | /* The return value variable should be the first entry after the | |
187 | function directive. */ | |
188 | const BrigBase *retval | |
189 | = (const BrigBase *) ((const char *) base + base->byteCount); | |
190 | gcc_assert (retval->kind == BRIG_KIND_DIRECTIVE_VARIABLE); | |
191 | ||
192 | const BrigDirectiveVariable *brigVar | |
193 | = (const BrigDirectiveVariable *) retval; | |
194 | ||
195 | brig_directive_variable_handler varhandler (m_parent); | |
196 | ||
197 | if (brigVar->type & BRIG_TYPE_ARRAY) | |
198 | { | |
199 | /* Push array output arguments to the beginning of the | |
200 | function argument list instead of regular function | |
201 | return values. */ | |
202 | ||
203 | tree arg_var = varhandler.build_variable (brigVar, PARM_DECL); | |
204 | vec_safe_push (args, TREE_TYPE (arg_var)); | |
205 | ||
206 | m_parent.m_cf->add_arg_variable (brigVar, arg_var); | |
207 | ||
208 | if (arg_decls == NULL_TREE) | |
209 | arg_decls = arg_var; | |
210 | else | |
080dc243 | 211 | arg_decls = chainon (arg_decls, arg_var); |
5fd1486c PJ |
212 | |
213 | m_parent.m_cf->add_arg_variable (brigVar, arg_var); | |
214 | ||
215 | ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, | |
216 | void_type_node); | |
217 | } | |
218 | else | |
219 | { | |
220 | ret_value = varhandler.build_variable (brigVar, RESULT_DECL); | |
221 | m_parent.m_cf->m_ret_value = ret_value; | |
222 | ret_type = TREE_TYPE (ret_value); | |
223 | m_parent.m_cf->m_ret_value_brig_var = brigVar; | |
224 | } | |
225 | bytes_consumed += retval->byteCount; | |
226 | } | |
227 | else | |
228 | ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, | |
229 | void_type_node); | |
230 | ||
231 | TREE_ADDRESSABLE (ret_value) = 1; | |
232 | ||
233 | if (exec->inArgCount > 0) | |
234 | { | |
235 | uint32_t arg_offset = exec->firstInArg; | |
236 | for (size_t arg = 0; arg < exec->inArgCount; ++arg) | |
237 | { | |
238 | ||
239 | const BrigDirectiveVariable *brigVar | |
240 | = (const BrigDirectiveVariable *) m_parent.get_brig_code_entry | |
241 | (arg_offset); | |
242 | ||
243 | gcc_assert (brigVar->base.kind == BRIG_KIND_DIRECTIVE_VARIABLE); | |
244 | ||
245 | /* Delegate to the brig_directive_variable_handler. */ | |
246 | brig_directive_variable_handler varhandler (m_parent); | |
247 | tree arg_var = varhandler.build_variable (brigVar, PARM_DECL); | |
248 | arg_offset += brigVar->base.byteCount; | |
249 | vec_safe_push (args, TREE_TYPE (arg_var)); | |
250 | ||
251 | m_parent.m_cf->add_arg_variable (brigVar, arg_var); | |
080dc243 | 252 | arg_decls = chainon (arg_decls, arg_var); |
5fd1486c PJ |
253 | } |
254 | } | |
080dc243 PJ |
255 | vec_safe_push (args, restrict_void_ptr); |
256 | vec_safe_push (args, restrict_char_ptr); | |
257 | vec_safe_push (args, uint32_type_node); | |
258 | vec_safe_push (args, restrict_char_ptr); | |
5fd1486c PJ |
259 | |
260 | fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier, | |
261 | build_function_type_vec (ret_type, args)); | |
262 | ||
263 | DECL_RESULT (fndecl) = ret_value; | |
264 | DECL_CONTEXT (ret_value) = fndecl; | |
265 | DECL_EXTERNAL (fndecl) = 0; | |
266 | DECL_ARGUMENTS (fndecl) = arg_decls; | |
267 | } | |
268 | ||
269 | /* All functions need the hidden __context argument passed on | |
270 | because they might call WI-specific functions which need | |
080dc243 PJ |
271 | the context info. Only kernels can write it, if they need |
272 | to update the local ids in the work-item loop. */ | |
273 | ||
274 | tree context_arg_type | |
275 | = true ? restrict_void_ptr : restrict_const_void_ptr; | |
5fd1486c | 276 | tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL, |
080dc243 PJ |
277 | get_identifier ("__context"), |
278 | context_arg_type); | |
279 | DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), context_arg); | |
5fd1486c | 280 | DECL_CONTEXT (context_arg) = fndecl; |
080dc243 | 281 | DECL_ARG_TYPE (context_arg) = context_arg_type; |
5fd1486c PJ |
282 | DECL_ARTIFICIAL (context_arg) = 1; |
283 | TREE_READONLY (context_arg) = 1; | |
284 | TREE_USED (context_arg) = 1; | |
080dc243 | 285 | m_parent.m_cf->m_context_arg = context_arg; |
5fd1486c PJ |
286 | |
287 | /* They can also access group memory, so we need to pass the | |
288 | group pointer along too. */ | |
289 | tree group_base_arg | |
290 | = build_decl (UNKNOWN_LOCATION, PARM_DECL, | |
080dc243 PJ |
291 | get_identifier ("__group_base_addr"), |
292 | restrict_char_ptr); | |
293 | DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_base_arg); | |
294 | DECL_ARG_TYPE (group_base_arg) = restrict_char_ptr; | |
5fd1486c PJ |
295 | DECL_CONTEXT (group_base_arg) = fndecl; |
296 | DECL_ARTIFICIAL (group_base_arg) = 1; | |
297 | TREE_READONLY (group_base_arg) = 1; | |
298 | TREE_USED (group_base_arg) = 1; | |
d4b7f2ee PJ |
299 | m_parent.m_cf->m_group_base_arg = group_base_arg; |
300 | ||
301 | /* To implement call stack and (non-kernel) function scope group variables, | |
302 | we need to pass an offset which describes how far are we from | |
303 | group_base_ptr. | |
304 | That must be substracted from any function local group variable offsets to | |
305 | get the address related to the bottom of the group memory chunk. */ | |
306 | tree group_local_offset_arg | |
307 | = build_decl (UNKNOWN_LOCATION, PARM_DECL, | |
308 | get_identifier ("__group_local_offset"), uint32_type_node); | |
080dc243 | 309 | DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg); |
d4b7f2ee PJ |
310 | DECL_ARG_TYPE (group_local_offset_arg) = uint32_type_node; |
311 | DECL_CONTEXT (group_local_offset_arg) = fndecl; | |
312 | DECL_ARTIFICIAL (group_local_offset_arg) = 1; | |
313 | TREE_READONLY (group_local_offset_arg) = 1; | |
314 | TREE_USED (group_local_offset_arg) = 1; | |
315 | m_parent.m_cf->m_group_local_offset_arg = group_local_offset_arg; | |
5fd1486c PJ |
316 | |
317 | /* Same for private. */ | |
318 | tree private_base_arg | |
319 | = build_decl (UNKNOWN_LOCATION, PARM_DECL, | |
080dc243 PJ |
320 | get_identifier ("__private_base_addr"), restrict_char_ptr); |
321 | DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), private_base_arg); | |
322 | DECL_ARG_TYPE (private_base_arg) = restrict_char_ptr; | |
5fd1486c PJ |
323 | DECL_CONTEXT (private_base_arg) = fndecl; |
324 | DECL_ARTIFICIAL (private_base_arg) = 1; | |
325 | TREE_READONLY (private_base_arg) = 1; | |
326 | TREE_USED (private_base_arg) = 1; | |
080dc243 | 327 | m_parent.m_cf->m_private_base_arg = private_base_arg; |
5fd1486c PJ |
328 | |
329 | DECL_SAVED_TREE (fndecl) = bind_expr; | |
330 | ||
5fd1486c PJ |
331 | if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION) |
332 | { | |
637f3cde | 333 | TREE_STATIC (fndecl) = 0; |
5fd1486c | 334 | TREE_PUBLIC (fndecl) = 1; |
637f3cde PJ |
335 | DECL_EXTERNAL (fndecl) = 0; |
336 | DECL_DECLARED_INLINE_P (fndecl) = 1; | |
080dc243 PJ |
337 | set_inline (fndecl); |
338 | set_externally_visible (fndecl); | |
5fd1486c PJ |
339 | } |
340 | else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL) | |
341 | { | |
637f3cde | 342 | TREE_STATIC (fndecl) = 0; |
5fd1486c | 343 | TREE_PUBLIC (fndecl) = 1; |
637f3cde PJ |
344 | DECL_EXTERNAL (fndecl) = 0; |
345 | set_externally_visible (fndecl); | |
5fd1486c PJ |
346 | } |
347 | else if (base->kind == BRIG_KIND_DIRECTIVE_SIGNATURE) | |
348 | { | |
349 | TREE_STATIC (fndecl) = 0; | |
350 | TREE_PUBLIC (fndecl) = 1; | |
351 | DECL_EXTERNAL (fndecl) = 1; | |
080dc243 | 352 | set_inline (fndecl); |
5fd1486c PJ |
353 | } |
354 | else if (base->kind == BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION) | |
355 | { | |
356 | TREE_STATIC (fndecl) = 0; | |
357 | TREE_PUBLIC (fndecl) = 1; | |
358 | } | |
359 | else | |
360 | gcc_unreachable (); | |
361 | ||
362 | TREE_USED (fndecl) = 1; | |
363 | DECL_ARTIFICIAL (fndecl) = 0; | |
364 | ||
365 | tree initial_block = make_node (BLOCK); | |
366 | DECL_INITIAL (fndecl) = initial_block; | |
367 | TREE_USED (DECL_INITIAL (fndecl)) = 1; | |
368 | ||
369 | if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node) | |
370 | { | |
371 | DECL_CONTEXT (ret_value) = fndecl; | |
372 | DECL_CHAIN (ret_value) = BIND_EXPR_VARS (bind_expr); | |
373 | BIND_EXPR_VARS (bind_expr) = ret_value; | |
374 | } | |
375 | ||
376 | tree arg; | |
377 | for (arg = DECL_ARGUMENTS (fndecl); arg != NULL_TREE; arg = TREE_CHAIN (arg)) | |
378 | { | |
379 | DECL_CONTEXT (arg) = fndecl; | |
380 | DECL_ARG_TYPE (arg) = TREE_TYPE (arg); | |
381 | } | |
382 | ||
383 | m_parent.add_function_decl (func_name, fndecl); | |
384 | m_parent.append_global (fndecl); | |
385 | ||
637f3cde | 386 | |
5fd1486c | 387 | if (!is_definition) |
637f3cde PJ |
388 | { |
389 | DECL_EXTERNAL (fndecl) = 1; | |
390 | return bytes_consumed; | |
391 | } | |
5fd1486c PJ |
392 | |
393 | m_parent.start_function (fndecl); | |
5fd1486c PJ |
394 | m_parent.m_cf->m_func_decl = fndecl; |
395 | m_parent.m_cf->m_current_bind_expr = bind_expr; | |
5fd1486c PJ |
396 | |
397 | if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node) | |
398 | { | |
399 | /* We cannot assign to <<retval>> directly in gcc trunk. We need to | |
400 | create a local temporary variable which can be stored to and when | |
401 | returning from the function, we'll copy it to the actual <<retval>> | |
402 | in return statement's argument. */ | |
403 | tree temp_var = m_parent.m_cf->m_ret_temp | |
404 | = m_parent.m_cf->add_local_variable ("_retvalue_temp", | |
405 | TREE_TYPE (ret_value)); | |
406 | TREE_ADDRESSABLE (temp_var) = 1; | |
407 | } | |
408 | ||
409 | if (is_kernel) | |
410 | { | |
411 | m_parent.m_cf->add_id_variables (); | |
412 | ||
413 | /* Create a single entry point in the function. */ | |
414 | m_parent.m_cf->m_entry_label_stmt | |
415 | = build_stmt (LABEL_EXPR, m_parent.m_cf->label ("__kernel_entry")); | |
416 | m_parent.m_cf->append_statement (m_parent.m_cf->m_entry_label_stmt); | |
417 | ||
418 | tree bind_expr = m_parent.m_cf->m_current_bind_expr; | |
419 | tree stmts = BIND_EXPR_BODY (bind_expr); | |
420 | ||
421 | m_parent.m_cf->m_kernel_entry = tsi_last (stmts); | |
422 | ||
423 | /* Let's not append the exit label yet, but only after the | |
424 | function has been built. We need to build it so it can | |
425 | be referred to because returns are converted to gotos to this | |
426 | label. */ | |
427 | m_parent.m_cf->m_exit_label = m_parent.m_cf->label ("__kernel_exit"); | |
428 | } | |
429 | ||
430 | return bytes_consumed; | |
431 | } |