Hello.

Following patch introduces support for direct call instructions
for HSAIL.

Thanks,
Martin

gcc/c-family/ChangeLog:

2014-12-05  Martin Liska  <mli...@suse.cz>

        * c-common.c: New 'hsafunc' attribute is added.

gcc/ChangeLog:

2014-12-05  Martin Liska  <mli...@suse.cz>

        * hsa-brig.c (struct function_linkage_pair): New data structure.
        (hsa_brig_section::get_ptr_by_offset): New function.
        (emit_directive_variable): Linkage is retrieved by symbol.
        (emit_function_directives): Emitted function is added to map
        with offsets.
        (enqueue_op): New operand type handling added.
        (emit_code_ref_operand): Created from emit_label_operand.
        (emit_code_list_operand): New function.
        (emit_queued_operands): New operand type handling added.
        (emit_segment_insn): BRIG_KIND_INST_SEG is changed
        to BRIG_KIND_INST_SEG_CVT.
        (emit_cvt_insn): Undefined behavior fixed by wrong array bounds.
        (emit_arg_block): New function.
        (emit_call_insn): Likewise.
        (emit_call_block_insn): Likewise.
        (emit_insn): New instructions are handled.
        (hsa_output_brig): Function offsets for call instructions
        are resolved.
        * hsa-dump.c (static void indent_stream): New function.
        (dump_hsa_insn): Added support for call instruction.
        * hsa-gen.c (hsa_init_data_for_cfun): New flag for hsa_cfun is parsed.
        (hsa_deinit_data_for_cfun): New pools are deallocated.
        (get_symbol_for_decl): Symbol's linkage is set up.
        (hsa_get_spill_symbol): Likewise.
        (hsa_alloc_code_list_op): New function.
        (hsa_alloc_call_insn): Likewise.
        (hsa_alloc_call_block_insn): Likewise.
        (gen_hsa_addr_for_arg): Likewise.
        (gen_hsa_insns_for_direct_call): Likewise.
        (gen_hsa_insns_for_return): Likewise.
        (gen_hsa_insns_for_call): Likewise.
        (gen_hsa_insns_for_gimple_stmt): GIMPLE labels with non-taken address
        are supported.
        (gen_function_parameters): Linkage condition is introduced.
        (generate_hsa): kern_p flag is parsed.
        (wrap_hsa): Likewise.
        (pass_gen_hsail::execute): Likewise.
        (struct hsa_op_reg::verify): New function.
        * hsa.h (struct hsa_symbol): Linkage member is added.
        (struct hsa_op_code_ref): Created from existing hsa_op_label_ref.
        (struct hsa_op_code_list): New operand is added.
        (struct hsa_insn_call): New instruction.
        (struct hsa_insn_call_block): Likewise.
        (struct hsa_function_representation): kern_p attribute is introduced.
        (struct hsa_op_reg::verify): New function.
---
 gcc/c-family/c-common.c |   2 +
 gcc/hsa-brig.c          | 222 +++++++++++++++++++++++++++++++++++++++++----
 gcc/hsa-dump.c          |  47 +++++++++-
 gcc/hsa-gen.c           | 234 +++++++++++++++++++++++++++++++++++++++++++++---
 gcc/hsa.h               | 114 +++++++++++++++++++++--
 5 files changed, 583 insertions(+), 36 deletions(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 7e348d3..74024a8 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -660,6 +660,8 @@ const struct attribute_spec c_common_attribute_table[] =
                              handle_hsa_attribute, false },
   { "hsakernel",              0, 0, true,  false, false,
                              handle_hsa_attribute, false },
+  { "hsafunc",                0, 0, true,  false, false,
+                             handle_hsa_attribute, false },
   { "leaf",                   0, 0, true,  false, false,
                              handle_leaf_attribute, false },
   { "always_inline",          0, 0, true,  false, false,
diff --git a/gcc/hsa-brig.c b/gcc/hsa-brig.c
index 13b4aaa..61eadf9 100644
--- a/gcc/hsa-brig.c
+++ b/gcc/hsa-brig.c
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "vec.h"
 #include "gimple-pretty-print.h"
 #include "diagnostic-core.h"
+#include "hash-map.h"
 
 #define BRIG_SECTION_DATA_NAME    "hsa_data"
 #define BRIG_SECTION_CODE_NAME    "hsa_code"
@@ -80,12 +81,31 @@ public:
   void output ();
   unsigned add (const void *data, unsigned len);
   void round_size_up (int factor);
+  void *get_ptr_by_offset (unsigned int offset);
 };
 
 static struct hsa_brig_section brig_data, brig_code, brig_operand;
 static uint32_t brig_insn_count;
 static bool brig_initialized = false;
 
+/* Mapping between emitted HSA functions and their offset in code segment.  */
+static hash_map <tree, BrigCodeOffset32_t> function_offsets;
+
+struct function_linkage_pair
+{
+  function_linkage_pair (tree decl, unsigned int off):
+    function_decl (decl), offset (off) {}
+
+  /* Declaration of called function.  */
+  tree function_decl;
+
+  /* Offset in operand section.  */
+  unsigned int offset;
+};
+
+/* Vector of function calls where we need to resolve function offsets.  */
+static auto_vec <function_linkage_pair> function_call_linkage;
+
 /* Add a new chunk, allocate data for it and initialize it.  */
 
 void
@@ -187,6 +207,23 @@ hsa_brig_section::round_size_up (int factor)
   cur_chunk->size += padding;
 }
 
+/* Return pointer to data by global OFFSET in the section.  */
+
+void*
+hsa_brig_section::get_ptr_by_offset (unsigned int offset)
+{
+  gcc_assert (offset < total_size);
+
+  offset -= header_byte_count;
+  unsigned int i;
+
+  for (i = 0; offset >= chunks[i].size; i++)
+    offset -= chunks[i].size;
+
+  return chunks[i].data + offset;
+}
+
+
 /* BRIG string data hashing.  */
 
 struct brig_string_slot
