1/* brig-code-entry-handler.cc -- brig function directive 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 <sstream> 23#include <iomanip> 24 25#include "brig-code-entry-handler.h" 26 27#include "brig-machine.h" 28#include "stringpool.h" 29#include "tree-iterator.h" 30#include "gimple-expr.h" 31#include "function.h" 32#include "phsa.h" 33 34#include "tree-pretty-print.h" 35#include "print-tree.h" 36 37extern int gccbrig_verbose; 38 39size_t 40brig_directive_function_handler::operator () (const BrigBase *base) 41{ 42 if (!m_parent.m_analyzing) 43 m_parent.finish_function (); 44 45 size_t bytes_consumed = base->byteCount; 46 47 const BrigDirectiveExecutable *exec = (const BrigDirectiveExecutable *) base; 48 49 if (gccbrig_verbose) 50 { 51 printf ("brig: function name %s\n", 52 m_parent.get_string (exec->name).c_str()); 53 printf ("brig: inargs %d outargs %d name offset %d\n", exec->inArgCount, 54 exec->outArgCount, exec->name); 55 } 56 57 const bool is_definition 58 = exec->modifier & BRIG_EXECUTABLE_DEFINITION; 59 60 const bool is_kernel = base->kind == BRIG_KIND_DIRECTIVE_KERNEL; 61 62 /* There doesn't seem to be actual use cases for kernel declarations 63 as they cannot be called by the program. Ignore them until there's 64 a reason not to. */ 65 if (is_kernel && !is_definition) 66 return bytes_consumed; 67 68 std::string func_name = m_parent.get_mangled_name (exec); 69 if (is_kernel) 70 /* The generated kernel function is not the one that should be 71 called by the host. */ 72 func_name = std::string ("_") + func_name; 73 74 m_parent.m_cf = new brig_function (exec, &m_parent); 75 m_parent.m_cf->m_name = func_name; 76 m_parent.m_cf->m_is_kernel = is_kernel; 77 78 /* During the analyze step, the above information is all we need per 79 function. */ 80 if (m_parent.m_analyzing) 81 return bytes_consumed; 82 83 /* There can be multiple forward declarations of the same function. 84 Skip all but the first one. */ 85 if (!is_definition && m_parent.function_decl (func_name) != NULL_TREE) 86 return bytes_consumed; 87 tree fndecl; 88 tree ret_value = NULL_TREE; 89 90 tree stmt_list = alloc_stmt_list (); 91 92 /* Add a function scope BIND_EXPR using which we can push local variables that 93 represent HSAIL registers. */ 94 tree bind_expr = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, NULL); 95 96 tree restrict_char_ptr 97 = build_qualified_type (build_pointer_type (char_type_node), 98 TYPE_QUAL_RESTRICT); 99 tree restrict_void_ptr 100 = build_qualified_type (build_pointer_type (void_type_node), 101 TYPE_QUAL_RESTRICT); 102 103 tree restrict_const_char_ptr 104 = build_qualified_type (build_pointer_type 105 (build_qualified_type (char_type_node, 106 TYPE_QUAL_CONST)), 107 TYPE_QUAL_RESTRICT); 108 109 tree restrict_const_void_ptr 110 = build_qualified_type (build_pointer_type 111 (build_qualified_type (void_type_node, 112 TYPE_QUAL_CONST)), 113 TYPE_QUAL_RESTRICT); 114 115 if (is_kernel) 116 { 117 tree name_identifier 118 = get_identifier_with_length (func_name.c_str (), func_name.size ()); 119 120 /* The generated kernel functions take the following arguments: 121 122 1) a char* which is a starting address of the argument segment where 123 the call's arguments are stored by the launcher. 124 2) a void* parameter that points to a phsail-finalizer context object 125 which passes the hsa kernel packet etc. 126 3) a void* parameter that contains the first flat address of the group 127 region allocated to the current work-group. */ 128 129 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier, 130 build_function_type_list (void_type_node, 131 restrict_const_char_ptr, 132 restrict_void_ptr, 133 restrict_char_ptr, NULL_TREE)); 134 135 SET_DECL_ASSEMBLER_NAME (fndecl, name_identifier); 136 137 tree resdecl 138 = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node); 139 140 tree typelist = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); 141 tree argtype = TREE_VALUE (typelist); 142 TYPE_ADDR_SPACE (argtype) 143 = gccbrig_get_target_addr_space_id (BRIG_SEGMENT_KERNARG); 144 145 tree arg_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL, 146 get_identifier ("__args"), 147 restrict_const_char_ptr); 148 DECL_ARGUMENTS (fndecl) = arg_arg; 149 DECL_ARG_TYPE (arg_arg) = restrict_const_char_ptr; 150 DECL_CONTEXT (arg_arg) = fndecl; 151 DECL_ARTIFICIAL (arg_arg) = 1; 152 TREE_READONLY (arg_arg) = 1; 153 TREE_USED (arg_arg) = 1; 154 155 DECL_RESULT (fndecl) = resdecl; 156 DECL_CONTEXT (resdecl) = fndecl; 157 DECL_EXTERNAL (fndecl) = 0; 158 159 /* Aggressive inlining to the kernel function is usually a good 160 idea with offlined functionality to enchance SIMD execution on 161 GPUs and vector units. */ 162 163 DECL_ATTRIBUTES (fndecl) 164 = tree_cons (get_identifier ("flatten"), NULL, 165 DECL_ATTRIBUTES (fndecl)); 166 } 167 else 168 { 169 /* Build a regular function fingerprint to enable targets to optimize 170 the calling convention as they see fit. */ 171 tree name_identifier 172 = get_identifier_with_length (func_name.c_str (), func_name.size ()); 173 174 m_parent.m_cf->m_arg_variables.clear (); 175 176 brig_directive_variable_handler arg_handler (m_parent); 177 178 vec<tree, va_gc> *args; 179 vec_alloc (args, 4); 180 181 tree arg_decls = NULL_TREE; 182 183 tree ret_type = void_type_node; 184 if (exec->outArgCount == 1) 185 { 186 /* The return value variable should be the first entry after the 187 function directive. */ 188 const BrigBase *retval 189 = (const BrigBase *) ((const char *) base + base->byteCount); 190 gcc_assert (retval->kind == BRIG_KIND_DIRECTIVE_VARIABLE); 191 192 const BrigDirectiveVariable *brigVar 193 = (const BrigDirectiveVariable *) retval; 194 195 brig_directive_variable_handler varhandler (m_parent); 196 197 if (brigVar->type & BRIG_TYPE_ARRAY) 198 { 199 /* Push array output arguments to the beginning of the 200 function argument list instead of regular function 201 return values. */ 202 203 tree arg_var = varhandler.build_variable (brigVar, PARM_DECL); 204 vec_safe_push (args, TREE_TYPE (arg_var)); 205 206 m_parent.m_cf->add_arg_variable (brigVar, arg_var); 207 208 if (arg_decls == NULL_TREE) 209 arg_decls = arg_var; 210 else 211 arg_decls = chainon (arg_decls, arg_var); 212 213 m_parent.m_cf->add_arg_variable (brigVar, arg_var); 214 215 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, 216 void_type_node); 217 } 218 else 219 { 220 ret_value = varhandler.build_variable (brigVar, RESULT_DECL); 221 m_parent.m_cf->m_ret_value = ret_value; 222 ret_type = TREE_TYPE (ret_value); 223 m_parent.m_cf->m_ret_value_brig_var = brigVar; 224 } 225 bytes_consumed += retval->byteCount; 226 } 227 else 228 ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, 229 void_type_node); 230 231 TREE_ADDRESSABLE (ret_value) = 1; 232 233 if (exec->inArgCount > 0) 234 { 235 uint32_t arg_offset = exec->firstInArg; 236 for (size_t arg = 0; arg < exec->inArgCount; ++arg) 237 { 238 239 const BrigDirectiveVariable *brigVar 240 = (const BrigDirectiveVariable *) m_parent.get_brig_code_entry 241 (arg_offset); 242 243 gcc_assert (brigVar->base.kind == BRIG_KIND_DIRECTIVE_VARIABLE); 244 245 /* Delegate to the brig_directive_variable_handler. */ 246 brig_directive_variable_handler varhandler (m_parent); 247 tree arg_var = varhandler.build_variable (brigVar, PARM_DECL); 248 arg_offset += brigVar->base.byteCount; 249 vec_safe_push (args, TREE_TYPE (arg_var)); 250 251 m_parent.m_cf->add_arg_variable (brigVar, arg_var); 252 arg_decls = chainon (arg_decls, arg_var); 253 } 254 } 255 vec_safe_push (args, restrict_void_ptr); 256 vec_safe_push (args, restrict_char_ptr); 257 vec_safe_push (args, uint32_type_node); 258 vec_safe_push (args, restrict_char_ptr); 259 260 fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier, 261 build_function_type_vec (ret_type, args)); 262 263 DECL_RESULT (fndecl) = ret_value; 264 DECL_CONTEXT (ret_value) = fndecl; 265 DECL_EXTERNAL (fndecl) = 0; 266 DECL_ARGUMENTS (fndecl) = arg_decls; 267 } 268 269 /* All functions need the hidden __context argument passed on 270 because they might call WI-specific functions which need 271 the context info. Only kernels can write it, if they need 272 to update the local ids in the work-item loop. */ 273 274 tree context_arg_type 275 = true ? restrict_void_ptr : restrict_const_void_ptr; 276 tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL, 277 get_identifier ("__context"), 278 context_arg_type); 279 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), context_arg); 280 DECL_CONTEXT (context_arg) = fndecl; 281 DECL_ARG_TYPE (context_arg) = context_arg_type; 282 DECL_ARTIFICIAL (context_arg) = 1; 283 TREE_READONLY (context_arg) = 1; 284 TREE_USED (context_arg) = 1; 285 m_parent.m_cf->m_context_arg = context_arg; 286 287 /* They can also access group memory, so we need to pass the 288 group pointer along too. */ 289 tree group_base_arg 290 = build_decl (UNKNOWN_LOCATION, PARM_DECL, 291 get_identifier ("__group_base_addr"), 292 restrict_char_ptr); 293 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_base_arg); 294 DECL_ARG_TYPE (group_base_arg) = restrict_char_ptr; 295 DECL_CONTEXT (group_base_arg) = fndecl; 296 DECL_ARTIFICIAL (group_base_arg) = 1; 297 TREE_READONLY (group_base_arg) = 1; 298 TREE_USED (group_base_arg) = 1; 299 m_parent.m_cf->m_group_base_arg = group_base_arg; 300 301 /* To implement call stack and (non-kernel) function scope group variables, 302 we need to pass an offset which describes how far are we from 303 group_base_ptr. 304 That must be substracted from any function local group variable offsets to 305 get the address related to the bottom of the group memory chunk. */ 306 tree group_local_offset_arg 307 = build_decl (UNKNOWN_LOCATION, PARM_DECL, 308 get_identifier ("__group_local_offset"), uint32_type_node); 309 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg); 310 DECL_ARG_TYPE (group_local_offset_arg) = uint32_type_node; 311 DECL_CONTEXT (group_local_offset_arg) = fndecl; 312 DECL_ARTIFICIAL (group_local_offset_arg) = 1; 313 TREE_READONLY (group_local_offset_arg) = 1; 314 TREE_USED (group_local_offset_arg) = 1; 315 m_parent.m_cf->m_group_local_offset_arg = group_local_offset_arg; 316 317 /* Same for private. */ 318 tree private_base_arg 319 = build_decl (UNKNOWN_LOCATION, PARM_DECL, 320 get_identifier ("__private_base_addr"), restrict_char_ptr); 321 DECL_ARGUMENTS (fndecl) = chainon (DECL_ARGUMENTS (fndecl), private_base_arg); 322 DECL_ARG_TYPE (private_base_arg) = restrict_char_ptr; 323 DECL_CONTEXT (private_base_arg) = fndecl; 324 DECL_ARTIFICIAL (private_base_arg) = 1; 325 TREE_READONLY (private_base_arg) = 1; 326 TREE_USED (private_base_arg) = 1; 327 m_parent.m_cf->m_private_base_arg = private_base_arg; 328 329 DECL_SAVED_TREE (fndecl) = bind_expr; 330 331 if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION) 332 { 333 TREE_STATIC (fndecl) = 0; 334 TREE_PUBLIC (fndecl) = 1; 335 DECL_EXTERNAL (fndecl) = 0; 336 DECL_DECLARED_INLINE_P (fndecl) = 1; 337 set_inline (fndecl); 338 set_externally_visible (fndecl); 339 } 340 else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL) 341 { 342 TREE_STATIC (fndecl) = 0; 343 TREE_PUBLIC (fndecl) = 1; 344 DECL_EXTERNAL (fndecl) = 0; 345 set_externally_visible (fndecl); 346 } 347 else if (base->kind == BRIG_KIND_DIRECTIVE_SIGNATURE) 348 { 349 TREE_STATIC (fndecl) = 0; 350 TREE_PUBLIC (fndecl) = 1; 351 DECL_EXTERNAL (fndecl) = 1; 352 set_inline (fndecl); 353 } 354 else if (base->kind == BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION) 355 { 356 TREE_STATIC (fndecl) = 0; 357 TREE_PUBLIC (fndecl) = 1; 358 } 359 else 360 gcc_unreachable (); 361 362 TREE_USED (fndecl) = 1; 363 DECL_ARTIFICIAL (fndecl) = 0; 364 365 tree initial_block = make_node (BLOCK); 366 DECL_INITIAL (fndecl) = initial_block; 367 TREE_USED (DECL_INITIAL (fndecl)) = 1; 368 369 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node) 370 { 371 DECL_CONTEXT (ret_value) = fndecl; 372 DECL_CHAIN (ret_value) = BIND_EXPR_VARS (bind_expr); 373 BIND_EXPR_VARS (bind_expr) = ret_value; 374 } 375 376 tree arg; 377 for (arg = DECL_ARGUMENTS (fndecl); arg != NULL_TREE; arg = TREE_CHAIN (arg)) 378 { 379 DECL_CONTEXT (arg) = fndecl; 380 DECL_ARG_TYPE (arg) = TREE_TYPE (arg); 381 } 382 383 m_parent.add_function_decl (func_name, fndecl); 384 m_parent.append_global (fndecl); 385 386 387 if (!is_definition) 388 { 389 DECL_EXTERNAL (fndecl) = 1; 390 return bytes_consumed; 391 } 392 393 m_parent.start_function (fndecl); 394 m_parent.m_cf->m_func_decl = fndecl; 395 m_parent.m_cf->m_current_bind_expr = bind_expr; 396 397 if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node) 398 { 399 /* We cannot assign to <<retval>> directly in gcc trunk. We need to 400 create a local temporary variable which can be stored to and when 401 returning from the function, we'll copy it to the actual <<retval>> 402 in return statement's argument. */ 403 tree temp_var = m_parent.m_cf->m_ret_temp 404 = m_parent.m_cf->add_local_variable ("_retvalue_temp", 405 TREE_TYPE (ret_value)); 406 TREE_ADDRESSABLE (temp_var) = 1; 407 } 408 409 if (is_kernel) 410 { 411 m_parent.m_cf->add_id_variables (); 412 413 /* Create a single entry point in the function. */ 414 m_parent.m_cf->m_entry_label_stmt 415 = build_stmt (LABEL_EXPR, m_parent.m_cf->label ("__kernel_entry")); 416 m_parent.m_cf->append_statement (m_parent.m_cf->m_entry_label_stmt); 417 418 tree bind_expr = m_parent.m_cf->m_current_bind_expr; 419 tree stmts = BIND_EXPR_BODY (bind_expr); 420 421 m_parent.m_cf->m_kernel_entry = tsi_last (stmts); 422 423 /* Let's not append the exit label yet, but only after the 424 function has been built. We need to build it so it can 425 be referred to because returns are converted to gotos to this 426 label. */ 427 m_parent.m_cf->m_exit_label = m_parent.m_cf->label ("__kernel_exit"); 428 } 429 430 return bytes_consumed; 431} 432