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
31size_t
32brig_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