@@ -414,7 +451,6 @@ emit_directive_variable (struct hsa_symbol *symbol)
       if (res_name_offset == 0)
        res_name_offset = brig_emit_string (symbol->name, '%');
       name_offset = res_name_offset;
-      dirvar.allocation = BRIG_ALLOCATION_NONE;
     }
   else if (symbol->name)
     name_offset = brig_emit_string (symbol->name, prefix);
@@ -431,7 +467,8 @@ emit_directive_variable (struct hsa_symbol *symbol)
   dirvar.type = htole16 (symbol->type);
   dirvar.segment = symbol->segment;
   dirvar.align = get_alignment (dirvar.type);
-  dirvar.linkage = BRIG_LINKAGE_FUNCTION ;
+  gcc_assert (symbol->linkage);
+  dirvar.linkage = symbol->linkage;
   dirvar.dim.lo = htole32 (symbol->dimLo);
   dirvar.dim.hi = htole32 (symbol->dimHi);
   dirvar.modifier = BRIG_SYMBOL_DEFINITION;
@@ -464,12 +501,12 @@ emit_function_directives (void)
        ++iter)
     if (TREE_CODE ((*iter)->decl) == VAR_DECL)
       count++;
-  count += hsa_cfun.spill_symbols.length();
+  count += hsa_cfun.spill_symbols.length ();
 
   next_toplev_off = scoped_off + count * sizeof (struct BrigDirectiveVariable);
 
   fndir.base.byteCount = htole16 (sizeof (fndir));
-  fndir.base.kind = htole16 (BRIG_KIND_DIRECTIVE_KERNEL);
+  fndir.base.kind = htole16 (hsa_cfun.kern_p ? BRIG_KIND_DIRECTIVE_KERNEL : 
BRIG_KIND_DIRECTIVE_FUNCTION);
   fndir.name = htole32 (name_offset);
   fndir.inArgCount = htole16 (hsa_cfun.input_args_count);
   fndir.outArgCount = htole16 (hsa_cfun.output_arg ? 1 : 0);
@@ -481,6 +518,8 @@ emit_function_directives (void)
   fndir.modifier = BRIG_EXECUTABLE_DEFINITION;
   memset (&fndir.reserved, 0, sizeof (fndir.reserved));
 
+  function_offsets.put (cfun->decl, brig_code.total_size);
+
   brig_code.add (&fndir, sizeof (fndir));
   /* XXX terrible hack: we need to set instCount after we emit all
      insns, but we need to emit directive in order, and we emit directives
@@ -501,12 +540,17 @@ emit_function_directives (void)
         = hsa_cfun.local_symbols->begin ();
        iter != hsa_cfun.local_symbols->end ();
        ++iter)
-    emit_directive_variable(*iter);
+    {
+      if (TREE_CODE ((*iter)->decl) == VAR_DECL)
+       brig_insn_count++;
+      emit_directive_variable(*iter);
+    }
   for (int i = 0; hsa_cfun.spill_symbols.iterate (i, &sym); i++)
     {
       emit_directive_variable (sym);
       brig_insn_count++;
     }
+
   return ptr_to_fndir;
 }
 
@@ -683,8 +727,10 @@ enqueue_op (hsa_op_base *op)
     {
     op_queue.projected_size += sizeof (struct BrigOperandAddress);
     }
-  else if (is_a <hsa_op_label *> (op))
+  else if (is_a <hsa_op_code_ref *> (op))
     op_queue.projected_size += sizeof (struct BrigOperandCodeRef);
+  else if (is_a <hsa_op_code_list *> (op))
+    op_queue.projected_size += sizeof (struct BrigOperandCodeList);
   else
     gcc_unreachable ();
   return ret;
@@ -857,17 +903,36 @@ emit_address_operand (hsa_op_address *addr)
   brig_operand.add (&out, sizeof (out));
 }
 
-/* Emit a label BRIG operand LABEL.  */
+/* Emit a code reference operand REF.  */
 
 static void
-emit_label_operand (hsa_op_label *lbl)
+emit_code_ref_operand (hsa_op_code_ref *ref)
 {
   struct BrigOperandCodeRef out;
 
-  gcc_assert (lbl->directive_offset);
   out.base.byteCount = htole16 (sizeof (out));
   out.base.kind = htole16 (BRIG_KIND_OPERAND_CODE_REF);
-  out.ref = htole32 (lbl->directive_offset);
+  out.ref = htole32 (ref->directive_offset);
+  brig_operand.add (&out, sizeof (out));
+}
+
+static void
+emit_code_list_operand (hsa_op_code_list *code_list)
+{
+  struct BrigOperandCodeList out;
+  unsigned args = code_list->offsets.length ();
+
+  for (unsigned i = 0; i < args; i++)
+    gcc_assert (code_list->offsets[i]);
+
+  out.base.byteCount = htole16 (sizeof (out));
+  out.base.kind = htole16 (BRIG_KIND_OPERAND_CODE_LIST);
+
+  uint32_t byteCount = htole32 (4 * args);
+
+  out.elements = htole32 (brig_data.add (&byteCount, sizeof (byteCount)));
+  brig_data.add (code_list->offsets.address (), args * sizeof (uint32_t));
+  brig_data.round_size_up (4);
   brig_operand.add (&out, sizeof (out));
 }
 
@@ -885,8 +950,10 @@ emit_queued_operands (void)
        emit_register_operand (reg);
       else if (hsa_op_address *addr = dyn_cast <hsa_op_address *> (op))
        emit_address_operand (addr);
