1/* brig-code-entry-handler.cc -- brig function directive handling
2   Copyright (C) 2016-2020 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
37extern int gccbrig_verbose;
38
39size_t
40brig_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