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