-      else if (hsa_op_label *lbl = dyn_cast <hsa_op_label *> (op))
-       emit_label_operand (lbl);
+      else if (hsa_op_code_ref *ref = dyn_cast <hsa_op_code_ref *> (op))
+       emit_code_ref_operand (ref);
+      else if (hsa_op_code_list *code_list = dyn_cast <hsa_op_code_list *> 
(op))
+       emit_code_list_operand (code_list);
       else
        gcc_unreachable ();
     }
@@ -1028,12 +1095,12 @@ emit_addr_insn (hsa_insn_addr *insn)
 static void
 emit_segment_insn (hsa_insn_seg *seg)
 {
-  struct BrigInstSeg repr;
+  struct BrigInstSegCvt repr;
   BrigOperandOffset32_t operand_offsets[2];
   uint32_t byteCount;
 
   repr.base.base.byteCount = htole16 (sizeof (repr));
-  repr.base.base.kind = htole16 (BRIG_KIND_INST_SEG);
+  repr.base.base.kind = htole16 (BRIG_KIND_INST_SEG_CVT);
   repr.base.opcode = htole16 (seg->opcode);
   repr.base.type = htole16 (seg->type);
 
@@ -1047,8 +1114,9 @@ emit_segment_insn (hsa_insn_seg *seg)
   brig_data.add (&operand_offsets, sizeof (operand_offsets));
   brig_data.round_size_up (4);
 
+  repr.sourceType = htole16 (as_a <hsa_op_reg *> (seg->operands[1])->type);
   repr.segment = seg->segment;
-  memset (&repr.reserved, 0, sizeof (repr.reserved));
+  repr.modifier = 0;
 
   brig_code.add (&repr, sizeof (repr));
 
@@ -1163,7 +1231,7 @@ emit_cvt_insn (hsa_insn_basic *insn)
 {
   struct BrigInstCvt repr;
   BrigType16_t srctype;
-  BrigOperandOffset32_t operand_offsets[1];
+  BrigOperandOffset32_t operand_offsets[HSA_OPERANDS_PER_INSN];
   uint32_t byteCount, operand_count=0;
 
   repr.base.base.byteCount = htole16 (sizeof (repr));
@@ -1203,6 +1271,99 @@ emit_cvt_insn (hsa_insn_basic *insn)
   brig_insn_count++;
 }
 
+/* Emit arg block to code segment.  */
+
+static void
+emit_arg_block (bool is_start)
+{
+  struct BrigDirectiveArgBlock repr;
+  repr.base.byteCount = htole16 (sizeof (repr));
+
+  BrigKinds16_t kind = is_start ? BRIG_KIND_DIRECTIVE_ARG_BLOCK_START
+    : BRIG_KIND_DIRECTIVE_ARG_BLOCK_END;
+  repr.base.kind = htole16 (kind);
+
+  brig_code.add (&repr, sizeof (repr));
+  brig_insn_count++;
+}
+
+static void
+emit_call_insn (hsa_insn_basic *insn)
+{
+  hsa_insn_call *call = dyn_cast <hsa_insn_call *> (insn);
+  struct BrigInstBr repr;
+  uint32_t byteCount;
+
+  BrigOperandOffset32_t operand_offsets[3];
+
+  repr.base.base.byteCount = htole16 (sizeof (repr));
+  repr.base.base.kind = htole16 (BRIG_KIND_INST_BR);
+  repr.base.opcode = htole16 (BRIG_OPCODE_CALL);
+  repr.base.type = htole16 (BRIG_TYPE_NONE);
+
+  /* Operand 0: out-args.  */
+  operand_offsets[0] = htole32 (enqueue_op (call->result_code_list));
+
+  /* Operand 1: func */
+  /* XXX: we have to save offset to operand section and
+     called function offset is filled up after all functions are visited. */
+  unsigned int offset = enqueue_op (&call->func);
+
+  function_call_linkage.safe_push
+    (function_linkage_pair (call->called_function, offset));
+
+  operand_offsets[1] = htole32 (offset);
+  /* Operand 2: in-args.  */
+  operand_offsets[2] = htole32 (enqueue_op (call->args_code_list));
+
+  /* We have 3 operands so use 3 * 4 for the byteCount */
+  byteCount = htole32 (3 * 4);
+  repr.base.operands = htole32 (brig_data.add (&byteCount, sizeof 
(byteCount)));
+  brig_data.add (&operand_offsets, sizeof (operand_offsets));
+  brig_data.round_size_up (4);
+  repr.width = BRIG_WIDTH_ALL;
+  memset (&repr.reserved, 0, sizeof (repr.reserved));
+
+  brig_code.add (&repr, sizeof (repr));
+  brig_insn_count++;
+}
+
+/* Emit call block instruction. This super instruction encapsulate all
+   instructions needed for argument load/store and corresponding
+   instruction.  */
+
+static void
+emit_call_block_insn (hsa_insn_call_block *insn)
+{
+  /* Argument scope start.  */
+  emit_arg_block (true);
+
+  for (unsigned i = 0; i < insn->input_args.length (); i++)
+    {
+      insn->call_insn->args_code_list->offsets[i] = htole32
+       (emit_directive_variable (insn->input_args[i]));
+      brig_insn_count++;
+    }
+
+  if (insn->call_insn->result_symbol)
+    {
+      insn->call_insn->result_code_list->offsets[0] = htole32
+       (emit_directive_variable (insn->output_arg));
+      brig_insn_count++;
+    }
+
+  for (unsigned i = 0; i < insn->input_arg_insns.length (); i++)
+    emit_memory_insn (insn->input_arg_insns[i]);
+
+  emit_call_insn (insn->call_insn);
+
+  if (insn->output_arg_insn)
+    emit_memory_insn (insn->output_arg_insn);
+
+  /* Argument scope end.  */
+  emit_arg_block (false);
+}
+
 /* Emit a basic HSA instruction and all necessary directives, schedule
    necessary operands for writing .  */
 
@@ -1317,6 +1478,16 @@ emit_insn (hsa_insn_basic *insn)
       emit_branch_insn (br);
       return;
     }
