]>
Commit | Line | Data |
---|---|---|
5fd1486c | 1 | /* brig-branch-inst-handler.cc -- brig branch instruction handling |
8d9254fc | 2 | Copyright (C) 2016-2020 Free Software Foundation, Inc. |
5fd1486c PJ |
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; | |
e1e299f3 HL |
45 | /* Ten elem initially, more reserved if needed. */ |
46 | vec_alloc (in_args, 10); | |
5fd1486c PJ |
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 | ||
14108eda | 73 | bool out_args_p = i == 0; |
5fd1486c PJ |
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. */ | |
14108eda | 88 | out_args_p = false; |
5fd1486c PJ |
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); | |
14108eda | 100 | vec_safe_push (out_args_p ? out_args : in_args, var); |
5fd1486c PJ |
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 | ||
d4b7f2ee | 121 | tree group_local_offset |
080dc243 PJ |
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())); | |
d4b7f2ee PJ |
127 | |
128 | /* TODO: ensure the callee's frame is aligned! */ | |
129 | ||
e1e299f3 | 130 | vec_safe_reserve (in_args, 4); |
5fd1486c PJ |
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); | |
d4b7f2ee | 133 | vec_safe_push (in_args, group_local_offset); |
5fd1486c PJ |
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 | ||
5fd1486c | 153 | m_parent.m_cf->m_called_functions.push_back (func_ref); |
637f3cde PJ |
154 | if (DECL_EXTERNAL (func_ref)) |
155 | m_parent.add_decl_call (call); | |
080dc243 | 156 | m_parent.m_cf->start_new_bb (); |
5fd1486c PJ |
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 | ||
9e851845 JJ |
174 | tree switch_expr = build2 (SWITCH_EXPR, TREE_TYPE (select), select, |
175 | NULL_TREE); | |
5fd1486c PJ |
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 | |
080dc243 PJ |
221 | (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode, |
222 | BRIG_TYPE_NONE, NULL_TREE, | |
223 | call_operands)); | |
5fd1486c PJ |
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 | |
080dc243 PJ |
230 | (m_parent.m_cf->expand_or_call_builtin (brig_inst->opcode, |
231 | BRIG_TYPE_NONE, | |
232 | uint32_type_node, operands)); | |
5fd1486c PJ |
233 | } |
234 | else | |
235 | gcc_unreachable (); | |
080dc243 | 236 | m_parent.m_cf->start_new_bb (); |
5fd1486c PJ |
237 | return base->byteCount; |
238 | } |