]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/brig/brigfrontend/brig-branch-inst-handler.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / brig / brigfrontend / brig-branch-inst-handler.cc
1 /* brig-branch-inst-handler.cc -- brig branch instruction handling
2 Copyright (C) 2016-2020 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 "brig-code-entry-handler.h"
23
24 #include "errors.h"
25 #include "brig-util.h"
26 #include "tree-pretty-print.h"
27 #include "print-tree.h"
28 #include "vec.h"
29 #include "fold-const.h"
30
31 size_t
32 brig_branch_inst_handler::operator () (const BrigBase *base)
33 {
34 const BrigInstBase *brig_inst
35 = (const BrigInstBase *) &((const BrigInstBasic *) base)->base;
36
37 if (brig_inst->opcode == BRIG_OPCODE_CALL)
38 {
39 const BrigData *operand_entries
40 = m_parent.get_brig_data_entry (brig_inst->operands);
41 tree func_ref = NULL_TREE;
42 vec<tree, va_gc> *out_args;
43 vec_alloc (out_args, 1);
44 vec<tree, va_gc> *in_args;
45 /* Ten elem initially, more reserved if needed. */
46 vec_alloc (in_args, 10);
47
48 size_t operand_count = operand_entries->byteCount / 4;
49 gcc_assert (operand_count < 4);
50
51 for (size_t i = 0; i < operand_count; ++i)
52 {
53 uint32_t operand_offset
54 = ((const uint32_t *) &operand_entries->bytes)[i];
55 const BrigBase *operand_data
56 = m_parent.get_brig_operand_entry (operand_offset);
57 if (i == 1)
58 {
59 gcc_assert (operand_data->kind == BRIG_KIND_OPERAND_CODE_REF);
60 func_ref = build_tree_operand (*brig_inst, *operand_data);
61 continue;
62 }
63 gcc_assert (operand_data->kind == BRIG_KIND_OPERAND_CODE_LIST);
64 const BrigOperandCodeList *codelist
65 = (const BrigOperandCodeList *) operand_data;
66 const BrigData *data
67 = m_parent.get_brig_data_entry (codelist->elements);
68
69 size_t bytes = data->byteCount;
70 const BrigOperandOffset32_t *operand_ptr
71 = (const BrigOperandOffset32_t *) data->bytes;
72
73 bool out_args_p = i == 0;
74
75 while (bytes > 0)
76 {
77 BrigOperandOffset32_t offset = *operand_ptr;
78 const BrigBase *code_element
79 = m_parent.get_brig_code_entry (offset);
80 gcc_assert (code_element->kind == BRIG_KIND_DIRECTIVE_VARIABLE);
81 const BrigDirectiveVariable *brig_var
82 = (const BrigDirectiveVariable *) code_element;
83 tree var = m_parent.m_cf->arg_variable (brig_var);
84
85 if (brig_var->type & BRIG_TYPE_ARRAY)
86 {
87 /* Array return values are passed as the first argument. */
88 out_args_p = false;
89 /* Pass pointer to the element zero and use its element zero
90 as the base address. */
91 tree etype = TREE_TYPE (TREE_TYPE (var));
92 tree ptype = build_pointer_type (etype);
93 tree element_zero
94 = build4 (ARRAY_REF, etype, var, integer_zero_node,
95 NULL_TREE, NULL_TREE);
96 var = build1 (ADDR_EXPR, ptype, element_zero);
97 }
98
99 gcc_assert (var != NULL_TREE);
100 vec_safe_push (out_args_p ? out_args : in_args, var);
101 ++operand_ptr;
102 bytes -= 4;
103 }
104 }
105
106 gcc_assert (func_ref != NULL_TREE);
107 gcc_assert (out_args->length () == 0 || out_args->length () == 1);
108
109 tree ret_val_type = void_type_node;
110 tree ret_val = NULL_TREE;
111 if (out_args->length () == 1)
112 {
113 ret_val = (*out_args)[0];
114 ret_val_type = TREE_TYPE (ret_val);
115 }
116
117 /* Pass the hidden kernel arguments along to the called functions as
118 they might call builtins that need them or access group/private
119 memory. */
120
121 tree group_local_offset
122 = m_parent.m_cf->add_temp_var ("group_local_offset",
123 build_int_cst
124 (uint32_type_node,
125 m_parent.m_cf->
126 m_local_group_variables.size()));
127
128 /* TODO: ensure the callee's frame is aligned! */
129
130 vec_safe_reserve (in_args, 4);
131 vec_safe_push (in_args, m_parent.m_cf->m_context_arg);
132 vec_safe_push (in_args, m_parent.m_cf->m_group_base_arg);
133 vec_safe_push (in_args, group_local_offset);
134 vec_safe_push (in_args, m_parent.m_cf->m_private_base_arg);
135
136 tree call = build_call_vec (ret_val_type, build_fold_addr_expr (func_ref),
137 in_args);
138 TREE_NOTHROW (func_ref) = 1;
139 TREE_NOTHROW (call) = 1;
140
141 if (ret_val != NULL_TREE)
142 {
143 TREE_ADDRESSABLE (ret_val) = 1;
144 tree result_assign
145 = build2 (MODIFY_EXPR, TREE_TYPE (ret_val), ret_val, call);
146 m_parent.m_cf->append_statement (result_assign);
147 }
148 else
149 {
150 m_parent.m_cf->append_statement (call);
151 }
152
153 m_parent.m_cf->m_called_functions.push_back (func_ref);
154 if (DECL_EXTERNAL (func_ref))
155 m_parent.add_decl_call (call);
156 m_parent.m_cf->start_new_bb ();
157
158 return base->byteCount;
159 }
160
161 tree instr_type = gccbrig_tree_type_for_hsa_type (brig_inst->type);
162 tree_stl_vec operands = build_operands (*brig_inst);
163
164 if (brig_inst->opcode == BRIG_OPCODE_BR)
165 {
166 tree goto_stmt = build1 (GOTO_EXPR, instr_type, operands[0]);
167 m_parent.m_cf->append_statement (goto_stmt);
168 }
169 else if (brig_inst->opcode == BRIG_OPCODE_SBR)
170 {
171 tree select = operands[0];
172 tree cases = operands[1];
173
174 tree switch_expr = build2 (SWITCH_EXPR, TREE_TYPE (select), select,
175 NULL_TREE);
176
177 tree default_case
178 = build_case_label (NULL_TREE, NULL_TREE,
179 create_artificial_label (UNKNOWN_LOCATION));
180 append_to_statement_list (default_case, &SWITCH_BODY (switch_expr));
181
182 tree default_jump
183 = build1 (GOTO_EXPR, void_type_node, TREE_VEC_ELT (cases, 0));
184 append_to_statement_list (default_jump, &SWITCH_BODY (switch_expr));
185
186 for (int c = 0; c < TREE_VEC_LENGTH (cases); ++c)
187 {
188 tree case_label
189 = build_case_label (build_int_cst (integer_type_node, c), NULL_TREE,
190 create_artificial_label (UNKNOWN_LOCATION));
191
192 append_to_statement_list (case_label, &SWITCH_BODY (switch_expr));
193
194 tree jump
195 = build1 (GOTO_EXPR, void_type_node, TREE_VEC_ELT (cases, c));
196 append_to_statement_list (jump, &SWITCH_BODY (switch_expr));
197 }
198 m_parent.m_cf->append_statement (switch_expr);
199 }
200 else if (brig_inst->opcode == BRIG_OPCODE_CBR)
201 {
202 tree condition = operands[0];
203 tree target_goto = build1 (GOTO_EXPR, void_type_node, operands[1]);
204 /* Represents the if..else as (condition)?(goto foo):(goto bar). */
205 tree if_stmt
206 = build3 (COND_EXPR, void_type_node, condition, target_goto, NULL_TREE);
207 m_parent.m_cf->append_statement (if_stmt);
208 }
209 else if (brig_inst->opcode == BRIG_OPCODE_WAVEBARRIER)
210 {
211 /* WAVEBARRIER is a NOP when WAVESIZE = 1. */
212 }
213 else if (brig_inst->opcode == BRIG_OPCODE_BARRIER)
214 {
215 m_parent.m_cf->m_has_barriers = true;
216 tree_stl_vec call_operands;
217 /* FIXME. We should add attributes (are there suitable ones in gcc?) that
218 ensure the barrier won't be duplicated or moved out of loops etc.
219 Like the 'noduplicate' of LLVM. Same goes for fbarriers. */
220 m_parent.m_cf->append_statement
221 (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode,
222 BRIG_TYPE_NONE, NULL_TREE,
223 call_operands));
224 }
225 else if (brig_inst->opcode >= BRIG_OPCODE_ARRIVEFBAR
226 && brig_inst->opcode <= BRIG_OPCODE_WAITFBAR)
227 {
228 m_parent.m_cf->m_has_barriers = true;
229 m_parent.m_cf->append_statement
230 (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode,
231 BRIG_TYPE_NONE,
232 uint32_type_node, operands));
233 }
234 else
235 gcc_unreachable ();
236 m_parent.m_cf->start_new_bb ();
237 return base->byteCount;
238 }