xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/brig/brigfrontend/brig-branch-inst-handler.cc (revision d90047b5d07facf36e6c01dcc0bded8997ce9cc2)
1 /* brig-branch-inst-handler.cc -- brig branch instruction handling
2    Copyright (C) 2016-2017 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
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       vec_alloc (in_args, 4);
46 
47       size_t operand_count = operand_entries->byteCount / 4;
48       gcc_assert (operand_count < 4);
49 
50       for (size_t i = 0; i < operand_count; ++i)
51 	{
52 	  uint32_t operand_offset
53 	    = ((const uint32_t *) &operand_entries->bytes)[i];
54 	  const BrigBase *operand_data
55 	    = m_parent.get_brig_operand_entry (operand_offset);
56 	  if (i == 1)
57 	    {
58 	      gcc_assert (operand_data->kind == BRIG_KIND_OPERAND_CODE_REF);
59 	      func_ref = build_tree_operand (*brig_inst, *operand_data);
60 	      continue;
61 	    }
62 	  gcc_assert (operand_data->kind == BRIG_KIND_OPERAND_CODE_LIST);
63 	  const BrigOperandCodeList *codelist
64 	    = (const BrigOperandCodeList *) operand_data;
65 	  const BrigData *data
66 	    = m_parent.get_brig_data_entry (codelist->elements);
67 
68 	  size_t bytes = data->byteCount;
69 	  const BrigOperandOffset32_t *operand_ptr
70 	    = (const BrigOperandOffset32_t *) data->bytes;
71 
72 	  vec<tree, va_gc> *args = i == 0 ? out_args : in_args;
73 
74 	  while (bytes > 0)
75 	    {
76 	      BrigOperandOffset32_t offset = *operand_ptr;
77 	      const BrigBase *code_element
78 		= m_parent.get_brig_code_entry (offset);
79 	      gcc_assert (code_element->kind == BRIG_KIND_DIRECTIVE_VARIABLE);
80 	      const BrigDirectiveVariable *brig_var
81 		= (const BrigDirectiveVariable *) code_element;
82 	      tree var = m_parent.m_cf->arg_variable (brig_var);
83 
84 	      if (brig_var->type & BRIG_TYPE_ARRAY)
85 		{
86 		  /* Array return values are passed as the first argument.  */
87 		  args = in_args;
88 		  /* Pass pointer to the element zero and use its element zero
89 		     as the base address.  */
90 		  tree etype = TREE_TYPE (TREE_TYPE (var));
91 		  tree ptype = build_pointer_type (etype);
92 		  tree element_zero
93 		    = build4 (ARRAY_REF, etype, var, integer_zero_node,
94 			      NULL_TREE, NULL_TREE);
95 		  var = build1 (ADDR_EXPR, ptype, element_zero);
96 		}
97 
98 	      gcc_assert (var != NULL_TREE);
99 	      vec_safe_push (args, var);
100 	      ++operand_ptr;
101 	      bytes -= 4;
102 	    }
103 	}
104 
105       gcc_assert (func_ref != NULL_TREE);
106       gcc_assert (out_args->length () == 0 || out_args->length () == 1);
107 
108       tree ret_val_type = void_type_node;
109       tree ret_val = NULL_TREE;
110       if (out_args->length () == 1)
111 	{
112 	  ret_val = (*out_args)[0];
113 	  ret_val_type = TREE_TYPE (ret_val);
114 	}
115 
116       /* Pass the hidden kernel arguments along to the called functions as
117 	 they might call builtins that need them or access group/private
118 	 memory.  */
119 
120       vec_safe_push (in_args, m_parent.m_cf->m_context_arg);
121       vec_safe_push (in_args, m_parent.m_cf->m_group_base_arg);
122       vec_safe_push (in_args, m_parent.m_cf->m_private_base_arg);
123 
124       tree call = build_call_vec (ret_val_type, build_fold_addr_expr (func_ref),
125 				  in_args);
126       TREE_NOTHROW (func_ref) = 1;
127       TREE_NOTHROW (call) = 1;
128 
129       if (ret_val != NULL_TREE)
130 	{
131 	  TREE_ADDRESSABLE (ret_val) = 1;
132 	  tree result_assign
133 	    = build2 (MODIFY_EXPR, TREE_TYPE (ret_val), ret_val, call);
134 	  m_parent.m_cf->append_statement (result_assign);
135 	}
136       else
137 	{
138 	  m_parent.m_cf->append_statement (call);
139 	}
140 
141       m_parent.m_cf->m_has_unexpanded_dp_builtins = false;
142       m_parent.m_cf->m_called_functions.push_back (func_ref);
143 
144       return base->byteCount;
145     }
146 
147   tree instr_type = gccbrig_tree_type_for_hsa_type (brig_inst->type);
148   tree_stl_vec operands = build_operands (*brig_inst);
149 
150   if (brig_inst->opcode == BRIG_OPCODE_BR)
151     {
152       tree goto_stmt = build1 (GOTO_EXPR, instr_type, operands[0]);
153       m_parent.m_cf->append_statement (goto_stmt);
154     }
155   else if (brig_inst->opcode == BRIG_OPCODE_SBR)
156     {
157       tree select = operands[0];
158       tree cases = operands[1];
159 
160       tree switch_expr = build3 (SWITCH_EXPR, TREE_TYPE (select), select,
161 				 NULL_TREE, NULL_TREE);
162 
163       tree default_case
164 	= build_case_label (NULL_TREE, NULL_TREE,
165 			    create_artificial_label (UNKNOWN_LOCATION));
166       append_to_statement_list (default_case, &SWITCH_BODY (switch_expr));
167 
168       tree default_jump
169 	= build1 (GOTO_EXPR, void_type_node, TREE_VEC_ELT (cases, 0));
170       append_to_statement_list (default_jump, &SWITCH_BODY (switch_expr));
171 
172       for (int c = 0; c < TREE_VEC_LENGTH (cases); ++c)
173 	{
174 	  tree case_label
175 	    = build_case_label (build_int_cst (integer_type_node, c), NULL_TREE,
176 				create_artificial_label (UNKNOWN_LOCATION));
177 
178 	  append_to_statement_list (case_label, &SWITCH_BODY (switch_expr));
179 
180 	  tree jump
181 	    = build1 (GOTO_EXPR, void_type_node, TREE_VEC_ELT (cases, c));
182 	  append_to_statement_list (jump, &SWITCH_BODY (switch_expr));
183 	}
184       m_parent.m_cf->append_statement (switch_expr);
185     }
186   else if (brig_inst->opcode == BRIG_OPCODE_CBR)
187     {
188       tree condition = operands[0];
189       tree target_goto = build1 (GOTO_EXPR, void_type_node, operands[1]);
190       /* Represents the if..else as (condition)?(goto foo):(goto bar).  */
191       tree if_stmt
192 	= build3 (COND_EXPR, void_type_node, condition, target_goto, NULL_TREE);
193       m_parent.m_cf->append_statement (if_stmt);
194     }
195   else if (brig_inst->opcode == BRIG_OPCODE_WAVEBARRIER)
196     {
197       /* WAVEBARRIER is a NOP when WAVESIZE = 1.  */
198     }
199   else if (brig_inst->opcode == BRIG_OPCODE_BARRIER)
200     {
201       m_parent.m_cf->m_has_barriers = true;
202       tree_stl_vec call_operands;
203       /* FIXME.  We should add attributes (are there suitable ones in gcc?) that
204 	 ensure the barrier won't be duplicated or moved out of loops etc.
205 	 Like the 'noduplicate' of LLVM.  Same goes for fbarriers.  */
206       m_parent.m_cf->append_statement
207 	(expand_or_call_builtin (brig_inst->opcode, BRIG_TYPE_NONE, NULL_TREE,
208 				 call_operands));
209     }
210   else if (brig_inst->opcode >= BRIG_OPCODE_ARRIVEFBAR
211 	   && brig_inst->opcode <= BRIG_OPCODE_WAITFBAR)
212     {
213       m_parent.m_cf->m_has_barriers = true;
214       m_parent.m_cf->append_statement
215 	(expand_or_call_builtin (brig_inst->opcode, BRIG_TYPE_NONE,
216 				 uint32_type_node, operands));
217     }
218   else
219     gcc_unreachable ();
220   return base->byteCount;
221 }
222