1 /* brig-atomic-inst-handler.cc -- brig atomic instruction handling 2 Copyright (C) 2016-2020 Free Software Foundation, Inc. 3 4 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com> 5 for General Processor Tech. 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 <sstream> 23 24 #include "brig-code-entry-handler.h" 25 #include "brig-util.h" 26 #include "fold-const.h" 27 #include "diagnostic.h" 28 #include "tree-pretty-print.h" 29 #include "print-tree.h" 30 #include "convert.h" 31 #include "langhooks.h" 32 #include "gimple-expr.h" 33 #include "stringpool.h" 34 #include "brig-builtins.h" 35 36 brig_atomic_inst_handler::brig_atomic_inst_handler (brig_to_generic &parent) 37 : brig_code_entry_handler (parent) 38 { 39 } 40 41 size_t 42 brig_atomic_inst_handler::generate_tree (const BrigInstBase &inst, 43 BrigAtomicOperation8_t atomic_opcode) 44 { 45 tree_stl_vec operands = build_operands (inst); 46 const int first_input 47 = gccbrig_hsa_opcode_op_output_p (inst.opcode, 0) ? 1 : 0; 48 49 tree instr_type = gccbrig_tree_type_for_hsa_type (inst.type); 50 51 /* Utilize the atomic data types (from C++11 support) for implementing 52 atomic operations. */ 53 54 tree atomic_type = build_qualified_type (instr_type, TYPE_QUAL_ATOMIC); 55 56 gcc_assert (atomic_type != NULL_TREE); 57 58 tree signal_handle = operands[first_input]; 59 tree atomic_ptype = build_pointer_type (atomic_type); 60 tree casted_to_ptr = convert_to_pointer (atomic_ptype, signal_handle); 61 62 tree src0 = NULL_TREE; 63 if (atomic_opcode != BRIG_ATOMIC_LD) 64 src0 = operands[first_input + 1]; 65 66 tree instr_expr = NULL_TREE; 67 68 tree ptype = build_pointer_type (instr_type); 69 tree ptr = convert_to_pointer (ptype, operands[first_input]); 70 71 if (atomic_opcode == BRIG_ATOMIC_ST) 72 { 73 tree mem_ref = build2 (MEM_REF, atomic_type, casted_to_ptr, 74 build_int_cst (atomic_ptype, 0)); 75 instr_expr = build2 (MODIFY_EXPR, atomic_type, mem_ref, src0); 76 } 77 else if (atomic_opcode == BRIG_ATOMIC_LD 78 || (atomic_opcode >= BRIG_ATOMIC_WAIT_EQ 79 && atomic_opcode <= BRIG_ATOMIC_WAITTIMEOUT_GTE)) 80 { 81 tree mem_ref = build2 (MEM_REF, atomic_type, casted_to_ptr, 82 build_int_cst (atomic_ptype, 0)); 83 /* signal_wait* instructions can return spuriously before the 84 condition becomes true. Therefore it's legal to return 85 right away. TODO: builtin calls which can be 86 implemented with a power efficient sleep-wait. */ 87 instr_expr = mem_ref; 88 } 89 else if (atomic_opcode == BRIG_ATOMIC_CAS) 90 { 91 /* Special case for CAS due to the two args. */ 92 tree built_in = NULL_TREE; 93 switch (gccbrig_hsa_type_bit_size (inst.type)) 94 { 95 case 32: 96 built_in 97 = builtin_decl_explicit (BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_4); 98 break; 99 case 64: 100 built_in 101 = builtin_decl_explicit (BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_8); 102 break; 103 default: 104 gcc_unreachable (); 105 } 106 107 tree src1 = operands[first_input + 2]; 108 109 tree src0_type 110 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (built_in)))); 111 112 tree src1_type = TREE_VALUE 113 (TREE_CHAIN (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (built_in))))); 114 115 instr_expr = call_builtin (built_in, 3, instr_type, ptype, ptr, 116 src0_type, src0, src1_type, src1); 117 } 118 else 119 { 120 tree built_in = NULL_TREE; 121 /* The rest of the builtins have the same number of parameters. 122 Generate a big if..else that finds the correct builtin 123 automagically from the def file. */ 124 #undef DEF_HSAIL_SAT_BUILTIN 125 #undef DEF_HSAIL_BUILTIN 126 #undef DEF_HSAIL_ATOMIC_BUILTIN 127 #undef DEF_HSAIL_INTR_BUILTIN 128 #undef DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN 129 130 #define DEF_HSAIL_ATOMIC_BUILTIN(ENUM, ATOMIC_OPCODE, HSAIL_TYPE, \ 131 NAME, TYPE, ATTRS) \ 132 if (atomic_opcode == ATOMIC_OPCODE && inst.type == HSAIL_TYPE) \ 133 built_in = builtin_decl_explicit (ENUM); \ 134 else 135 #include "brig-builtins.def" 136 switch (atomic_opcode) 137 { 138 case BRIG_ATOMIC_ADD: 139 switch (gccbrig_hsa_type_bit_size (inst.type)) 140 { 141 case 32: 142 built_in 143 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_ADD_4); 144 break; 145 case 64: 146 built_in 147 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_ADD_8); 148 break; 149 default: 150 gcc_unreachable (); 151 } 152 break; 153 case BRIG_ATOMIC_SUB: 154 switch (gccbrig_hsa_type_bit_size (inst.type)) 155 { 156 case 32: 157 built_in 158 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_SUB_4); 159 break; 160 case 64: 161 built_in 162 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_SUB_8); 163 break; 164 default: 165 gcc_unreachable (); 166 } 167 break; 168 case BRIG_ATOMIC_AND: 169 switch (gccbrig_hsa_type_bit_size (inst.type)) 170 { 171 case 32: 172 built_in 173 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_AND_4); 174 break; 175 case 64: 176 built_in 177 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_AND_8); 178 break; 179 default: 180 gcc_unreachable (); 181 } 182 break; 183 case BRIG_ATOMIC_XOR: 184 switch (gccbrig_hsa_type_bit_size (inst.type)) 185 { 186 case 32: 187 built_in 188 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_XOR_4); 189 break; 190 case 64: 191 built_in 192 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_XOR_8); 193 break; 194 default: 195 gcc_unreachable (); 196 } 197 break; 198 case BRIG_ATOMIC_OR: 199 switch (gccbrig_hsa_type_bit_size (inst.type)) 200 { 201 case 32: 202 built_in 203 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_OR_4); 204 break; 205 case 64: 206 built_in 207 = builtin_decl_explicit (BUILT_IN_SYNC_FETCH_AND_OR_8); 208 break; 209 default: 210 gcc_unreachable (); 211 } 212 break; 213 case BRIG_ATOMIC_EXCH: 214 switch (gccbrig_hsa_type_bit_size (inst.type)) 215 { 216 case 32: 217 built_in 218 = builtin_decl_explicit (BUILT_IN_SYNC_LOCK_TEST_AND_SET_4); 219 break; 220 case 64: 221 built_in 222 = builtin_decl_explicit (BUILT_IN_SYNC_LOCK_TEST_AND_SET_8); 223 break; 224 default: 225 gcc_unreachable (); 226 } 227 break; 228 default: 229 gcc_unreachable (); 230 }; 231 232 gcc_assert (built_in != NULL_TREE); 233 tree arg0_type 234 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (built_in)))); 235 236 instr_expr = call_builtin (built_in, 2, instr_type, ptr_type_node, 237 ptr, arg0_type, src0); 238 239 /* We need a temp variable for the result, because otherwise 240 the gimplifier drops a necessary (unsigned to signed) cast in 241 the output assignment and fails a check later. */ 242 tree tmp_var = create_tmp_var (arg0_type, "builtin_out"); 243 tree tmp_assign 244 = build2 (MODIFY_EXPR, TREE_TYPE (tmp_var), tmp_var, instr_expr); 245 m_parent.m_cf->append_statement (tmp_assign); 246 instr_expr = tmp_var; 247 } 248 249 if (first_input > 0) 250 build_output_assignment (inst, operands[0], instr_expr); 251 else 252 m_parent.m_cf->append_statement (instr_expr); 253 254 return inst.base.byteCount; 255 } 256 257 size_t 258 brig_atomic_inst_handler::operator () (const BrigBase *base) 259 { 260 const BrigInstAtomic *inst = (const BrigInstAtomic *) base; 261 BrigAtomicOperation8_t atomic_opcode; 262 atomic_opcode = inst->atomicOperation; 263 264 return generate_tree (inst->base, atomic_opcode); 265 } 266