xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/brig/brigfrontend/brig-branch-inst-handler.cc (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
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
operator ()(const BrigBase * base)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 }
239