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