1 /* brig-code-entry-handler.cc -- brig function directive handling
2 Copyright (C) 2016-2019 Free Software Foundation, Inc.
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
operator ()(const BrigBase * base)40 brig_directive_function_handler::operator () (const BrigBase *base)
41 {
42 if (!m_parent.m_analyzing)
43 m_parent.finish_function ();
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
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
74 m_parent.m_cf = new brig_function (exec, &m_parent);
75 m_parent.m_cf->m_name = func_name;
76 m_parent.m_cf->m_is_kernel = is_kernel;
77
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;
82
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;
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
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
115 if (is_kernel)
116 {
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
129 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
130 build_function_type_list (void_type_node,
131 restrict_const_char_ptr,
132 restrict_void_ptr,
133 restrict_char_ptr, NULL_TREE));
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,
146 get_identifier ("__args"),
147 restrict_const_char_ptr);
148 DECL_ARGUMENTS (fndecl) = arg_arg;
149 DECL_ARG_TYPE (arg_arg) = restrict_const_char_ptr;
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;
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));
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
211 arg_decls = chainon (arg_decls, arg_var);
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);
252 arg_decls = chainon (arg_decls, arg_var);
253 }
254 }
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);
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
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;
276 tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
277 get_identifier ("__context"),
278 context_arg_type);
279 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), context_arg);
280 DECL_CONTEXT (context_arg) = fndecl;
281 DECL_ARG_TYPE (context_arg) = context_arg_type;
282 DECL_ARTIFICIAL (context_arg) = 1;
283 TREE_READONLY (context_arg) = 1;
284 TREE_USED (context_arg) = 1;
285 m_parent.m_cf->m_context_arg = context_arg;
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,
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;
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;
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);
309 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg);
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;
316
317 /* Same for private. */
318 tree private_base_arg
319 = build_decl (UNKNOWN_LOCATION, PARM_DECL,
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;
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;
327 m_parent.m_cf->m_private_base_arg = private_base_arg;
328
329 DECL_SAVED_TREE (fndecl) = bind_expr;
330
331 if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION)
332 {
333 TREE_STATIC (fndecl) = 0;
334 TREE_PUBLIC (fndecl) = 1;
335 DECL_EXTERNAL (fndecl) = 0;
336 DECL_DECLARED_INLINE_P (fndecl) = 1;
337 set_inline (fndecl);
338 set_externally_visible (fndecl);
339 }
340 else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL)
341 {
342 TREE_STATIC (fndecl) = 0;
343 TREE_PUBLIC (fndecl) = 1;
344 DECL_EXTERNAL (fndecl) = 0;
345 set_externally_visible (fndecl);
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;
352 set_inline (fndecl);
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
386
387 if (!is_definition)
388 {
389 DECL_EXTERNAL (fndecl) = 1;
390 return bytes_consumed;
391 }
392
393 m_parent.start_function (fndecl);
394 m_parent.m_cf->m_func_decl = fndecl;
395 m_parent.m_cf->m_current_bind_expr = bind_expr;
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 }
432