]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/brig/brigfrontend/brig-function-handler.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / brig / brigfrontend / brig-function-handler.cc
CommitLineData
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
37extern int gccbrig_verbose;
38
39size_t
40brig_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}