111
|
1 /* brig-code-entry-handler.cc -- brig function directive handling
|
131
|
2 Copyright (C) 2016-2018 Free Software Foundation, Inc.
|
111
|
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
|
|
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
|
131
|
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;
|
111
|
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
|
131
|
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
|
111
|
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
|
131 restrict_const_char_ptr,
|
|
132 restrict_void_ptr,
|
|
133 restrict_char_ptr, NULL_TREE));
|
111
|
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,
|
131
|
146 get_identifier ("__args"),
|
|
147 restrict_const_char_ptr);
|
111
|
148 DECL_ARGUMENTS (fndecl) = arg_arg;
|
131
|
149 DECL_ARG_TYPE (arg_arg) = restrict_const_char_ptr;
|
111
|
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;
|
131
|
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));
|
111
|
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
|
131
|
211 arg_decls = chainon (arg_decls, arg_var);
|
111
|
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);
|
131
|
252 arg_decls = chainon (arg_decls, arg_var);
|
111
|
253 }
|
|
254 }
|
131
|
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);
|
111
|
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
|
131
|
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;
|
111
|
276 tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
131
|
277 get_identifier ("__context"),
|
|
278 context_arg_type);
|
|
279 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), context_arg);
|
111
|
280 DECL_CONTEXT (context_arg) = fndecl;
|
131
|
281 DECL_ARG_TYPE (context_arg) = context_arg_type;
|
111
|
282 DECL_ARTIFICIAL (context_arg) = 1;
|
|
283 TREE_READONLY (context_arg) = 1;
|
|
284 TREE_USED (context_arg) = 1;
|
131
|
285 m_parent.m_cf->m_context_arg = context_arg;
|
111
|
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,
|
131
|
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;
|
111
|
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);
|
131
|
309 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg);
|
111
|
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,
|
131
|
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;
|
111
|
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;
|
131
|
327 m_parent.m_cf->m_private_base_arg = private_base_arg;
|
111
|
328
|
|
329 DECL_SAVED_TREE (fndecl) = bind_expr;
|
|
330
|
|
331 if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION)
|
|
332 {
|
131
|
333 TREE_STATIC (fndecl) = 0;
|
111
|
334 TREE_PUBLIC (fndecl) = 1;
|
131
|
335 DECL_EXTERNAL (fndecl) = 0;
|
|
336 DECL_DECLARED_INLINE_P (fndecl) = 1;
|
|
337 set_inline (fndecl);
|
|
338 set_externally_visible (fndecl);
|
111
|
339 }
|
|
340 else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL)
|
|
341 {
|
131
|
342 TREE_STATIC (fndecl) = 0;
|
111
|
343 TREE_PUBLIC (fndecl) = 1;
|
131
|
344 DECL_EXTERNAL (fndecl) = 0;
|
|
345 set_externally_visible (fndecl);
|
111
|
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;
|
131
|
352 set_inline (fndecl);
|
111
|
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
|
131
|
386
|
111
|
387 if (!is_definition)
|
131
|
388 {
|
|
389 DECL_EXTERNAL (fndecl) = 1;
|
|
390 return bytes_consumed;
|
|
391 }
|
111
|
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 }
|