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