+  if (hsa_insn_call_block *call_block = dyn_cast <hsa_insn_call_block *> 
(insn))
+    {
+      emit_call_block_insn (call_block);
+      return;
+    }
+  if (hsa_insn_call *call = dyn_cast <hsa_insn_call *> (insn))
+    {
+      emit_call_insn (call);
+      return;
+    }
   emit_basic_insn (insn);
 }
 
@@ -1413,6 +1584,25 @@ hsa_output_brig (void)
   if (!brig_initialized)
     return;
 
+  for (unsigned i = 0; i < function_call_linkage.length (); i++)
+    {
+      function_linkage_pair p = function_call_linkage[i];
+
+      BrigCodeOffset32_t *func_offset = function_offsets.get (p.function_decl);
+      if (*func_offset)
+        {
+         BrigOperandCodeRef *code_ref = (BrigOperandCodeRef *)
+           (brig_operand.get_ptr_by_offset (p.offset));
+         gcc_assert (code_ref->base.kind == BRIG_KIND_OPERAND_CODE_REF);
+         code_ref->ref = htole32 (*func_offset);
+       }
+      else
+       {
+         sorry ("Missing offset to a HSA function in call instruction");
+         return;
+       }
+    }
+
   saved_section = in_section;
 
   brig_data.output ();
diff --git a/gcc/hsa-dump.c b/gcc/hsa-dump.c
index d78dc08..3a4774a 100644
--- a/gcc/hsa-dump.c
+++ b/gcc/hsa-dump.c
@@ -700,14 +700,21 @@ dump_hsa_address (FILE *f, hsa_op_address *addr)
     fprintf (f, "[" HOST_WIDE_INT_PRINT_DEC "]", addr->imm_offset);
 }
 
+/* Indent F stream with INDENT spaces.  */
+
+static void indent_stream (FILE *f, int indent)
+{
+  for (int i = 0; i < indent; i++)
+    fputc (' ', f);
+}
+
 /* Dump textual representation of HSA IL instruction INSN to file F.  */
 
 static void
 dump_hsa_insn (FILE *f, hsa_insn_basic *insn, int indent)
 {
   gcc_checking_assert (insn);
-  for (int i = 0; i < indent; i++)
-    fputc (' ', f);
+  indent_stream (f, indent);
 
   if (is_a <hsa_insn_phi *> (insn))
     {
@@ -844,6 +851,42 @@ dump_hsa_insn (FILE *f, hsa_insn_basic *insn, int indent)
          }
       fprintf (f, "BB %i\n", hsa_bb_for_bb (target)->index);
     }
