1 /* brig-branch-inst-handler.cc -- brig branch instruction handling
2 Copyright (C) 2016-2018 Free Software Foundation, Inc.
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4 for General Processor Tech.
6 This file is part of GCC.
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
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
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/>. */
22 #include "brig-code-entry-handler.h"
25 #include "brig-util.h"
26 #include "tree-pretty-print.h"
27 #include "print-tree.h"
29 #include "fold-const.h"
32 brig_branch_inst_handler::operator () (const BrigBase
*base
)
34 const BrigInstBase
*brig_inst
35 = (const BrigInstBase
*) &((const BrigInstBasic
*) base
)->base
;
37 if (brig_inst
->opcode
== BRIG_OPCODE_CALL
)
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);
48 size_t operand_count
= operand_entries
->byteCount
/ 4;
49 gcc_assert (operand_count
< 4);
51 for (size_t i
= 0; i
< operand_count
; ++i
)
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
);
59 gcc_assert (operand_data
->kind
== BRIG_KIND_OPERAND_CODE_REF
);
60 func_ref
= build_tree_operand (*brig_inst
, *operand_data
);
63 gcc_assert (operand_data
->kind
== BRIG_KIND_OPERAND_CODE_LIST
);
64 const BrigOperandCodeList
*codelist
65 = (const BrigOperandCodeList
*) operand_data
;
67 = m_parent
.get_brig_data_entry (codelist
->elements
);
69 size_t bytes
= data
->byteCount
;
70 const BrigOperandOffset32_t
*operand_ptr
71 = (const BrigOperandOffset32_t
*) data
->bytes
;
73 bool out_args_p
= i
== 0;
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
);
85 if (brig_var
->type
& BRIG_TYPE_ARRAY
)
87 /* Array return values are passed as the first argument. */
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
);
94 = build4 (ARRAY_REF
, etype
, var
, integer_zero_node
,
95 NULL_TREE
, NULL_TREE
);
96 var
= build1 (ADDR_EXPR
, ptype
, element_zero
);
99 gcc_assert (var
!= NULL_TREE
);
100 vec_safe_push (out_args_p
? out_args
: in_args
, var
);
106 gcc_assert (func_ref
!= NULL_TREE
);
107 gcc_assert (out_args
->length () == 0 || out_args
->length () == 1);
109 tree ret_val_type
= void_type_node
;
110 tree ret_val
= NULL_TREE
;
111 if (out_args
->length () == 1)
113 ret_val
= (*out_args
)[0];
114 ret_val_type
= TREE_TYPE (ret_val
);
117 /* Pass the hidden kernel arguments along to the called functions as
118 they might call builtins that need them or access group/private
121 tree group_local_offset
122 = m_parent
.m_cf
->add_temp_var ("group_local_offset",
126 m_local_group_variables
.size()));
128 /* TODO: ensure the callee's frame is aligned! */
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
);
136 tree call
= build_call_vec (ret_val_type
, build_fold_addr_expr (func_ref
),
138 TREE_NOTHROW (func_ref
) = 1;
139 TREE_NOTHROW (call
) = 1;
141 if (ret_val
!= NULL_TREE
)
143 TREE_ADDRESSABLE (ret_val
) = 1;
145 = build2 (MODIFY_EXPR
, TREE_TYPE (ret_val
), ret_val
, call
);
146 m_parent
.m_cf
->append_statement (result_assign
);
150 m_parent
.m_cf
->append_statement (call
);
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 ();
158 return base
->byteCount
;
161 tree instr_type
= gccbrig_tree_type_for_hsa_type (brig_inst
->type
);
162 tree_stl_vec operands
= build_operands (*brig_inst
);
164 if (brig_inst
->opcode
== BRIG_OPCODE_BR
)
166 tree goto_stmt
= build1 (GOTO_EXPR
, instr_type
, operands
[0]);
167 m_parent
.m_cf
->append_statement (goto_stmt
);
169 else if (brig_inst
->opcode
== BRIG_OPCODE_SBR
)
171 tree select
= operands
[0];
172 tree cases
= operands
[1];
174 tree switch_expr
= build2 (SWITCH_EXPR
, TREE_TYPE (select
), select
,
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
));
183 = build1 (GOTO_EXPR
, void_type_node
, TREE_VEC_ELT (cases
, 0));
184 append_to_statement_list (default_jump
, &SWITCH_BODY (switch_expr
));
186 for (int c
= 0; c
< TREE_VEC_LENGTH (cases
); ++c
)
189 = build_case_label (build_int_cst (integer_type_node
, c
), NULL_TREE
,
190 create_artificial_label (UNKNOWN_LOCATION
));
192 append_to_statement_list (case_label
, &SWITCH_BODY (switch_expr
));
195 = build1 (GOTO_EXPR
, void_type_node
, TREE_VEC_ELT (cases
, c
));
196 append_to_statement_list (jump
, &SWITCH_BODY (switch_expr
));
198 m_parent
.m_cf
->append_statement (switch_expr
);
200 else if (brig_inst
->opcode
== BRIG_OPCODE_CBR
)
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). */
206 = build3 (COND_EXPR
, void_type_node
, condition
, target_goto
, NULL_TREE
);
207 m_parent
.m_cf
->append_statement (if_stmt
);
209 else if (brig_inst
->opcode
== BRIG_OPCODE_WAVEBARRIER
)
211 /* WAVEBARRIER is a NOP when WAVESIZE = 1. */
213 else if (brig_inst
->opcode
== BRIG_OPCODE_BARRIER
)
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
,
225 else if (brig_inst
->opcode
>= BRIG_OPCODE_ARRIVEFBAR
226 && brig_inst
->opcode
<= BRIG_OPCODE_WAITFBAR
)
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
,
232 uint32_type_node
, operands
));
236 m_parent
.m_cf
->start_new_bb ();
237 return base
->byteCount
;