Mercurial > hg > CbC > CbC_gcc
comparison gcc/brig/brigfrontend/brig-function-handler.cc @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
comparison
equal
deleted
inserted
replaced
68:561a7518be6b | 111:04ced10e8804 |
---|---|
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 } |