+  else if (is_a <hsa_insn_call_block *> (insn))
+    {
+      hsa_insn_call_block *call_block = as_a <hsa_insn_call_block *> (insn);
+
+      fprintf (f, "{\n");
+
+      for (unsigned i = 0; i < call_block->input_arg_insns.length (); i++)
+       dump_hsa_insn (f, call_block->input_arg_insns[i], indent + 2);
+
+      dump_hsa_insn (f, call_block->call_insn, indent + 2);
+
+      if (call_block->output_arg_insn)
+       dump_hsa_insn (f, call_block->output_arg_insn, indent + 2);
+
+      indent_stream (f, indent);
+      fprintf (f, "}\n");
+    }
+  else if (is_a <hsa_insn_call *> (insn))
+    {
+      hsa_insn_call *call = as_a <hsa_insn_call *> (insn);
+      const char *name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME 
(call->called_function)));
+
+      fprintf (f, "call &%s", name);
+
+      if (call->result_symbol)
+       fprintf (f, "(%%%s) ", call->result_symbol->name);
+
+      fprintf (f, "(");
+      for (unsigned i = 0; i < call->args_symbols.length (); i++)
+        {
+         fprintf (f, "%%%s", call->args_symbols[i]->name);
+         if (i != call->args_symbols.length () - 1)
+           fprintf (f, ", ");
+       }
+      fprintf (f, ")\n");
+    }
   else
     {
       bool first = true;
diff --git a/gcc/hsa-gen.c b/gcc/hsa-gen.c
index 8b759df..5029d26 100644
--- a/gcc/hsa-gen.c
+++ b/gcc/hsa-gen.c
@@ -70,6 +70,7 @@ struct hsa_function_representation hsa_cfun;
 static alloc_pool hsa_allocp_operand_address;
 static alloc_pool hsa_allocp_operand_immed;
 static alloc_pool hsa_allocp_operand_reg;
+static alloc_pool hsa_allocp_operand_code_list;
 static alloc_pool hsa_allocp_inst_basic;
 static alloc_pool hsa_allocp_inst_phi;
 static alloc_pool hsa_allocp_inst_mem;
@@ -78,6 +79,8 @@ static alloc_pool hsa_allocp_inst_addr;
 static alloc_pool hsa_allocp_inst_seg;
 static alloc_pool hsa_allocp_inst_cmp;
 static alloc_pool hsa_allocp_inst_br;
+static alloc_pool hsa_allocp_inst_call;
+static alloc_pool hsa_allocp_inst_call_block;
 static alloc_pool hsa_allocp_bb;
 static alloc_pool hsa_allocp_symbols;
 
@@ -129,6 +132,9 @@ hsa_init_data_for_cfun ()
   hsa_allocp_operand_reg
     = create_alloc_pool ("HSA register operands",
                         sizeof (struct hsa_op_reg), 64);
+  hsa_allocp_operand_code_list
+    = create_alloc_pool ("HSA code list operands",
+                        sizeof (struct hsa_op_code_list), 64);
   hsa_allocp_inst_basic
     = create_alloc_pool ("HSA basic instructions",
                         sizeof (struct hsa_insn_basic), 64);
@@ -153,6 +159,13 @@ hsa_init_data_for_cfun ()
   hsa_allocp_inst_br
     = create_alloc_pool ("HSA branching instructions",
                         sizeof (struct hsa_insn_br), 16);
+  hsa_allocp_inst_call
+    = create_alloc_pool ("HSA call instructions",
+                        sizeof (struct hsa_insn_call), 16);
+  hsa_allocp_inst_call_block
+    = create_alloc_pool ("HSA call block instructions",
+                        sizeof (struct hsa_insn_call_block), 16);
+
   hsa_allocp_bb
     = create_alloc_pool ("HSA basic blocks",
                         sizeof (struct hsa_bb), 8);
@@ -184,6 +197,7 @@ hsa_deinit_data_for_cfun (void)
   free_alloc_pool (hsa_allocp_operand_address);
   free_alloc_pool (hsa_allocp_operand_immed);
   free_alloc_pool (hsa_allocp_operand_reg);
+  free_alloc_pool (hsa_allocp_operand_code_list);
   free_alloc_pool (hsa_allocp_inst_basic);
   free_alloc_pool (hsa_allocp_inst_phi);
   free_alloc_pool (hsa_allocp_inst_atomic);
@@ -192,6 +206,8 @@ hsa_deinit_data_for_cfun (void)
   free_alloc_pool (hsa_allocp_inst_seg);
   free_alloc_pool (hsa_allocp_inst_cmp);
   free_alloc_pool (hsa_allocp_inst_br);
+  free_alloc_pool (hsa_allocp_inst_call);
+  free_alloc_pool (hsa_allocp_inst_call_block);
   free_alloc_pool (hsa_allocp_bb);
 
   free_alloc_pool (hsa_allocp_symbols);
@@ -484,6 +500,7 @@ get_symbol_for_decl (tree decl)
        return *slot;
       sym = XCNEW (struct hsa_symbol);
       sym->segment = BRIG_SEGMENT_GLOBAL;
+      sym->linkage = BRIG_LINKAGE_FUNCTION;
       warning (0, "referring to global symbol %q+D by name from HSA code won't 
work", decl);
     }
   else
@@ -496,6 +513,7 @@ get_symbol_for_decl (tree decl)
       sym = (struct hsa_symbol *) pool_alloc (hsa_allocp_symbols);
       memset (sym, 0, sizeof (hsa_symbol));
       sym->segment = BRIG_SEGMENT_PRIVATE;
+      sym->linkage = BRIG_LINKAGE_FUNCTION;
     }
 
   fillup_sym_for_decl (decl, sym);
@@ -515,6 +533,7 @@ hsa_get_spill_symbol (BrigType16_t type)
   hsa_symbol *sym = (struct hsa_symbol *) pool_alloc (hsa_allocp_symbols);
   memset (sym, 0, sizeof (hsa_symbol));
   sym->segment = BRIG_SEGMENT_SPILL;
+  sym->linkage = BRIG_LINKAGE_FUNCTION;
   sym->type = type;
   hsa_cfun.spill_symbols.safe_push(sym);
   return sym;
@@ -538,6 +557,13 @@ hsa_alloc_immed_op (tree tree_val)
   return imm;
 }
 
+/* Verify register operand.  */
+void
+hsa_op_reg::verify ()
+{
+  gcc_checking_assert (def_insn);
+}
+
 /* Allocate, clear and return a hsa_op_reg.  */
 
 static hsa_op_reg *
@@ -573,6 +599,20 @@ hsa_alloc_addr_op (hsa_symbol *sym, hsa_op_reg *reg, 
HOST_WIDE_INT offset)
   return addr;
 }
 
+static hsa_op_code_list *
+hsa_alloc_code_list_op (unsigned elements)
+{
+  hsa_op_code_list *list;
+  list = (hsa_op_code_list *) pool_alloc (hsa_allocp_operand_code_list);
+
+  memset (list, 0, sizeof (hsa_op_code_list));
+  list->kind = BRIG_KIND_OPERAND_CODE_LIST;
+  list->offsets.create (1);
+  list->offsets.safe_grow_cleared (elements);
+
+  return list;
+}
+
 /* Lookup or create a HSA pseudo register for a given gimple SSA name.  */
 
 static hsa_op_reg *
@@ -700,6 +740,33 @@ hsa_build_cbr_insn (hsa_op_reg *ctrl)
   return cbr;
 }
 
+/* Allocate, clear and return a call instruction structure.  */
+
+static hsa_insn_call *
+hsa_alloc_call_insn (void)
+{
+  hsa_insn_call *call;
+
+  call = (hsa_insn_call *) pool_alloc (hsa_allocp_inst_call);
+  memset (call, 0, sizeof (hsa_insn_call));
+  return call;
+}
+
+/* Allocate, clear and return a arg block instruction structure.  */
+
+static hsa_insn_call_block *
+hsa_alloc_call_block_insn (void)
+{
+  hsa_insn_call_block *call_block;
+
+  call_block = (hsa_insn_call_block *) pool_alloc (hsa_allocp_inst_call_block);
+  memset (call_block, 0, sizeof (hsa_insn_call_block));
+
+  call_block->opcode = HSA_OPCODE_CALL_BLOCK;
+  return call_block;
+}
+
+
 /* Append HSA instruction INSN to basic block HBB.  */
 
 static void
