]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/brig/brigfrontend/brig-function.cc
brig-function.cc: Include profile-count.h.
[thirdparty/gcc.git] / gcc / brig / brigfrontend / brig-function.cc
1 /* brig-function.cc -- declaration of brig_function class.
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-function.h"
26 #include "stringpool.h"
27 #include "tree-iterator.h"
28 #include "toplev.h"
29 #include "gimplify.h"
30 #include "gimple-expr.h"
31 #include "print-tree.h"
32 #include "hsa-brig-format.h"
33 #include "stor-layout.h"
34 #include "diagnostic-core.h"
35 #include "brig-code-entry-handler.h"
36 #include "brig-machine.h"
37 #include "brig-util.h"
38 #include "phsa.h"
39 #include "tree-pretty-print.h"
40 #include "dumpfile.h"
41 #include "profile-count.h"
42 #include "tree-cfg.h"
43 #include "errors.h"
44 #include "function.h"
45 #include "brig-to-generic.h"
46 #include "brig-builtins.h"
47
48 brig_function::brig_function (const BrigDirectiveExecutable *exec,
49 brig_to_generic *parent)
50 : m_brig_def (exec), m_is_kernel (false), m_is_finished (false), m_name (""),
51 m_current_bind_expr (NULL_TREE), m_func_decl (NULL_TREE),
52 m_context_arg (NULL_TREE), m_group_base_arg (NULL_TREE),
53 m_private_base_arg (NULL_TREE), m_ret_value (NULL_TREE),
54 m_next_kernarg_offset (0), m_kernarg_max_align (0),
55 m_ret_value_brig_var (NULL), m_has_barriers (false),
56 m_has_allocas (false), m_has_function_calls_with_barriers (false),
57 m_calls_analyzed (false), m_is_wg_function (false),
58 m_has_unexpanded_dp_builtins (false), m_generating_arg_block (false),
59 m_parent (parent)
60 {
61 memset (m_regs, 0,
62 BRIG_2_TREE_HSAIL_TOTAL_REG_COUNT * sizeof (BrigOperandRegister *));
63 memset (&m_descriptor, 0, sizeof (phsa_descriptor));
64 }
65
66 brig_function::~brig_function ()
67 {
68 for (size_t i = 0; i < BRIG_2_TREE_HSAIL_TOTAL_REG_COUNT; ++i)
69 {
70 if (m_regs[i] != NULL)
71 {
72 delete m_regs[i];
73 m_regs[i] = NULL;
74 }
75 }
76 }
77
78 /* Returns a GENERIC label with the given name in the given function.
79 Creates it, if not yet found. */
80
81 tree
82 brig_function::label (const std::string &name)
83 {
84 label_index::const_iterator i = m_label_index.find (name);
85 if (i == m_label_index.end ())
86 {
87 tree name_identifier
88 = get_identifier_with_length (name.c_str (), name.size ());
89
90 tree label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
91 name_identifier, void_type_node);
92
93 DECL_CONTEXT (label_decl) = m_func_decl;
94 DECL_ARTIFICIAL (label_decl) = 0;
95
96 m_label_index[name] = label_decl;
97 return label_decl;
98 }
99 else
100 return (*i).second;
101 }
102
103 /* Record an argument variable for later use. This includes both local
104 variables inside arg blocks and incoming function arguments. */
105
106 void
107 brig_function::add_arg_variable (const BrigDirectiveVariable *brigVar,
108 tree treeDecl)
109 {
110 m_arg_variables[brigVar] = treeDecl;
111 }
112
113 tree
114 brig_function::arg_variable (const BrigDirectiveVariable *var) const
115 {
116 variable_index::const_iterator i = m_arg_variables.find (var);
117 if (i == m_arg_variables.end ())
118 return NULL_TREE;
119 else
120 return (*i).second;
121 }
122
123 /* Appends a new kernel argument descriptor for the current kernel's
124 arg space. */
125
126 void
127 brig_function::append_kernel_arg (const BrigDirectiveVariable *var, size_t size,
128 size_t alignment)
129 {
130 gcc_assert (m_func_decl != NULL_TREE);
131 gcc_assert (m_is_kernel);
132
133 size_t align_padding = m_next_kernarg_offset % alignment == 0 ?
134 0 : (alignment - m_next_kernarg_offset % alignment);
135 m_next_kernarg_offset += align_padding;
136 m_kernarg_offsets[var] = m_next_kernarg_offset;
137 m_next_kernarg_offset += size;
138
139 m_kernarg_max_align
140 = m_kernarg_max_align < alignment ? alignment : m_kernarg_max_align;
141 }
142
143 size_t
144 brig_function::kernel_arg_offset (const BrigDirectiveVariable *var) const
145 {
146 var_offset_table::const_iterator i = m_kernarg_offsets.find (var);
147 gcc_assert (i != m_kernarg_offsets.end ());
148 return (*i).second;
149 }
150
151 /* Add work-item ID variables to the beginning of the kernel function
152 which can be used for address computation as kernel dispatch packet
153 instructions can be expanded to GENERIC nodes referring to them. */
154
155 void
156 brig_function::add_id_variables ()
157 {
158 tree bind_expr = m_current_bind_expr;
159 tree stmts = BIND_EXPR_BODY (bind_expr);
160
161 /* Initialize the WG limits and local ids. */
162
163 tree_stmt_iterator entry = tsi_start (stmts);
164
165 for (int i = 0; i < 3; ++i)
166 {
167 char dim_char = (char) ((int) 'x' + i);
168
169 /* The local sizes are limited to 16b values, but let's still use 32b
170 to avoid unnecessary casts (the ID functions are 32b). */
171 m_local_id_vars[i]
172 = add_local_variable (std::string ("__local_") + dim_char,
173 uint32_type_node);
174
175 tree workitemid_call
176 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_WORKITEMID), 2,
177 uint32_type_node, uint32_type_node,
178 build_int_cst (uint32_type_node, i), ptr_type_node,
179 m_context_arg);
180
181 tree id_init = build2 (MODIFY_EXPR, TREE_TYPE (m_local_id_vars[i]),
182 m_local_id_vars[i], workitemid_call);
183
184 tsi_link_after (&entry, id_init, TSI_NEW_STMT);
185
186 m_cur_wg_size_vars[i]
187 = add_local_variable (std::string ("__cur_wg_size_") + dim_char,
188 uint32_type_node);
189
190 tree cwgz_call
191 = call_builtin
192 (builtin_decl_explicit (BUILT_IN_HSAIL_CURRENTWORKGROUPSIZE),
193 2, uint32_type_node, uint32_type_node,
194 build_int_cst (uint32_type_node, i), ptr_type_node, m_context_arg);
195
196 tree limit_init = build2 (MODIFY_EXPR, TREE_TYPE (m_cur_wg_size_vars[i]),
197 m_cur_wg_size_vars[i], cwgz_call);
198
199 tsi_link_after (&entry, limit_init, TSI_NEW_STMT);
200
201 m_wg_id_vars[i]
202 = add_local_variable (std::string ("__workgroupid_") + dim_char,
203 uint32_type_node);
204
205 tree wgid_call
206 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_WORKGROUPID),
207 2, uint32_type_node, uint32_type_node,
208 build_int_cst (uint32_type_node, i), ptr_type_node,
209 m_context_arg);
210
211 tree wgid_init = build2 (MODIFY_EXPR, TREE_TYPE (m_wg_id_vars[i]),
212 m_wg_id_vars[i], wgid_call);
213
214 tsi_link_after (&entry, wgid_init, TSI_NEW_STMT);
215
216 m_wg_size_vars[i]
217 = add_local_variable (std::string ("__workgroupsize_") + dim_char,
218 uint32_type_node);
219
220 tree wgsize_call
221 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_WORKGROUPSIZE),
222 2, uint32_type_node, uint32_type_node,
223 build_int_cst (uint32_type_node, i), ptr_type_node,
224 m_context_arg);
225
226 tree wgsize_init = build2 (MODIFY_EXPR, TREE_TYPE (m_wg_size_vars[i]),
227 m_wg_size_vars[i], wgsize_call);
228
229 tsi_link_after (&entry, wgsize_init, TSI_NEW_STMT);
230
231 m_grid_size_vars[i]
232 = add_local_variable (std::string ("__gridsize_") + dim_char,
233 uint32_type_node);
234
235 tree gridsize_call
236 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_GRIDSIZE), 2,
237 uint32_type_node, uint32_type_node,
238 build_int_cst (uint32_type_node, i), ptr_type_node,
239 m_context_arg);
240
241 tree gridsize_init = build2 (MODIFY_EXPR, TREE_TYPE (m_grid_size_vars[i]),
242 m_grid_size_vars[i], gridsize_call);
243
244 tsi_link_after (&entry, gridsize_init, TSI_NEW_STMT);
245 }
246
247 m_kernel_entry = entry;
248 }
249
250 /* Creates a new local variable with the given NAME and given GENERIC
251 TYPE. */
252
253 tree
254 brig_function::add_local_variable (std::string name, tree type)
255 {
256 tree name_identifier
257 = get_identifier_with_length (name.c_str (), name.size ());
258 tree variable
259 = build_decl (UNKNOWN_LOCATION, VAR_DECL, name_identifier, type);
260
261 DECL_NONLOCAL (variable) = 0;
262 TREE_ADDRESSABLE (variable) = 0;
263 TREE_STATIC (variable) = 0;
264 TREE_USED (variable) = 1;
265 DECL_ARTIFICIAL (variable) = 0;
266
267 tree bind_expr = DECL_SAVED_TREE (m_func_decl);
268
269 DECL_CONTEXT (variable) = m_func_decl;
270
271 DECL_CHAIN (variable) = BIND_EXPR_VARS (bind_expr);
272 BIND_EXPR_VARS (bind_expr) = variable;
273 return variable;
274 }
275
276 /* Returns a DECL_VAR for the given HSAIL operand register.
277 If it has not been created yet for the function being generated,
278 creates it as an unsigned int variable. */
279
280 tree
281 brig_function::get_m_var_declfor_reg (const BrigOperandRegister *reg)
282 {
283 size_t offset = reg->regNum;
284 switch (reg->regKind)
285 {
286 case BRIG_REGISTER_KIND_QUAD:
287 offset
288 += BRIG_2_TREE_HSAIL_D_REG_COUNT + BRIG_2_TREE_HSAIL_S_REG_COUNT +
289 BRIG_2_TREE_HSAIL_C_REG_COUNT;
290 break;
291 case BRIG_REGISTER_KIND_DOUBLE:
292 offset += BRIG_2_TREE_HSAIL_S_REG_COUNT + BRIG_2_TREE_HSAIL_C_REG_COUNT;
293 break;
294 case BRIG_REGISTER_KIND_SINGLE:
295 offset += BRIG_2_TREE_HSAIL_C_REG_COUNT;
296 case BRIG_REGISTER_KIND_CONTROL:
297 break;
298 default:
299 gcc_unreachable ();
300 break;
301 }
302
303 reg_decl_index_entry *regEntry = m_regs[offset];
304 if (regEntry == NULL)
305 {
306 size_t reg_size = gccbrig_reg_size (reg);
307 tree type;
308 if (reg_size > 1)
309 type = build_nonstandard_integer_type (reg_size, true);
310 else
311 type = boolean_type_node;
312
313 /* Drop the const qualifier so we do not end up with a read only
314 register variable which cannot be written to later. */
315 tree nonconst_type = build_type_variant (type, false, false);
316
317 regEntry = new reg_decl_index_entry;
318
319 regEntry->m_var_decl
320 = add_local_variable (gccbrig_reg_name (reg), nonconst_type);
321 m_regs[offset] = regEntry;
322 }
323 return regEntry->m_var_decl;
324 }
325
326 /* Builds a work-item do..while loop for a single DIM. HEADER_ENTRY is
327 a statement after which the iteration variables should be initialized and
328 the loop body starts. BRANCH_AFTER is the statement after which the loop
329 predicate check and the back edge goto will be appended. */
330
331 void
332 brig_function::add_wi_loop (int dim, tree_stmt_iterator *header_entry,
333 tree_stmt_iterator *branch_after)
334 {
335 tree ivar = m_local_id_vars[dim];
336 tree ivar_max = m_cur_wg_size_vars[dim];
337 tree_stmt_iterator entry = *header_entry;
338
339 /* TODO: this is not a parallel loop as we share the "register variables"
340 across work-items. Should create a copy of them per WI instance. That
341 is, declare temporaries for new definitions inside the loop body, not at
342 function scope. */
343
344 tree ivar_init = build2 (MODIFY_EXPR, TREE_TYPE (ivar), ivar,
345 build_zero_cst (TREE_TYPE (ivar)));
346 tsi_link_after (&entry, ivar_init, TSI_NEW_STMT);
347
348 tree loop_body_label
349 = label (std::string ("__wi_loop_") + (char) ((int) 'x' + dim));
350 tree loop_body_label_stmt = build_stmt (LABEL_EXPR, loop_body_label);
351
352 tsi_link_after (&entry, loop_body_label_stmt, TSI_NEW_STMT);
353
354 if (m_has_unexpanded_dp_builtins)
355 {
356 tree id_set_builtin
357 = builtin_decl_explicit (BUILT_IN_HSAIL_SETWORKITEMID);
358 /* Set the local ID to the current wi-loop iteration variable value to
359 ensure the builtins see the correct values. */
360 tree id_set_call
361 = call_builtin (id_set_builtin, 3,
362 void_type_node, uint32_type_node,
363 build_int_cst (uint32_type_node, dim), uint32_type_node,
364 ivar, ptr_type_node, m_context_arg);
365 tsi_link_after (&entry, id_set_call, TSI_NEW_STMT);
366 }
367
368 /* Increment the WI iteration variable. */
369 tree incr = build2 (PREINCREMENT_EXPR, TREE_TYPE (ivar), ivar,
370 build_one_cst (TREE_TYPE (ivar)));
371
372 tsi_link_after (branch_after, incr, TSI_NEW_STMT);
373
374 /* Append the predicate check with the back edge goto. */
375 tree condition = build2 (LT_EXPR, TREE_TYPE (ivar), ivar, ivar_max);
376 tree target_goto = build1 (GOTO_EXPR, void_type_node, loop_body_label);
377 tree if_stmt
378 = build3 (COND_EXPR, void_type_node, condition, target_goto, NULL_TREE);
379 tsi_link_after (branch_after, if_stmt, TSI_NEW_STMT);
380 }
381
382 /* Recursively analyzes the function and its callees for barrier usage. */
383
384 void
385 brig_function::analyze_calls ()
386 {
387 if (m_calls_analyzed)
388 return;
389
390 /* Set this early to not get stuck in case of recursive call graphs.
391 This is safe because if the function calls itself, either the function
392 has barrier calls which implies a call to a function with barrier calls,
393 or it doesn't in which case the result depends on the later called
394 functions. */
395 m_calls_analyzed = true;
396
397 for (size_t i = 0; i < m_called_functions.size (); ++i)
398 {
399 tree f = m_called_functions[i];
400 brig_function *called_f = m_parent->get_finished_function (f);
401 if (called_f == NULL)
402 {
403 /* Unfinished function (only declaration within the set of BRIGs)
404 found. Cannot finish the CG analysis. Have to assume it does have
405 a barrier for safety. */
406 m_has_function_calls_with_barriers = true;
407 m_has_unexpanded_dp_builtins = true;
408 break;
409 }
410 called_f->analyze_calls ();
411 /* We can assume m_has_barriers has been correctly set during the
412 construction of the function decl. No need to reanalyze it. */
413 m_has_function_calls_with_barriers |= called_f->m_has_barriers;
414
415 /* If the function or any of its called functions has dispatch
416 packet builtin calls that require the local id, we need to
417 set the local id to the context in the work item loop before
418 the functions are called. If we analyze the opposite, these
419 function calls can be omitted. */
420 m_has_unexpanded_dp_builtins |= called_f->m_has_unexpanded_dp_builtins;
421 }
422 }
423
424 /* Tries to convert the current kernel to a work-group function that executes
425 all work-items using loops. Returns true in case the conversion was
426 successful. */
427
428 bool
429 brig_function::convert_to_wg_function ()
430 {
431 if (!m_calls_analyzed)
432 analyze_calls ();
433
434 if (m_has_barriers || m_has_function_calls_with_barriers)
435 return false;
436
437 /* The most trivial case: No barriers at all in the kernel.
438 We can create one big work-item loop around the whole kernel. */
439 tree bind_expr = m_current_bind_expr;
440 tree stmts = BIND_EXPR_BODY (bind_expr);
441
442 for (int i = 0; i < 3; ++i)
443 {
444 /* The previous loop has added a new label to the end of the function,
445 the next level loop should wrap around it also. */
446 tree_stmt_iterator function_exit = tsi_last (stmts);
447 add_wi_loop (i, &m_kernel_entry, &function_exit);
448 }
449
450 m_is_wg_function = true;
451 return false;
452 }
453
454 /* Emits a kernel description to a special ELF section so it can be
455 utilized by an HSA runtime implementation. The assembly block
456 must be emitted to a statement list of an function, which is given
457 as an argument. Returns the assembly block used to emit the section. */
458
459 tree
460 brig_function::emit_metadata (tree stmt_list)
461 {
462 /* Emit an ELF section via an assembly directive that generates a special
463 ELF section for each kernel that contains raw bytes of a descriptor
464 object. This is pretty disgusting, but life is never perfect ;) */
465
466 /* Use the original kernel name without the '_' prefix in the section name. */
467 std::string kern_name = m_is_kernel ? m_name.substr (1) : m_name;
468
469 std::ostringstream strstr;
470 strstr << std::endl
471 << ".pushsection " << PHSA_DESC_SECTION_PREFIX << kern_name
472 << std::endl
473 << "\t.p2align 1, 1, 1" << std::endl
474 << "\t.byte ";
475
476 for (size_t i = 0; i < sizeof (phsa_descriptor); ++i)
477 {
478 strstr << "0x" << std::setw (2) << std::setfill ('0') << std::hex
479 << (unsigned) *((unsigned char *) &m_descriptor + i);
480 if (i + 1 < sizeof (phsa_descriptor))
481 strstr << ", ";
482 }
483
484 strstr << std::endl << ".popsection" << std::endl << std::endl;
485
486 tree metadata_asm
487 = build_stmt (ASM_EXPR,
488 build_string (strstr.str ().size (), strstr.str ().c_str ()),
489 NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
490
491 append_to_statement_list_force (metadata_asm, &stmt_list);
492 return metadata_asm;
493 }
494
495 /* Emits the kernel launcher function. Also emits the metadata section
496 creation statements in it.
497
498 The launcher function calls the device-side runtime
499 that runs the kernel for all work-items. In C:
500
501 void KernelName (void* context, void* group_base_addr)
502 {
503 __hsail_launch_kernel (_KernelName, context, group_base_addr);
504 }
505
506 or, in case of a successful conversion to a work-group function:
507
508 void KernelName (void* context, void* group_base_addr)
509 {
510 __hsail_launch_wg_function (_KernelName, context, group_base_addr);
511 }
512
513 The user/host sees this function as the kernel to call from the
514 outside. The actual kernel generated from HSAIL was named _KernelName.
515 */
516
517 tree
518 brig_function::emit_launcher_and_metadata ()
519 {
520 /* The original kernel name without the '_' prefix. */
521 std::string kern_name = m_name.substr (1);
522
523 tree name_identifier
524 = get_identifier_with_length (kern_name.c_str (), kern_name.size ());
525
526 tree launcher
527 = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
528 build_function_type_list (void_type_node, ptr_type_node,
529 ptr_type_node, NULL_TREE));
530
531 TREE_USED (launcher) = 1;
532 DECL_ARTIFICIAL (launcher) = 1;
533
534 tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
535 get_identifier ("__context"), ptr_type_node);
536
537 DECL_ARGUMENTS (launcher) = context_arg;
538 DECL_ARG_TYPE (context_arg) = ptr_type_node;
539 DECL_CONTEXT (context_arg) = launcher;
540 TREE_USED (context_arg) = 1;
541 DECL_ARTIFICIAL (context_arg) = 1;
542
543 tree group_base_addr_arg
544 = build_decl (UNKNOWN_LOCATION, PARM_DECL,
545 get_identifier ("__group_base_addr"), ptr_type_node);
546
547 chainon (DECL_ARGUMENTS (launcher), group_base_addr_arg);
548 DECL_ARG_TYPE (group_base_addr_arg) = ptr_type_node;
549 DECL_CONTEXT (group_base_addr_arg) = launcher;
550 TREE_USED (group_base_addr_arg) = 1;
551 DECL_ARTIFICIAL (group_base_addr_arg) = 1;
552
553 tree resdecl
554 = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node);
555
556 DECL_RESULT (launcher) = resdecl;
557 DECL_CONTEXT (resdecl) = launcher;
558
559 DECL_INITIAL (launcher) = make_node (BLOCK);
560 TREE_USED (DECL_INITIAL (launcher)) = 1;
561
562 tree stmt_list = alloc_stmt_list ();
563
564 tree bind_expr = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, NULL);
565
566 TREE_STATIC (launcher) = 0;
567 TREE_PUBLIC (launcher) = 1;
568
569 DECL_SAVED_TREE (launcher) = bind_expr;
570
571 if (DECL_STRUCT_FUNCTION (launcher) == NULL)
572 push_struct_function (launcher);
573 else
574 push_cfun (DECL_STRUCT_FUNCTION (launcher));
575
576 tree kernel_func_ptr = build1 (ADDR_EXPR, ptr_type_node, m_func_decl);
577
578 tree phsail_launch_kernel_call;
579
580 /* Emit a launcher depending whether we converted the kernel function to
581 a work group function or not. */
582 if (m_is_wg_function)
583 phsail_launch_kernel_call
584 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_LAUNCH_WG_FUNC),
585 3, void_type_node,
586 ptr_type_node, kernel_func_ptr, ptr_type_node,
587 context_arg, ptr_type_node, group_base_addr_arg);
588 else
589 phsail_launch_kernel_call
590 = call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_LAUNCH_KERNEL),
591 3, void_type_node,
592 ptr_type_node, kernel_func_ptr, ptr_type_node,
593 context_arg, ptr_type_node, group_base_addr_arg);
594
595 append_to_statement_list_force (phsail_launch_kernel_call, &stmt_list);
596
597 emit_metadata (stmt_list);
598
599 return launcher;
600 }
601
602 tree
603 brig_function::append_statement (tree stmt)
604 {
605 gcc_assert (m_func_decl != NULL);
606
607 tree bind_expr = m_current_bind_expr;
608 tree stmts = BIND_EXPR_BODY (bind_expr);
609
610 append_to_statement_list_force (stmt, &stmts);
611 return stmt;
612 }
613
614 /* Creates a new "alloca frame" for the current function by
615 injecting an alloca frame push in the beginning of the function
616 and an alloca frame pop before all function exit points. */
617
618 void
619 brig_function::create_alloca_frame ()
620 {
621 tree_stmt_iterator entry;
622
623 /* Adds the alloca push only after the ids have been initialized
624 in case of a kernel function. */
625 if (m_is_kernel)
626 entry = m_kernel_entry;
627 else
628 {
629 tree bind_expr = m_current_bind_expr;
630 tree stmts = BIND_EXPR_BODY (bind_expr);
631 entry = tsi_start (stmts);
632 }
633
634 tree push_frame_builtin = builtin_decl_explicit (BUILT_IN_HSAIL_PUSH_FRAME);
635 tree push_frame_call
636 = call_builtin (push_frame_builtin, 1, void_type_node, ptr_type_node,
637 m_context_arg);
638
639 tsi_link_before (&entry, push_frame_call, TSI_NEW_STMT);
640
641 tree pop_frame_builtin = builtin_decl_explicit (BUILT_IN_HSAIL_POP_FRAME);
642
643 do
644 {
645 tree stmt = tsi_stmt (entry);
646 if (TREE_CODE (stmt) == RETURN_EXPR)
647 {
648 tree pop_frame_call
649 = call_builtin (pop_frame_builtin, 1, void_type_node,
650 ptr_type_node, m_context_arg);
651
652 tsi_link_before (&entry, pop_frame_call, TSI_SAME_STMT);
653 }
654 tsi_next (&entry);
655 }
656 while (!tsi_end_p (entry));
657 }
658
659 /* Finishes the currently built function. After calling this, no new
660 statements should be appeneded to the function. */
661 void
662 brig_function::finish ()
663 {
664 append_return_stmt ();
665
666 /* Currently assume single alloca frame per WG. */
667 if (m_has_allocas)
668 create_alloca_frame ();
669 }
670
671 void
672 brig_function::finish_kernel ()
673 {
674 /* Kernel functions should have a single exit point.
675 Let's create one. The return instructions should have
676 been converted to branches to this label. */
677 append_statement (build_stmt (LABEL_EXPR, m_exit_label));
678 /* Attempt to convert the kernel to a work-group function that
679 executes all work-items of the WG using a loop. */
680 convert_to_wg_function ();
681
682 append_return_stmt ();
683
684 /* Currently assume single alloca frame per WG. */
685 if (m_has_allocas)
686 create_alloca_frame ();
687 }
688
689 void
690 brig_function::append_return_stmt ()
691 {
692 gcc_assert (m_current_bind_expr != NULL_TREE);
693 tree stmts = BIND_EXPR_BODY (m_current_bind_expr);
694
695 if (STATEMENT_LIST_TAIL (stmts) == NULL)
696 return; /* Empty function. */
697
698 tree last_stmt = tsi_stmt (tsi_last (stmts));
699
700 if (TREE_CODE (last_stmt) == RETURN_EXPR)
701 return;
702
703 if (m_ret_value != NULL_TREE)
704 {
705 tree result_assign
706 = build2 (MODIFY_EXPR, TREE_TYPE (m_ret_value), m_ret_value,
707 m_ret_temp);
708
709 tree return_expr
710 = build1 (RETURN_EXPR, TREE_TYPE (result_assign), result_assign);
711 append_to_statement_list_force (return_expr, &stmts);
712 }
713 else
714 {
715 tree return_stmt = build_stmt (RETURN_EXPR, NULL);
716 append_to_statement_list_force (return_stmt, &stmts);
717 }
718 }
719
720 bool
721 brig_function::has_function_scope_var (const BrigBase* var) const
722 {
723 return m_function_scope_vars.find (var) != m_function_scope_vars.end ();
724 }