111
|
1 /* brig-branch-inst-handler.cc -- brig branch instruction 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 "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
|
131
|
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()));
|
111
|
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);
|
131
|
154 if (DECL_EXTERNAL (func_ref))
|
|
155 m_parent.add_decl_call (call);
|
|
156 m_parent.m_cf->start_new_bb ();
|
111
|
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
|
131
|
174 tree switch_expr = build2 (SWITCH_EXPR, TREE_TYPE (select), select,
|
|
175 NULL_TREE);
|
111
|
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
|
131
|
221 (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode,
|
|
222 BRIG_TYPE_NONE, NULL_TREE,
|
|
223 call_operands));
|
111
|
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
|
131
|
230 (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode,
|
|
231 BRIG_TYPE_NONE,
|
|
232 uint32_type_node, operands));
|
111
|
233 }
|
|
234 else
|
|
235 gcc_unreachable ();
|
131
|
236 m_parent.m_cf->start_new_bb ();
|
111
|
237 return base->byteCount;
|
|
238 }
|