111
|
1 /* brig-code-entry-handler.cc -- brig function directive handling
|
|
2 Copyright (C) 2016-2017 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
|
|
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 tree fndecl;
|
|
84 tree ret_value = NULL_TREE;
|
|
85
|
|
86 tree stmt_list = alloc_stmt_list ();
|
|
87
|
|
88 /* Add a function scope BIND_EXPR using which we can push local variables that
|
|
89 represent HSAIL registers. */
|
|
90 tree bind_expr = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, NULL);
|
|
91
|
|
92 if (is_kernel)
|
|
93 {
|
|
94 tree name_identifier
|
|
95 = get_identifier_with_length (func_name.c_str (), func_name.size ());
|
|
96
|
|
97 /* The generated kernel functions take the following arguments:
|
|
98
|
|
99 1) a char* which is a starting address of the argument segment where
|
|
100 the call's arguments are stored by the launcher.
|
|
101 2) a void* parameter that points to a phsail-finalizer context object
|
|
102 which passes the hsa kernel packet etc.
|
|
103 3) a void* parameter that contains the first flat address of the group
|
|
104 region allocated to the current work-group. */
|
|
105
|
|
106 tree char_ptr_type_node = build_pointer_type (char_type_node);
|
|
107 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
|
|
108 build_function_type_list (void_type_node,
|
|
109 char_ptr_type_node,
|
|
110 ptr_type_node,
|
|
111 ptr_type_node, NULL_TREE));
|
|
112
|
|
113 SET_DECL_ASSEMBLER_NAME (fndecl, name_identifier);
|
|
114
|
|
115 tree resdecl
|
|
116 = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node);
|
|
117
|
|
118 tree typelist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
|
|
119 tree argtype = TREE_VALUE (typelist);
|
|
120 TYPE_ADDR_SPACE (argtype)
|
|
121 = gccbrig_get_target_addr_space_id (BRIG_SEGMENT_KERNARG);
|
|
122
|
|
123 tree arg_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
124 get_identifier ("__args"), char_ptr_type_node);
|
|
125 DECL_ARGUMENTS (fndecl) = arg_arg;
|
|
126 DECL_ARG_TYPE (arg_arg) = char_ptr_type_node;
|
|
127 DECL_CONTEXT (arg_arg) = fndecl;
|
|
128 DECL_ARTIFICIAL (arg_arg) = 1;
|
|
129 TREE_READONLY (arg_arg) = 1;
|
|
130 TREE_USED (arg_arg) = 1;
|
|
131
|
|
132 DECL_RESULT (fndecl) = resdecl;
|
|
133 DECL_CONTEXT (resdecl) = fndecl;
|
|
134 DECL_EXTERNAL (fndecl) = 0;
|
|
135 }
|
|
136 else
|
|
137 {
|
|
138 /* Build a regular function fingerprint to enable targets to optimize
|
|
139 the calling convention as they see fit. */
|
|
140 tree name_identifier
|
|
141 = get_identifier_with_length (func_name.c_str (), func_name.size ());
|
|
142
|
|
143 m_parent.m_cf->m_arg_variables.clear ();
|
|
144
|
|
145 brig_directive_variable_handler arg_handler (m_parent);
|
|
146
|
|
147 vec<tree, va_gc> *args;
|
|
148 vec_alloc (args, 4);
|
|
149
|
|
150 tree arg_decls = NULL_TREE;
|
|
151
|
|
152 tree ret_type = void_type_node;
|
|
153 if (exec->outArgCount == 1)
|
|
154 {
|
|
155 /* The return value variable should be the first entry after the
|
|
156 function directive. */
|
|
157 const BrigBase *retval
|
|
158 = (const BrigBase *) ((const char *) base + base->byteCount);
|
|
159 gcc_assert (retval->kind == BRIG_KIND_DIRECTIVE_VARIABLE);
|
|
160
|
|
161 const BrigDirectiveVariable *brigVar
|
|
162 = (const BrigDirectiveVariable *) retval;
|
|
163
|
|
164 brig_directive_variable_handler varhandler (m_parent);
|
|
165
|
|
166 if (brigVar->type & BRIG_TYPE_ARRAY)
|
|
167 {
|
|
168 /* Push array output arguments to the beginning of the
|
|
169 function argument list instead of regular function
|
|
170 return values. */
|
|
171
|
|
172 tree arg_var = varhandler.build_variable (brigVar, PARM_DECL);
|
|
173 vec_safe_push (args, TREE_TYPE (arg_var));
|
|
174
|
|
175 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
|
|
176
|
|
177 if (arg_decls == NULL_TREE)
|
|
178 arg_decls = arg_var;
|
|
179 else
|
|
180 chainon (arg_decls, arg_var);
|
|
181
|
|
182 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
|
|
183
|
|
184 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
|
|
185 void_type_node);
|
|
186 }
|
|
187 else
|
|
188 {
|
|
189 ret_value = varhandler.build_variable (brigVar, RESULT_DECL);
|
|
190 m_parent.m_cf->m_ret_value = ret_value;
|
|
191 ret_type = TREE_TYPE (ret_value);
|
|
192 m_parent.m_cf->m_ret_value_brig_var = brigVar;
|
|
193 }
|
|
194 bytes_consumed += retval->byteCount;
|
|
195 }
|
|
196 else
|
|
197 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
|
|
198 void_type_node);
|
|
199
|
|
200 TREE_ADDRESSABLE (ret_value) = 1;
|
|
201
|
|
202 if (exec->inArgCount > 0)
|
|
203 {
|
|
204 uint32_t arg_offset = exec->firstInArg;
|
|
205 for (size_t arg = 0; arg < exec->inArgCount; ++arg)
|
|
206 {
|
|
207
|
|
208 const BrigDirectiveVariable *brigVar
|
|
209 = (const BrigDirectiveVariable *) m_parent.get_brig_code_entry
|
|
210 (arg_offset);
|
|
211
|
|
212 gcc_assert (brigVar->base.kind == BRIG_KIND_DIRECTIVE_VARIABLE);
|
|
213
|
|
214 /* Delegate to the brig_directive_variable_handler. */
|
|
215 brig_directive_variable_handler varhandler (m_parent);
|
|
216 tree arg_var = varhandler.build_variable (brigVar, PARM_DECL);
|
|
217 arg_offset += brigVar->base.byteCount;
|
|
218 vec_safe_push (args, TREE_TYPE (arg_var));
|
|
219
|
|
220 m_parent.m_cf->add_arg_variable (brigVar, arg_var);
|
|
221
|
|
222 if (arg_decls == NULL_TREE)
|
|
223 arg_decls = arg_var;
|
|
224 else
|
|
225 chainon (arg_decls, arg_var);
|
|
226 }
|
|
227 }
|
|
228
|
|
229 vec_safe_push (args, ptr_type_node);
|
|
230 vec_safe_push (args, ptr_type_node);
|
|
231
|
|
232 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
|
|
233 build_function_type_vec (ret_type, args));
|
|
234
|
|
235 DECL_RESULT (fndecl) = ret_value;
|
|
236 DECL_CONTEXT (ret_value) = fndecl;
|
|
237 DECL_EXTERNAL (fndecl) = 0;
|
|
238 DECL_ARGUMENTS (fndecl) = arg_decls;
|
|
239 }
|
|
240
|
|
241 /* All functions need the hidden __context argument passed on
|
|
242 because they might call WI-specific functions which need
|
|
243 the context info. */
|
|
244 tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
245 get_identifier ("__context"), ptr_type_node);
|
|
246 if (DECL_ARGUMENTS (fndecl) == NULL_TREE)
|
|
247 DECL_ARGUMENTS (fndecl) = context_arg;
|
|
248 else
|
|
249 chainon (DECL_ARGUMENTS (fndecl), context_arg);
|
|
250 DECL_CONTEXT (context_arg) = fndecl;
|
|
251 DECL_ARG_TYPE (context_arg) = ptr_type_node;
|
|
252 DECL_ARTIFICIAL (context_arg) = 1;
|
|
253 TREE_READONLY (context_arg) = 1;
|
|
254 TREE_USED (context_arg) = 1;
|
|
255
|
|
256 /* They can also access group memory, so we need to pass the
|
|
257 group pointer along too. */
|
|
258 tree group_base_arg
|
|
259 = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
260 get_identifier ("__group_base_addr"), ptr_type_node);
|
|
261 chainon (DECL_ARGUMENTS (fndecl), group_base_arg);
|
|
262 DECL_ARG_TYPE (group_base_arg) = ptr_type_node;
|
|
263 DECL_CONTEXT (group_base_arg) = fndecl;
|
|
264 DECL_ARTIFICIAL (group_base_arg) = 1;
|
|
265 TREE_READONLY (group_base_arg) = 1;
|
|
266 TREE_USED (group_base_arg) = 1;
|
|
267 m_parent.m_cf->m_group_base_arg = group_base_arg;
|
|
268
|
|
269 /* To implement call stack and (non-kernel) function scope group variables,
|
|
270 we need to pass an offset which describes how far are we from
|
|
271 group_base_ptr.
|
|
272 That must be substracted from any function local group variable offsets to
|
|
273 get the address related to the bottom of the group memory chunk. */
|
|
274 tree group_local_offset_arg
|
|
275 = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
276 get_identifier ("__group_local_offset"), uint32_type_node);
|
|
277 chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg);
|
|
278 DECL_ARG_TYPE (group_local_offset_arg) = uint32_type_node;
|
|
279 DECL_CONTEXT (group_local_offset_arg) = fndecl;
|
|
280 DECL_ARTIFICIAL (group_local_offset_arg) = 1;
|
|
281 TREE_READONLY (group_local_offset_arg) = 1;
|
|
282 TREE_USED (group_local_offset_arg) = 1;
|
|
283 m_parent.m_cf->m_group_local_offset_arg = group_local_offset_arg;
|
|
284
|
|
285 /* Same for private. */
|
|
286 tree private_base_arg
|
|
287 = build_decl (UNKNOWN_LOCATION, PARM_DECL,
|
|
288 get_identifier ("__private_base_addr"), ptr_type_node);
|
|
289 chainon (DECL_ARGUMENTS (fndecl), private_base_arg);
|
|
290 DECL_ARG_TYPE (private_base_arg) = ptr_type_node;
|
|
291 DECL_CONTEXT (private_base_arg) = fndecl;
|
|
292 DECL_ARTIFICIAL (private_base_arg) = 1;
|
|
293 TREE_READONLY (private_base_arg) = 1;
|
|
294 TREE_USED (private_base_arg) = 1;
|
|
295
|
|
296 DECL_SAVED_TREE (fndecl) = bind_expr;
|
|
297
|
|
298 /* Try to preserve the functions across IPA. */
|
|
299 DECL_PRESERVE_P (fndecl) = 1;
|
|
300 TREE_SIDE_EFFECTS (fndecl) = 1;
|
|
301
|
|
302 TREE_ADDRESSABLE (fndecl) = 1;
|
|
303
|
|
304 if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION)
|
|
305 {
|
|
306 TREE_STATIC (fndecl) = 1;
|
|
307 TREE_PUBLIC (fndecl) = 1;
|
|
308 }
|
|
309 else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL)
|
|
310 {
|
|
311 TREE_STATIC (fndecl) = 1;
|
|
312 TREE_PUBLIC (fndecl) = 1;
|
|
313 }
|
|
314 else if (base->kind == BRIG_KIND_DIRECTIVE_SIGNATURE)
|
|
315 {
|
|
316 TREE_STATIC (fndecl) = 0;
|
|
317 TREE_PUBLIC (fndecl) = 1;
|
|
318 DECL_EXTERNAL (fndecl) = 1;
|
|
319 }
|
|
320 else if (base->kind == BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION)
|
|
321 {
|
|
322 TREE_STATIC (fndecl) = 0;
|
|
323 TREE_PUBLIC (fndecl) = 1;
|
|
324 }
|
|
325 else
|
|
326 gcc_unreachable ();
|
|
327
|
|
328 TREE_USED (fndecl) = 1;
|
|
329 DECL_ARTIFICIAL (fndecl) = 0;
|
|
330
|
|
331 tree initial_block = make_node (BLOCK);
|
|
332 DECL_INITIAL (fndecl) = initial_block;
|
|
333 TREE_USED (DECL_INITIAL (fndecl)) = 1;
|
|
334
|
|
335 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
|
|
336 {
|
|
337 DECL_CONTEXT (ret_value) = fndecl;
|
|
338 DECL_CHAIN (ret_value) = BIND_EXPR_VARS (bind_expr);
|
|
339 BIND_EXPR_VARS (bind_expr) = ret_value;
|
|
340 }
|
|
341
|
|
342 tree arg;
|
|
343 for (arg = DECL_ARGUMENTS (fndecl); arg != NULL_TREE; arg = TREE_CHAIN (arg))
|
|
344 {
|
|
345 DECL_CONTEXT (arg) = fndecl;
|
|
346 DECL_ARG_TYPE (arg) = TREE_TYPE (arg);
|
|
347 }
|
|
348
|
|
349 m_parent.add_function_decl (func_name, fndecl);
|
|
350 m_parent.append_global (fndecl);
|
|
351
|
|
352 if (!is_definition)
|
|
353 return bytes_consumed;
|
|
354
|
|
355 m_parent.start_function (fndecl);
|
|
356
|
|
357 m_parent.m_cf->m_func_decl = fndecl;
|
|
358 m_parent.m_cf->m_current_bind_expr = bind_expr;
|
|
359 m_parent.m_cf->m_context_arg = context_arg;
|
|
360 m_parent.m_cf->m_private_base_arg = private_base_arg;
|
|
361
|
|
362 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
|
|
363 {
|
|
364 /* We cannot assign to <<retval>> directly in gcc trunk. We need to
|
|
365 create a local temporary variable which can be stored to and when
|
|
366 returning from the function, we'll copy it to the actual <<retval>>
|
|
367 in return statement's argument. */
|
|
368 tree temp_var = m_parent.m_cf->m_ret_temp
|
|
369 = m_parent.m_cf->add_local_variable ("_retvalue_temp",
|
|
370 TREE_TYPE (ret_value));
|
|
371 TREE_ADDRESSABLE (temp_var) = 1;
|
|
372 }
|
|
373
|
|
374 if (is_kernel)
|
|
375 {
|
|
376 m_parent.m_cf->add_id_variables ();
|
|
377
|
|
378 /* Create a single entry point in the function. */
|
|
379 m_parent.m_cf->m_entry_label_stmt
|
|
380 = build_stmt (LABEL_EXPR, m_parent.m_cf->label ("__kernel_entry"));
|
|
381 m_parent.m_cf->append_statement (m_parent.m_cf->m_entry_label_stmt);
|
|
382
|
|
383 tree bind_expr = m_parent.m_cf->m_current_bind_expr;
|
|
384 tree stmts = BIND_EXPR_BODY (bind_expr);
|
|
385
|
|
386 m_parent.m_cf->m_kernel_entry = tsi_last (stmts);
|
|
387
|
|
388 /* Let's not append the exit label yet, but only after the
|
|
389 function has been built. We need to build it so it can
|
|
390 be referred to because returns are converted to gotos to this
|
|
391 label. */
|
|
392 m_parent.m_cf->m_exit_label = m_parent.m_cf->label ("__kernel_exit");
|
|
393 }
|
|
394
|
|
395 return bytes_consumed;
|
|
396 }
|