Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF
[official-gcc.git] / gcc / brig / brigfrontend / brig-function-handler.cc
blobf22f065c45ceee7a48542d70555f433663163337
1 /* brig-code-entry-handler.cc -- brig function directive handling
2 Copyright (C) 2016-2018 Free Software Foundation, Inc.
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4 for General Processor Tech.
6 This file is part of GCC.
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.
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.
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/>. */
22 #include <sstream>
23 #include <iomanip>
25 #include "brig-code-entry-handler.h"
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"
34 #include "tree-pretty-print.h"
35 #include "print-tree.h"
37 extern int gccbrig_verbose;
39 size_t
40 brig_directive_function_handler::operator () (const BrigBase *base)
42 if (!m_parent.m_analyzing)
43 m_parent.finish_function ();
45 size_t bytes_consumed = base->byteCount;
47 const BrigDirectiveExecutable *exec = (const BrigDirectiveExecutable *) base;
49 if (gccbrig_verbose)
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);
57 const bool is_definition
58 = exec->modifier & BRIG_EXECUTABLE_DEFINITION;
60 const bool is_kernel = base->kind == BRIG_KIND_DIRECTIVE_KERNEL;
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;
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;
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;
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;
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;
90 tree stmt_list = alloc_stmt_list ();
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);
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);
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);
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);
115 if (is_kernel)
117 tree name_identifier
118 = get_identifier_with_length (func_name.c_str (), func_name.size ());
120 /* The generated kernel functions take the following arguments:
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. */
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));
135 SET_DECL_ASSEMBLER_NAME (fndecl, name_identifier);
137 tree resdecl
138 = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node);
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);
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;
155 DECL_RESULT (fndecl) = resdecl;
156 DECL_CONTEXT (resdecl) = fndecl;
157 DECL_EXTERNAL (fndecl) = 0;
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. */
163 DECL_ATTRIBUTES (fndecl)
164 = tree_cons (get_identifier ("flatten"), NULL,
165 DECL_ATTRIBUTES (fndecl));
167 else
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 ());
174 m_parent.m_cf->m_arg_variables.clear ();
176 brig_directive_variable_handler arg_handler (m_parent);
178 vec<tree, va_gc> *args;
179 vec_alloc (args, 4);
181 tree arg_decls = NULL_TREE;
183 tree ret_type = void_type_node;
184 if (exec->outArgCount == 1)
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);
192 const BrigDirectiveVariable *brigVar
193 = (const BrigDirectiveVariable *) retval;
195 brig_directive_variable_handler varhandler (m_parent);
197 if (brigVar->type & BRIG_TYPE_ARRAY)
199 /* Push array output arguments to the beginning of the
200 function argument list instead of regular function
201 return values. */
203 tree arg_var = varhandler.build_variable (brigVar, PARM_DECL);
204 vec_safe_push (args, TREE_TYPE (arg_var));
206 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
208 if (arg_decls == NULL_TREE)
209 arg_decls = arg_var;
210 else
211 arg_decls = chainon (arg_decls, arg_var);
213 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
215 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
216 void_type_node);
218 else
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;
225 bytes_consumed += retval->byteCount;
227 else
228 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
229 void_type_node);
231 TREE_ADDRESSABLE (ret_value) = 1;
233 if (exec->inArgCount > 0)
235 uint32_t arg_offset = exec->firstInArg;
236 for (size_t arg = 0; arg < exec->inArgCount; ++arg)
239 const BrigDirectiveVariable *brigVar
240 = (const BrigDirectiveVariable *) m_parent.get_brig_code_entry
241 (arg_offset);
243 gcc_assert (brigVar->base.kind == BRIG_KIND_DIRECTIVE_VARIABLE);
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));
251 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
252 arg_decls = chainon (arg_decls, arg_var);
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);
260 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
261 build_function_type_vec (ret_type, args));
263 DECL_RESULT (fndecl) = ret_value;
264 DECL_CONTEXT (ret_value) = fndecl;
265 DECL_EXTERNAL (fndecl) = 0;
266 DECL_ARGUMENTS (fndecl) = arg_decls;
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. */
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;
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;
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;
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;
329 DECL_SAVED_TREE (fndecl) = bind_expr;
331 if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION)
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);
340 else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL)
342 TREE_STATIC (fndecl) = 0;
343 TREE_PUBLIC (fndecl) = 1;
344 DECL_EXTERNAL (fndecl) = 0;
345 set_externally_visible (fndecl);
347 else if (base->kind == BRIG_KIND_DIRECTIVE_SIGNATURE)
349 TREE_STATIC (fndecl) = 0;
350 TREE_PUBLIC (fndecl) = 1;
351 DECL_EXTERNAL (fndecl) = 1;
352 set_inline (fndecl);
354 else if (base->kind == BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION)
356 TREE_STATIC (fndecl) = 0;
357 TREE_PUBLIC (fndecl) = 1;
359 else
360 gcc_unreachable ();
362 TREE_USED (fndecl) = 1;
363 DECL_ARTIFICIAL (fndecl) = 0;
365 tree initial_block = make_node (BLOCK);
366 DECL_INITIAL (fndecl) = initial_block;
367 TREE_USED (DECL_INITIAL (fndecl)) = 1;
369 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
371 DECL_CONTEXT (ret_value) = fndecl;
372 DECL_CHAIN (ret_value) = BIND_EXPR_VARS (bind_expr);
373 BIND_EXPR_VARS (bind_expr) = ret_value;
376 tree arg;
377 for (arg = DECL_ARGUMENTS (fndecl); arg != NULL_TREE; arg = TREE_CHAIN (arg))
379 DECL_CONTEXT (arg) = fndecl;
380 DECL_ARG_TYPE (arg) = TREE_TYPE (arg);
383 m_parent.add_function_decl (func_name, fndecl);
384 m_parent.append_global (fndecl);
387 if (!is_definition)
389 DECL_EXTERNAL (fndecl) = 1;
390 return bytes_consumed;
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;
397 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
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;
409 if (is_kernel)
411 m_parent.m_cf->add_id_variables ();
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);
418 tree bind_expr = m_parent.m_cf->m_current_bind_expr;
419 tree stmts = BIND_EXPR_BODY (bind_expr);
421 m_parent.m_cf->m_kernel_entry = tsi_last (stmts);
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");
430 return bytes_consumed;