diff gcc/brig/brigfrontend/brig-function-handler.cc @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/brig/brigfrontend/brig-function-handler.cc	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,396 @@
+/* brig-code-entry-handler.cc -- brig function directive handling
+   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+   Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
+   for General Processor Tech.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sstream>
+#include <iomanip>
+
+#include "brig-code-entry-handler.h"
+
+#include "brig-machine.h"
+#include "stringpool.h"
+#include "tree-iterator.h"
+#include "gimple-expr.h"
+#include "function.h"
+#include "phsa.h"
+
+#include "tree-pretty-print.h"
+#include "print-tree.h"
+
+extern int gccbrig_verbose;
+
+size_t
+brig_directive_function_handler::operator () (const BrigBase *base)
+{
+  if (!m_parent.m_analyzing)
+    m_parent.finish_function ();
+
+  size_t bytes_consumed = base->byteCount;
+
+  const BrigDirectiveExecutable *exec = (const BrigDirectiveExecutable *) base;
+
+  if (gccbrig_verbose)
+    {
+      printf ("brig: function name %s\n",
+	      m_parent.get_string (exec->name).c_str());
+      printf ("brig: inargs %d outargs %d name offset %d\n", exec->inArgCount,
+	      exec->outArgCount, exec->name);
+    }
+
+  const bool is_definition
+    = exec->modifier & BRIG_EXECUTABLE_DEFINITION;
+
+  const bool is_kernel = base->kind == BRIG_KIND_DIRECTIVE_KERNEL;
+
+  /* There doesn't seem to be actual use cases for kernel declarations
+     as they cannot be called by the program.  Ignore them until there's
+     a reason not to.  */
+  if (is_kernel && !is_definition)
+    return bytes_consumed;
+
+  std::string func_name = m_parent.get_mangled_name (exec);
+  if (is_kernel)
+    /* The generated kernel function is not the one that should be
+       called by the host.  */
+    func_name = std::string ("_") + func_name;
+
+  m_parent.m_cf = new brig_function (exec, &m_parent);
+  m_parent.m_cf->m_name = func_name;
+  m_parent.m_cf->m_is_kernel = is_kernel;
+
+  /* During the analyze step, the above information is all we need per
+     function.  */
+  if (m_parent.m_analyzing)
+    return bytes_consumed;
+
+  tree fndecl;
+  tree ret_value = NULL_TREE;
+
+  tree stmt_list = alloc_stmt_list ();
+
+  /* Add a function scope BIND_EXPR using which we can push local variables that
+     represent HSAIL registers.  */
+  tree bind_expr = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, NULL);
+
+  if (is_kernel)
+    {
+      tree name_identifier
+	= get_identifier_with_length (func_name.c_str (), func_name.size ());
+
+      /* The generated kernel functions take the following arguments:
+
+	 1) a char* which is a starting address of the argument segment where
+	 the call's arguments are stored by the launcher.
+	 2) a void* parameter that points to a phsail-finalizer context object
+	 which passes the hsa kernel packet etc.
+	 3) a void* parameter that contains the first flat address of the group
+	 region allocated to the current work-group.  */
+
+      tree char_ptr_type_node = build_pointer_type (char_type_node);
+      fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
+			   build_function_type_list (void_type_node,
+						     char_ptr_type_node,
+						     ptr_type_node,
+						     ptr_type_node, NULL_TREE));
+
+      SET_DECL_ASSEMBLER_NAME (fndecl, name_identifier);
+
+      tree resdecl
+	= build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, void_type_node);
+
+      tree typelist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+      tree argtype = TREE_VALUE (typelist);
+      TYPE_ADDR_SPACE (argtype)
+	= gccbrig_get_target_addr_space_id (BRIG_SEGMENT_KERNARG);
+
+      tree arg_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+				 get_identifier ("__args"), char_ptr_type_node);
+      DECL_ARGUMENTS (fndecl) = arg_arg;
+      DECL_ARG_TYPE (arg_arg) = char_ptr_type_node;
+      DECL_CONTEXT (arg_arg) = fndecl;
+      DECL_ARTIFICIAL (arg_arg) = 1;
+      TREE_READONLY (arg_arg) = 1;
+      TREE_USED (arg_arg) = 1;
+
+      DECL_RESULT (fndecl) = resdecl;
+      DECL_CONTEXT (resdecl) = fndecl;
+      DECL_EXTERNAL (fndecl) = 0;
+    }
+  else
+    {
+      /* Build a regular function fingerprint to enable targets to optimize
+	 the calling convention as they see fit.  */
+      tree name_identifier
+	= get_identifier_with_length (func_name.c_str (), func_name.size ());
+
+      m_parent.m_cf->m_arg_variables.clear ();
+
+      brig_directive_variable_handler arg_handler (m_parent);
+
+      vec<tree, va_gc> *args;
+      vec_alloc (args, 4);
+
+      tree arg_decls = NULL_TREE;
+
+      tree ret_type = void_type_node;
+      if (exec->outArgCount == 1)
+	{
+	  /* The return value variable should be the first entry after the
+	     function directive.  */
+	  const BrigBase *retval
+	    = (const BrigBase *) ((const char *) base + base->byteCount);
+	  gcc_assert (retval->kind == BRIG_KIND_DIRECTIVE_VARIABLE);
+
+	  const BrigDirectiveVariable *brigVar
+	    = (const BrigDirectiveVariable *) retval;
+
+	  brig_directive_variable_handler varhandler (m_parent);
+
+	  if (brigVar->type & BRIG_TYPE_ARRAY)
+	    {
+	      /* Push array output arguments to the beginning of the
+		 function argument list instead of regular function
+		 return values.  */
+
+	      tree arg_var = varhandler.build_variable (brigVar, PARM_DECL);
+	      vec_safe_push (args, TREE_TYPE (arg_var));
+
+	      m_parent.m_cf->add_arg_variable (brigVar, arg_var);
+
+	      if (arg_decls == NULL_TREE)
+		arg_decls = arg_var;
+	      else
+		chainon (arg_decls, arg_var);
+
+	      m_parent.m_cf->add_arg_variable (brigVar, arg_var);
+
+	      ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+				      void_type_node);
+	    }
+	  else
+	    {
+	      ret_value = varhandler.build_variable (brigVar, RESULT_DECL);
+	      m_parent.m_cf->m_ret_value = ret_value;
+	      ret_type = TREE_TYPE (ret_value);
+	      m_parent.m_cf->m_ret_value_brig_var = brigVar;
+	    }
+	  bytes_consumed += retval->byteCount;
+	}
+      else
+	ret_value = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+				void_type_node);
+
+      TREE_ADDRESSABLE (ret_value) = 1;
+
+      if (exec->inArgCount > 0)
+	{
+	  uint32_t arg_offset = exec->firstInArg;
+	  for (size_t arg = 0; arg < exec->inArgCount; ++arg)
+	    {
+
+	      const BrigDirectiveVariable *brigVar
+		= (const BrigDirectiveVariable *) m_parent.get_brig_code_entry
+		(arg_offset);
+
+	      gcc_assert (brigVar->base.kind == BRIG_KIND_DIRECTIVE_VARIABLE);
+
+	      /* Delegate to the brig_directive_variable_handler.  */
+	      brig_directive_variable_handler varhandler (m_parent);
+	      tree arg_var = varhandler.build_variable (brigVar, PARM_DECL);
+	      arg_offset += brigVar->base.byteCount;
+	      vec_safe_push (args, TREE_TYPE (arg_var));
+
+	      m_parent.m_cf->add_arg_variable (brigVar, arg_var);
+
+	      if (arg_decls == NULL_TREE)
+		arg_decls = arg_var;
+	      else
+		chainon (arg_decls, arg_var);
+	    }
+	}
+
+      vec_safe_push (args, ptr_type_node);
+      vec_safe_push (args, ptr_type_node);
+
+      fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, name_identifier,
+			   build_function_type_vec (ret_type, args));
+
+      DECL_RESULT (fndecl) = ret_value;
+      DECL_CONTEXT (ret_value) = fndecl;
+      DECL_EXTERNAL (fndecl) = 0;
+      DECL_ARGUMENTS (fndecl) = arg_decls;
+    }
+
+  /* All functions need the hidden __context argument passed on
+     because they might call WI-specific functions which need
+     the context info.  */
+  tree context_arg = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+				 get_identifier ("__context"), ptr_type_node);
+  if (DECL_ARGUMENTS (fndecl) == NULL_TREE)
+    DECL_ARGUMENTS (fndecl) = context_arg;
+  else
+    chainon (DECL_ARGUMENTS (fndecl), context_arg);
+  DECL_CONTEXT (context_arg) = fndecl;
+  DECL_ARG_TYPE (context_arg) = ptr_type_node;
+  DECL_ARTIFICIAL (context_arg) = 1;
+  TREE_READONLY (context_arg) = 1;
+  TREE_USED (context_arg) = 1;
+
+  /* They can also access group memory, so we need to pass the
+     group pointer along too.  */
+  tree group_base_arg
+    = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		  get_identifier ("__group_base_addr"), ptr_type_node);
+  chainon (DECL_ARGUMENTS (fndecl), group_base_arg);
+  DECL_ARG_TYPE (group_base_arg) = ptr_type_node;
+  DECL_CONTEXT (group_base_arg) = fndecl;
+  DECL_ARTIFICIAL (group_base_arg) = 1;
+  TREE_READONLY (group_base_arg) = 1;
+  TREE_USED (group_base_arg) = 1;
+  m_parent.m_cf->m_group_base_arg = group_base_arg;
+
+  /* To implement call stack and (non-kernel) function scope group variables,
+     we need to pass an offset which describes how far are we from
+     group_base_ptr.
+     That must be substracted from any function local group variable offsets to
+     get the address related to the bottom of the group memory chunk.  */
+  tree group_local_offset_arg
+    = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		  get_identifier ("__group_local_offset"), uint32_type_node);
+  chainon (DECL_ARGUMENTS (fndecl), group_local_offset_arg);
+  DECL_ARG_TYPE (group_local_offset_arg) = uint32_type_node;
+  DECL_CONTEXT (group_local_offset_arg) = fndecl;
+  DECL_ARTIFICIAL (group_local_offset_arg) = 1;
+  TREE_READONLY (group_local_offset_arg) = 1;
+  TREE_USED (group_local_offset_arg) = 1;
+  m_parent.m_cf->m_group_local_offset_arg = group_local_offset_arg;
+
+  /* Same for private.  */
+  tree private_base_arg
+    = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		  get_identifier ("__private_base_addr"), ptr_type_node);
+  chainon (DECL_ARGUMENTS (fndecl), private_base_arg);
+  DECL_ARG_TYPE (private_base_arg) = ptr_type_node;
+  DECL_CONTEXT (private_base_arg) = fndecl;
+  DECL_ARTIFICIAL (private_base_arg) = 1;
+  TREE_READONLY (private_base_arg) = 1;
+  TREE_USED (private_base_arg) = 1;
+
+  DECL_SAVED_TREE (fndecl) = bind_expr;
+
+  /* Try to preserve the functions across IPA.  */
+  DECL_PRESERVE_P (fndecl) = 1;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  TREE_ADDRESSABLE (fndecl) = 1;
+
+  if (base->kind == BRIG_KIND_DIRECTIVE_FUNCTION)
+    {
+      TREE_STATIC (fndecl) = 1;
+      TREE_PUBLIC (fndecl) = 1;
+    }
+  else if (base->kind == BRIG_KIND_DIRECTIVE_KERNEL)
+    {
+      TREE_STATIC (fndecl) = 1;
+      TREE_PUBLIC (fndecl) = 1;
+    }
+  else if (base->kind == BRIG_KIND_DIRECTIVE_SIGNATURE)
+    {
+      TREE_STATIC (fndecl) = 0;
+      TREE_PUBLIC (fndecl) = 1;
+      DECL_EXTERNAL (fndecl) = 1;
+    }
+  else if (base->kind == BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION)
+    {
+      TREE_STATIC (fndecl) = 0;
+      TREE_PUBLIC (fndecl) = 1;
+    }
+  else
+    gcc_unreachable ();
+
+  TREE_USED (fndecl) = 1;
+  DECL_ARTIFICIAL (fndecl) = 0;
+
+  tree initial_block = make_node (BLOCK);
+  DECL_INITIAL (fndecl) = initial_block;
+  TREE_USED (DECL_INITIAL (fndecl)) = 1;
+
+  if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
+    {
+      DECL_CONTEXT (ret_value) = fndecl;
+      DECL_CHAIN (ret_value) = BIND_EXPR_VARS (bind_expr);
+      BIND_EXPR_VARS (bind_expr) = ret_value;
+    }
+
+  tree arg;
+  for (arg = DECL_ARGUMENTS (fndecl); arg != NULL_TREE; arg = TREE_CHAIN (arg))
+    {
+      DECL_CONTEXT (arg) = fndecl;
+      DECL_ARG_TYPE (arg) = TREE_TYPE (arg);
+    }
+
+  m_parent.add_function_decl (func_name, fndecl);
+  m_parent.append_global (fndecl);
+
+  if (!is_definition)
+    return bytes_consumed;
+
+  m_parent.start_function (fndecl);
+
+  m_parent.m_cf->m_func_decl = fndecl;
+  m_parent.m_cf->m_current_bind_expr = bind_expr;
+  m_parent.m_cf->m_context_arg = context_arg;
+  m_parent.m_cf->m_private_base_arg = private_base_arg;
+
+  if (ret_value != NULL_TREE && TREE_TYPE (ret_value) != void_type_node)
+    {
+      /* We cannot assign to <<retval>> directly in gcc trunk.  We need to
+	 create a local temporary variable which can be stored to and when
+	 returning from the function, we'll copy it to the actual <<retval>>
+	 in return statement's argument.  */
+      tree temp_var = m_parent.m_cf->m_ret_temp
+	= m_parent.m_cf->add_local_variable ("_retvalue_temp",
+					     TREE_TYPE (ret_value));
+      TREE_ADDRESSABLE (temp_var) = 1;
+    }
+
+  if (is_kernel)
+    {
+      m_parent.m_cf->add_id_variables ();
+
+      /* Create a single entry point in the function.  */
+      m_parent.m_cf->m_entry_label_stmt
+	= build_stmt (LABEL_EXPR, m_parent.m_cf->label ("__kernel_entry"));
+      m_parent.m_cf->append_statement (m_parent.m_cf->m_entry_label_stmt);
+
+      tree bind_expr = m_parent.m_cf->m_current_bind_expr;
+      tree stmts = BIND_EXPR_BODY (bind_expr);
+
+      m_parent.m_cf->m_kernel_entry = tsi_last (stmts);
+
+      /* Let's not append the exit label yet, but only after the
+	 function has been built.  We need to build it so it can
+	 be referred to because returns are converted to gotos to this
+	 label.  */
+      m_parent.m_cf->m_exit_label = m_parent.m_cf->label ("__kernel_exit");
+    }
+
+  return bytes_consumed;
+}