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