@@ -998,6 +1065,29 @@ gen_hsa_addr (tree ref, hsa_bb *hbb, vec <hsa_op_reg_p> 
ssa_map)
   return hsa_alloc_addr_op (symbol, reg, offset);
 }
 
+static hsa_op_address *
+gen_hsa_addr_for_arg (tree tree_type, int index)
+{
+  hsa_symbol *sym = (struct hsa_symbol *) pool_alloc (hsa_allocp_symbols);
+  memset (sym, 0, sizeof (hsa_symbol));
+  sym->segment = BRIG_SEGMENT_ARG;
+  sym->linkage = BRIG_LINKAGE_ARG;
+
+  sym->type = hsa_type_for_tree_type (tree_type, &sym->dimLo,
+                                     &sym->dimHi, false);
+
+  if (index == -1) /* Function result.  */
+    sym->name = "res";
+  else /* Function call arguments.  */
+    {
+      char *name = (char *) pool_alloc (hsa_allocp_symbols);
+      sprintf (name, "arg_%d", index);
+      sym->name = name;
+    }
+
+  return hsa_alloc_addr_op (sym, NULL, 0);
+}
+
 /* Generate HSA instructions that calculate address of VAL including all
    necessary conversions to flat addressing and place the result into DEST.
    Instructions are appended to HBB.  SSA_MAP maps gimple SSA names to HSA
@@ -1568,6 +1658,104 @@ gen_hsa_insns_for_cond_stmt (gimple cond, hsa_bb *hbb,
   hsa_append_insn (hbb, cbr);
 }
 
+static void
+gen_hsa_insns_for_direct_call (gimple stmt, hsa_bb *hbb,
+                              vec <hsa_op_reg_p> ssa_map)
+{
+  hsa_insn_call *call_insn = hsa_alloc_call_insn ();
+  call_insn->opcode = BRIG_OPCODE_CALL;
+  call_insn->called_function = gimple_call_fndecl (stmt);
+  call_insn->func.kind = BRIG_KIND_OPERAND_CODE_REF;
+
+  hsa_insn_call_block *call_block_insn = hsa_alloc_call_block_insn ();
+
+  /* Preparation of arguments that will be passed to function.  */
+  const unsigned args = gimple_call_num_args (stmt);
+  for (unsigned i = 0; i < args; ++i)
+    {
+      tree parm = gimple_call_arg (stmt, (int)i);
+      hsa_op_address *addr;
+      hsa_insn_mem *mem = hsa_alloc_mem_insn ();
+      hsa_op_base *src = hsa_reg_or_immed_for_gimple_op (parm, hbb, ssa_map,
+                                                        mem);
+
+      addr = gen_hsa_addr_for_arg (TREE_TYPE (parm), i);
+      mem->opcode = BRIG_OPCODE_ST;
+      mem->type = mem_type_for_type (hsa_type_for_scalar_tree_type (TREE_TYPE 
(parm), false));
+      mem->operands[0] = src;
+      mem->operands[1] = addr;
+
+      call_block_insn->input_args.safe_push (addr->symbol);
+      call_block_insn->input_arg_insns.safe_push (mem);
+
+      call_insn->args_symbols.safe_push (addr->symbol);
+    }
+
+  call_insn->args_code_list = hsa_alloc_code_list_op (args);
+
+  tree result_type = TREE_TYPE (TREE_TYPE (gimple_call_fndecl (stmt)));
+
+  tree result = gimple_call_lhs (stmt);
+  hsa_insn_mem *result_insn = NULL;
+  if (!VOID_TYPE_P (result_type))
+    {
+      hsa_op_address *addr = gen_hsa_addr_for_arg (result_type, -1);
+
+      /* Even if result of a function call is unused, we have to emit
+        declaration for the result.  */
+      if (result)
+       {
+         result_insn = hsa_alloc_mem_insn ();
+         hsa_op_reg *dst = hsa_reg_for_gimple_ssa (result, ssa_map);
+
+         result_insn->opcode = BRIG_OPCODE_LD;
+         result_insn->type = mem_type_for_type (hsa_type_for_scalar_tree_type 
(TREE_TYPE (result), false));
+         result_insn->operands[0] = dst;
+         result_insn->operands[1] = addr;
+         set_reg_def (dst, result_insn);
+
+         call_block_insn->output_arg_insn = result_insn;
+       }
+
+      call_block_insn->output_arg = addr->symbol;
+      call_insn->result_symbol = addr->symbol;
+      call_insn->result_code_list = hsa_alloc_code_list_op (1);
+    }
+  else
+    call_insn->result_code_list = hsa_alloc_code_list_op (0);
+
+  call_block_insn->call_insn = call_insn;
+
+  if (result_insn)
+    call_block_insn->output_arg_insn = result_insn;
+
+  hsa_append_insn (hbb, call_block_insn);
+}
+
+static void
+gen_hsa_insns_for_return (gimple stmt, hsa_bb *hbb,
+                       vec <hsa_op_reg_p> ssa_map)
+{
+  tree retval = gimple_return_retval (stmt);
+  if (retval)
+    {
+      /* Store of return value.  */
+      hsa_insn_mem *mem = hsa_alloc_mem_insn ();
+      hsa_op_reg *src = hsa_reg_for_gimple_ssa (retval, ssa_map);
+
+      hsa_op_address *addr = hsa_alloc_addr_op (hsa_cfun.output_arg, NULL, 0);
+      mem->opcode = BRIG_OPCODE_ST;
+      mem->type = mem_type_for_type (hsa_type_for_scalar_tree_type (TREE_TYPE 
(retval), false));
+      mem->operands[0] = src;
+      mem->operands[1] = addr;
+      hsa_append_insn (hbb, mem);
+    }
+
+  /* HSAIL return instruction emission.  */
+  hsa_insn_basic *ret = hsa_alloc_basic_insn ();
+  ret->opcode = BRIG_OPCODE_RET;
+  hsa_append_insn (hbb, ret);
+}
 
 static void
 gen_hsa_insns_for_call (gimple stmt, hsa_bb *hbb,
@@ -1580,7 +1768,11 @@ gen_hsa_insns_for_call (gimple stmt, hsa_bb *hbb,
 
   if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     {
-      sorry ("Support for HSA does not implement calling user functions");
+      if (!lookup_attribute ("hsafunc", DECL_ATTRIBUTES (gimple_call_fndecl 
(stmt))))
+       sorry ("HSA does support only call for functions with 'hsafunc' 
attribute");
+      else
+        gen_hsa_insns_for_direct_call (stmt, hbb, ssa_map);
+
       return;
     }
 
@@ -1731,12 +1923,8 @@ gen_hsa_insns_for_gimple_stmt (gimple stmt, hsa_bb *hbb,
        gen_hsa_insns_for_operation_assignment (stmt, hbb, ssa_map);
       break;
     case GIMPLE_RETURN:
-      {
-       hsa_insn_basic *ret = hsa_alloc_basic_insn ();
-       ret->opcode = BRIG_OPCODE_RET;
-       hsa_append_insn (hbb, ret);
-       break;
-      }
+      gen_hsa_insns_for_return (stmt, hbb, ssa_map);
+      break;
     case GIMPLE_COND:
       gen_hsa_insns_for_cond_stmt (stmt, hbb, ssa_map);
       break;
@@ -1746,6 +1934,14 @@ gen_hsa_insns_for_gimple_stmt (gimple stmt, hsa_bb *hbb,
     case GIMPLE_DEBUG:
       /* ??? HSA supports some debug facilities.  */
       break;
+    case GIMPLE_LABEL:
+    {
+      tree label = gimple_label_label (stmt);
+      if (FORCED_LABEL (label))
+       sorry ("Support for HSA does not implement gimple label with address 
taken");
+
+      break;
+    }
     default:
       sorry ("Support for HSA does not implement gimple statement %s",
             gimple_code_name[(int) gimple_code (stmt)]);
@@ -1890,7 +2086,10 @@ gen_function_parameters (vec <hsa_op_reg_p> ssa_map)
       struct hsa_symbol **slot;
 
       fillup_sym_for_decl (parm, &hsa_cfun.input_args[i]);
-      hsa_cfun.input_args[i].segment = BRIG_SEGMENT_KERNARG;
+      hsa_cfun.input_args[i].segment = hsa_cfun.kern_p ? BRIG_SEGMENT_KERNARG :
+                                      BRIG_SEGMENT_ARG;
+
+      hsa_cfun.input_args[i].linkage = BRIG_LINKAGE_FUNCTION;
       if (!DECL_NAME (parm))
        {
          /* FIXME: Just generate some UID.  */
@@ -1931,13 +2130,12 @@ gen_function_parameters (vec <hsa_op_reg_p> ssa_map)
       hsa_cfun.output_arg = XCNEW (hsa_symbol);
       fillup_sym_for_decl (DECL_RESULT (cfun->decl), hsa_cfun.output_arg);
       hsa_cfun.output_arg->segment = BRIG_SEGMENT_ARG;
-      hsa_cfun.output_arg->name = "output$param";
+      hsa_cfun.output_arg->linkage = BRIG_LINKAGE_FUNCTION;
+      hsa_cfun.output_arg->name = "res";
       slot = hsa_cfun.local_symbols->find_slot (hsa_cfun.output_arg, INSERT);
       gcc_assert (!*slot);
       *slot = hsa_cfun.output_arg;
     }
-  else
-    hsa_cfun.output_arg = NULL;
 }
 
 
@@ -1959,6 +2157,10 @@ generate_hsa (void)
 
   hsa_init_data_for_cfun ();
 
+  bool kern_p = lookup_attribute ("hsakernel",
+    DECL_ATTRIBUTES (current_function_decl));
+  hsa_cfun.kern_p = kern_p;
+
   ssa_map.safe_grow_cleared (SSANAMES (cfun)->length ());
   hsa_cfun.name
     = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME 
(current_function_decl)));
@@ -1967,6 +2169,13 @@ generate_hsa (void)
   if (seen_error ())
     goto fail;
   gen_body_from_gimple (ssa_map);
+
+#ifdef ENABLE_CHECKING
+  for (unsigned i = 0; i < ssa_map.length (); i++)
+    if (ssa_map[i])
+      ssa_map[i]->verify ();
+#endif
+
   if (seen_error ())
     goto fail;
   ssa_map.release ();
@@ -2109,6 +2318,7 @@ wrap_hsa (void)
            str = build_string_literal (1, "");
            bool kern_p = lookup_attribute ("hsakernel",
                                            DECL_ATTRIBUTES (fndecl));
+           hsa_cfun.kern_p = kern_p;
            if (!in_lto_p && main_input_filename)
              {
                char *filename;
@@ -2309,6 +2519,8 @@ unsigned int
 pass_gen_hsail::execute (function *)
 {
   if (lookup_attribute ("hsa", DECL_ATTRIBUTES (current_function_decl))
+      || lookup_attribute ("hsafunc",
+                          DECL_ATTRIBUTES (current_function_decl))
       || lookup_attribute ("hsakernel",
                           DECL_ATTRIBUTES (current_function_decl)))
     return generate_hsa ();
diff --git a/gcc/hsa.h b/gcc/hsa.h
index 16fb95f..0cce181 100644
--- a/gcc/hsa.h
+++ b/gcc/hsa.h
@@ -57,6 +57,9 @@ struct hsa_symbol
   /* The HSA segment this will eventually end up in.  */
   BrigSegment8_t segment;
 
+  /* The HSA kind of linkage.  */
+  BrigLinkage8_t linkage;
+
   /* Array dimensions, if non-zero.  */
   uint32_t dimLo, dimHi;
 };
@@ -102,6 +105,14 @@ is_a_helper <hsa_op_immed *>::test (hsa_op_base *p)
 
 struct hsa_op_reg : public hsa_op_base
 {
+  /* Destructor.  */
+  ~hsa_op_reg ()
+  {
+  }
+
+  /* Verify register operand.  */
+  void verify ();
+
   /* If NON-NULL, gimple SSA that we come from.  NULL if none.
      !!? Do we need it? */
   tree gimple_ssa;
@@ -165,25 +176,43 @@ is_a_helper <hsa_op_address *>::test (hsa_op_base *p)
   return p->kind == BRIG_KIND_OPERAND_ADDRESS;
 }
 
-/* A reference-to-label HSA operand.  In reality this is a reference to a start
-   of a BB.  */
+/* A reference to code HSA operand. It can be either reference
+   to a start of a BB or a start of a function.  */
 
-struct hsa_op_label : public hsa_op_base
+struct hsa_op_code_ref : public hsa_op_base
 {
-  /* Offset in the code section that this label refers to.  */
+  /* Offset in the code section that this refers to.  */
   unsigned directive_offset;
 };
 
-/* Report whether or not P is a label reference operand.  */
+/* Report whether or not P is a code reference operand.  */
 
 template <>
 template <>
 inline bool
-is_a_helper <hsa_op_label *>::test (hsa_op_base *p)
+is_a_helper <hsa_op_code_ref *>::test (hsa_op_base *p)
 {
   return p->kind == BRIG_KIND_OPERAND_CODE_REF;
 }
 
+/* Code list HSA operand.  */
+struct hsa_op_code_list: public hsa_op_base
+{
+  /* Offset to variable-sized array in hsa_data section, where
+     are offsets to entries in the hsa_code section.  */
+  vec<unsigned> offsets;
+};
+
+/* Report whether or not P is a code list operand.  */
+
+template <>
+template <>
+inline bool
+is_a_helper <hsa_op_code_list *>::test (hsa_op_base *p)
+{
+  return p->kind == BRIG_KIND_OPERAND_CODE_LIST;
+}
+
 #define HSA_OPERANDS_PER_INSN 5
 
 /* Class representing an HSA instruction.  Unlike typical ancestors for
@@ -211,6 +240,7 @@ struct hsa_insn_basic
 };
 
 #define HSA_OPCODE_PHI -1
+#define HSA_OPCODE_CALL_BLOCK -2
 
 /* Structure representing a PHI node of the SSA form of HSA virtual
    registers.  */
@@ -367,6 +397,73 @@ is_a_helper <hsa_insn_seg *>::test (hsa_insn_basic *p)
          || p->opcode == BRIG_OPCODE_FTOS);
 }
 
+/* HSA instruction for function call.  */
+
+struct hsa_insn_call: hsa_insn_basic
+{
+  /* Called function */
+  tree called_function;
+
+  /* Called function code reference.  */
+  struct hsa_op_code_ref func;
+
+  /* Argument symbols.  */
+  vec <hsa_symbol *> args_symbols;
+
+  /* Code list for arguments of the function.  */
+  hsa_op_code_list *args_code_list;
+
+  /* Result symbol.  */
+  hsa_symbol *result_symbol;
+
+  /* Code list for result of the function.  */
+  hsa_op_code_list *result_code_list;
+};
+
+/* Report whether or not P is a call instruction.  */
+
+template <>
+template <>
+inline bool
+is_a_helper <hsa_insn_call *>::test (hsa_insn_basic *p)
+{
+  return (p->opcode == BRIG_OPCODE_CALL);
+}
+
+/* HSA call instruction block encapsulates definition of arguments,
+   result type, corresponding loads and a possible store.
+   Moreover, it contains a single call instruction.
+   Emission of the instruction will produce multiple
+   HSAIL instructions.  */
+
+struct hsa_insn_call_block: hsa_insn_basic
+{
+  /* Input formal arguments.  */
+  vec <hsa_symbol *> input_args;
+
+  /* Input arguments store instructions.  */
+  vec <hsa_insn_mem *> input_arg_insns;
+
+  /* Output argument, can be NULL for void functions.  */
+  hsa_symbol *output_arg;
+
+  /* Output argument load instruction.  */
+  hsa_insn_mem *output_arg_insn;
+
+  /* Call isntruction.  */
+  hsa_insn_call *call_insn;
+};
+
+/* Report whether or not P is a call block instruction.  */
+
+template <>
+template <>
+inline bool
+is_a_helper <hsa_insn_call_block *>::test (hsa_insn_basic *p)
+{
+  return (p->opcode == HSA_OPCODE_CALL_BLOCK);
+}
+
 /* Basic block of HSA instructions.  */
 
 struct hsa_bb
@@ -375,7 +472,7 @@ struct hsa_bb
   basic_block bb;
 
   /* The operand that referes to the label to this BB.  */
-  hsa_op_label label_ref;
+  hsa_op_code_ref label_ref;
 
   /* The first and last instruction.  */
   struct hsa_insn_basic *first_insn, *last_insn;
@@ -480,6 +577,9 @@ struct hsa_function_representation
 
   /* Whether or not we could check and enforce SSA properties.  */
   bool in_ssa;
+
+  /* True if the function is kernel function.  */
+  bool kern_p;
 };
 
 /* In hsa-gen.c.  */
-- 
2.1.2


Reply via email to