| /* Producing binary form of HSA BRIG from our internal representation. |
| Copyright (C) 2013-2019 Free Software Foundation, Inc. |
| Contributed by Martin Jambor <mjambor@suse.cz> and |
| Martin Liska <mliska@suse.cz>. |
| |
| 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 "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "target.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "is-a.h" |
| #include "vec.h" |
| #include "hash-table.h" |
| #include "hash-map.h" |
| #include "tree.h" |
| #include "tree-iterator.h" |
| #include "stor-layout.h" |
| #include "output.h" |
| #include "basic-block.h" |
| #include "function.h" |
| #include "cfg.h" |
| #include "fold-const.h" |
| #include "stringpool.h" |
| #include "gimple-pretty-print.h" |
| #include "diagnostic-core.h" |
| #include "cgraph.h" |
| #include "dumpfile.h" |
| #include "print-tree.h" |
| #include "symbol-summary.h" |
| #include "hsa-common.h" |
| #include "gomp-constants.h" |
| |
| /* Convert VAL to little endian form, if necessary. */ |
| |
| static uint16_t |
| lendian16 (uint16_t val) |
| { |
| #if GCC_VERSION >= 4008 |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| return val; |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| return __builtin_bswap16 (val); |
| #else /* __ORDER_PDP_ENDIAN__ */ |
| return val; |
| #endif |
| #else |
| // provide a safe slower default, with shifts and masking |
| #ifndef WORDS_BIGENDIAN |
| return val; |
| #else |
| return (val >> 8) | (val << 8); |
| #endif |
| #endif |
| } |
| |
| /* Convert VAL to little endian form, if necessary. */ |
| |
| static uint32_t |
| lendian32 (uint32_t val) |
| { |
| #if GCC_VERSION >= 4006 |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| return val; |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| return __builtin_bswap32 (val); |
| #else /* __ORDER_PDP_ENDIAN__ */ |
| return (val >> 16) | (val << 16); |
| #endif |
| #else |
| // provide a safe slower default, with shifts and masking |
| #ifndef WORDS_BIGENDIAN |
| return val; |
| #else |
| val = ((val & 0xff00ff00) >> 8) | ((val & 0xff00ff) << 8); |
| return (val >> 16) | (val << 16); |
| #endif |
| #endif |
| } |
| |
| /* Convert VAL to little endian form, if necessary. */ |
| |
| static uint64_t |
| lendian64 (uint64_t val) |
| { |
| #if GCC_VERSION >= 4006 |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| return val; |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| return __builtin_bswap64 (val); |
| #else /* __ORDER_PDP_ENDIAN__ */ |
| return (((val & 0xffffll) << 48) |
| | ((val & 0xffff0000ll) << 16) |
| | ((val & 0xffff00000000ll) >> 16) |
| | ((val & 0xffff000000000000ll) >> 48)); |
| #endif |
| #else |
| // provide a safe slower default, with shifts and masking |
| #ifndef WORDS_BIGENDIAN |
| return val; |
| #else |
| val = (((val & 0xff00ff00ff00ff00ll) >> 8) |
| | ((val & 0x00ff00ff00ff00ffll) << 8)); |
| val = ((( val & 0xffff0000ffff0000ll) >> 16) |
| | (( val & 0x0000ffff0000ffffll) << 16)); |
| return (val >> 32) | (val << 32); |
| #endif |
| #endif |
| } |
| |
| #define BRIG_ELF_SECTION_NAME ".brig" |
| #define BRIG_LABEL_STRING "hsa_brig" |
| #define BRIG_SECTION_DATA_NAME "hsa_data" |
| #define BRIG_SECTION_CODE_NAME "hsa_code" |
| #define BRIG_SECTION_OPERAND_NAME "hsa_operand" |
| |
| #define BRIG_CHUNK_MAX_SIZE (64 * 1024) |
| |
| /* Required HSA section alignment. */ |
| |
| #define HSA_SECTION_ALIGNMENT 16 |
| |
| /* Chunks of BRIG binary data. */ |
| |
| struct hsa_brig_data_chunk |
| { |
| /* Size of the data already stored into a chunk. */ |
| unsigned size; |
| |
| /* Pointer to the data. */ |
| char *data; |
| }; |
| |
| /* Structure representing a BRIG section, holding and writing its data. */ |
| |
| class hsa_brig_section |
| { |
| public: |
| /* Section name that will be output to the BRIG. */ |
| const char *section_name; |
| /* Size in bytes of all data stored in the section. */ |
| unsigned total_size; |
| /* The size of the header of the section including padding. */ |
| unsigned header_byte_count; |
| /* The size of the header of the section without any padding. */ |
| unsigned header_byte_delta; |
| |
| void init (const char *name); |
| void release (); |
| void output (); |
| unsigned add (const void *data, unsigned len, void **output = NULL); |
| void round_size_up (int factor); |
| void *get_ptr_by_offset (unsigned int offset); |
| |
| private: |
| void allocate_new_chunk (); |
| |
| /* Buffers of binary data, each containing BRIG_CHUNK_MAX_SIZE bytes. */ |
| vec <struct hsa_brig_data_chunk> chunks; |
| |
| /* More convenient access to the last chunk from the vector above. */ |
| struct hsa_brig_data_chunk *cur_chunk; |
| }; |
| |
| 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; |
| |
| /* Hash map of emitted function declarations. */ |
| static hash_map <tree, BrigDirectiveExecutable *> *emitted_declarations; |
| |
| /* Hash table of emitted internal function declaration offsets. */ |
| hash_table <hsa_internal_fn_hasher> *hsa_emitted_internal_decls; |
| |
| /* List of sbr instructions. */ |
| static vec <hsa_insn_sbr *> *switch_instructions; |
| |
| 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 |
| hsa_brig_section::allocate_new_chunk () |
| { |
| struct hsa_brig_data_chunk new_chunk; |
| |
| new_chunk.data = XCNEWVEC (char, BRIG_CHUNK_MAX_SIZE); |
| new_chunk.size = 0; |
| cur_chunk = chunks.safe_push (new_chunk); |
| } |
| |
| /* Initialize the brig section. */ |
| |
| void |
| hsa_brig_section::init (const char *name) |
| { |
| section_name = name; |
| /* While the following computation is basically wrong, because the intent |
| certainly wasn't to have the first character of name and padding, which |
| are a part of sizeof (BrigSectionHeader), included in the first addend, |
| this is what the disassembler expects. */ |
| total_size = sizeof (BrigSectionHeader) + strlen (section_name); |
| chunks.create (1); |
| allocate_new_chunk (); |
| header_byte_delta = total_size; |
| round_size_up (4); |
| header_byte_count = total_size; |
| } |
| |
| /* Free all data in the section. */ |
| |
| void |
| hsa_brig_section::release () |
| { |
| for (unsigned i = 0; i < chunks.length (); i++) |
| free (chunks[i].data); |
| chunks.release (); |
| cur_chunk = NULL; |
| } |
| |
| /* Write the section to the output file to a section with the name given at |
| initialization. Switches the output section and does not restore it. */ |
| |
| void |
| hsa_brig_section::output () |
| { |
| struct BrigSectionHeader section_header; |
| char padding[8]; |
| |
| section_header.byteCount = lendian64 (total_size); |
| section_header.headerByteCount = lendian32 (header_byte_count); |
| section_header.nameLength = lendian32 (strlen (section_name)); |
| assemble_string ((const char *) §ion_header, 16); |
| assemble_string (section_name, (section_header.nameLength)); |
| memset (&padding, 0, sizeof (padding)); |
| /* This is also a consequence of the wrong header size computation described |
| in a comment in hsa_brig_section::init. */ |
| assemble_string (padding, 8); |
| for (unsigned i = 0; i < chunks.length (); i++) |
| assemble_string (chunks[i].data, chunks[i].size); |
| } |
| |
| /* Add to the stream LEN bytes of opaque binary DATA. Return the offset at |
| which it was stored. If OUTPUT is not NULL, store into it the pointer to |
| the place where DATA was actually stored. */ |
| |
| unsigned |
| hsa_brig_section::add (const void *data, unsigned len, void **output) |
| { |
| unsigned offset = total_size; |
| |
| gcc_assert (len <= BRIG_CHUNK_MAX_SIZE); |
| if (cur_chunk->size > (BRIG_CHUNK_MAX_SIZE - len)) |
| allocate_new_chunk (); |
| |
| char *dst = cur_chunk->data + cur_chunk->size; |
| memcpy (dst, data, len); |
| if (output) |
| *output = dst; |
| cur_chunk->size += len; |
| total_size += len; |
| |
| return offset; |
| } |
| |
| /* Add padding to section so that its size is divisible by FACTOR. */ |
| |
| void |
| hsa_brig_section::round_size_up (int factor) |
| { |
| unsigned padding, res = total_size % factor; |
| |
| if (res == 0) |
| return; |
| |
| padding = factor - res; |
| total_size += padding; |
| if (cur_chunk->size > (BRIG_CHUNK_MAX_SIZE - padding)) |
| { |
| padding -= BRIG_CHUNK_MAX_SIZE - cur_chunk->size; |
| cur_chunk->size = BRIG_CHUNK_MAX_SIZE; |
| allocate_new_chunk (); |
| } |
| |
| 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_delta; |
| |
| unsigned 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 |
| { |
| const char *s; |
| char prefix; |
| int len; |
| uint32_t offset; |
| }; |
| |
| /* Hash table helpers. */ |
| |
| struct brig_string_slot_hasher : pointer_hash <brig_string_slot> |
| { |
| static inline hashval_t hash (const value_type); |
| static inline bool equal (const value_type, const compare_type); |
| static inline void remove (value_type); |
| }; |
| |
| /* Returns a hash code for DS. Adapted from libiberty's htab_hash_string |
| to support strings that may not end in '\0'. */ |
| |
| inline hashval_t |
| brig_string_slot_hasher::hash (const value_type ds) |
| { |
| hashval_t r = ds->len; |
| int i; |
| |
| for (i = 0; i < ds->len; i++) |
| r = r * 67 + (unsigned) ds->s[i] - 113; |
| r = r * 67 + (unsigned) ds->prefix - 113; |
| return r; |
| } |
| |
| /* Returns nonzero if DS1 and DS2 are equal. */ |
| |
| inline bool |
| brig_string_slot_hasher::equal (const value_type ds1, const compare_type ds2) |
| { |
| if (ds1->len == ds2->len) |
| return ds1->prefix == ds2->prefix |
| && memcmp (ds1->s, ds2->s, ds1->len) == 0; |
| |
| return 0; |
| } |
| |
| /* Deallocate memory for DS upon its removal. */ |
| |
| inline void |
| brig_string_slot_hasher::remove (value_type ds) |
| { |
| free (const_cast<char *> (ds->s)); |
| free (ds); |
| } |
| |
| /* Hash for strings we output in order not to duplicate them needlessly. */ |
| |
| static hash_table<brig_string_slot_hasher> *brig_string_htab; |
| |
| /* Emit a null terminated string STR to the data section and return its |
| offset in it. If PREFIX is non-zero, output it just before STR too. |
| Sanitize the string if SANITIZE option is set to true. */ |
| |
| static unsigned |
| brig_emit_string (const char *str, char prefix = 0, bool sanitize = true) |
| { |
| unsigned slen = strlen (str); |
| unsigned offset, len = slen + (prefix ? 1 : 0); |
| uint32_t hdr_len = lendian32 (len); |
| brig_string_slot s_slot; |
| brig_string_slot **slot; |
| char *str2; |
| |
| str2 = xstrdup (str); |
| |
| if (sanitize) |
| hsa_sanitize_name (str2); |
| s_slot.s = str2; |
| s_slot.len = slen; |
| s_slot.prefix = prefix; |
| s_slot.offset = 0; |
| |
| slot = brig_string_htab->find_slot (&s_slot, INSERT); |
| if (*slot == NULL) |
| { |
| brig_string_slot *new_slot = XCNEW (brig_string_slot); |
| |
| /* In theory we should fill in BrigData but that would mean copying |
| the string to a buffer for no reason, so we just emulate it. */ |
| offset = brig_data.add (&hdr_len, sizeof (hdr_len)); |
| if (prefix) |
| brig_data.add (&prefix, 1); |
| |
| brig_data.add (str2, slen); |
| brig_data.round_size_up (4); |
| |
| /* TODO: could use the string we just copied into |
| brig_string->cur_chunk */ |
| new_slot->s = str2; |
| new_slot->len = slen; |
| new_slot->prefix = prefix; |
| new_slot->offset = offset; |
| *slot = new_slot; |
| } |
| else |
| { |
| offset = (*slot)->offset; |
| free (str2); |
| } |
| |
| return offset; |
| } |
| |
| /* Linked list of queued operands. */ |
| |
| static struct operand_queue |
| { |
| /* First from the chain of queued operands. */ |
| hsa_op_base *first_op, *last_op; |
| |
| /* The offset at which the next operand will be enqueued. */ |
| unsigned projected_size; |
| |
| } op_queue; |
| |
| /* Unless already initialized, initialize infrastructure to produce BRIG. */ |
| |
| static void |
| brig_init (void) |
| { |
| brig_insn_count = 0; |
| |
| if (brig_initialized) |
| return; |
| |
| brig_string_htab = new hash_table<brig_string_slot_hasher> (37); |
| brig_data.init (BRIG_SECTION_DATA_NAME); |
| brig_code.init (BRIG_SECTION_CODE_NAME); |
| brig_operand.init (BRIG_SECTION_OPERAND_NAME); |
| brig_initialized = true; |
| |
| struct BrigDirectiveModule moddir; |
| memset (&moddir, 0, sizeof (moddir)); |
| moddir.base.byteCount = lendian16 (sizeof (moddir)); |
| |
| char *modname; |
| if (main_input_filename && *main_input_filename != '\0') |
| { |
| const char *part = strrchr (main_input_filename, '/'); |
| if (!part) |
| part = main_input_filename; |
| else |
| part++; |
| modname = concat ("&__hsa_module_", part, NULL); |
| char *extension = strchr (modname, '.'); |
| if (extension) |
| *extension = '\0'; |
| |
| /* As in LTO mode, we have to emit a different module names. */ |
| if (flag_ltrans) |
| { |
| part = strrchr (asm_file_name, '/'); |
| if (!part) |
| part = asm_file_name; |
| else |
| part++; |
| char *modname2; |
| modname2 = xasprintf ("%s_%s", modname, part); |
| free (modname); |
| modname = modname2; |
| } |
| |
| hsa_sanitize_name (modname); |
| moddir.name = brig_emit_string (modname); |
| free (modname); |
| } |
| else |
| moddir.name = brig_emit_string ("__hsa_module_unnamed", '&'); |
| moddir.base.kind = lendian16 (BRIG_KIND_DIRECTIVE_MODULE); |
| moddir.hsailMajor = lendian32 (BRIG_VERSION_HSAIL_MAJOR); |
| moddir.hsailMinor = lendian32 (BRIG_VERSION_HSAIL_MINOR); |
| moddir.profile = hsa_full_profile_p () ? BRIG_PROFILE_FULL: BRIG_PROFILE_BASE; |
| if (hsa_machine_large_p ()) |
| moddir.machineModel = BRIG_MACHINE_LARGE; |
| else |
| moddir.machineModel = BRIG_MACHINE_SMALL; |
| moddir.defaultFloatRound = BRIG_ROUND_FLOAT_DEFAULT; |
| brig_code.add (&moddir, sizeof (moddir)); |
| } |
| |
| /* Free all BRIG data. */ |
| |
| static void |
| brig_release_data (void) |
| { |
| delete brig_string_htab; |
| brig_data.release (); |
| brig_code.release (); |
| brig_operand.release (); |
| |
| brig_initialized = 0; |
| } |
| |
| /* Enqueue operation OP. Return the offset at which it will be stored. */ |
| |
| static unsigned int |
| enqueue_op (hsa_op_base *op) |
| { |
| unsigned ret; |
| |
| if (op->m_brig_op_offset) |
| return op->m_brig_op_offset; |
| |
| ret = op_queue.projected_size; |
| op->m_brig_op_offset = op_queue.projected_size; |
| |
| if (!op_queue.first_op) |
| op_queue.first_op = op; |
| else |
| op_queue.last_op->m_next = op; |
| op_queue.last_op = op; |
| |
| if (is_a <hsa_op_immed *> (op)) |
| op_queue.projected_size += sizeof (struct BrigOperandConstantBytes); |
| else if (is_a <hsa_op_reg *> (op)) |
| op_queue.projected_size += sizeof (struct BrigOperandRegister); |
| else if (is_a <hsa_op_address *> (op)) |
| op_queue.projected_size += sizeof (struct BrigOperandAddress); |
| 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 if (is_a <hsa_op_operand_list *> (op)) |
| op_queue.projected_size += sizeof (struct BrigOperandOperandList); |
| else |
| gcc_unreachable (); |
| return ret; |
| } |
| |
| static void emit_immediate_operand (hsa_op_immed *imm); |
| |
| /* Emit directive describing a symbol if it has not been emitted already. |
| Return the offset of the directive. */ |
| |
| static unsigned |
| emit_directive_variable (struct hsa_symbol *symbol) |
| { |
| struct BrigDirectiveVariable dirvar; |
| unsigned name_offset; |
| static unsigned res_name_offset; |
| |
| if (symbol->m_directive_offset) |
| return symbol->m_directive_offset; |
| |
| memset (&dirvar, 0, sizeof (dirvar)); |
| dirvar.base.byteCount = lendian16 (sizeof (dirvar)); |
| dirvar.base.kind = lendian16 (BRIG_KIND_DIRECTIVE_VARIABLE); |
| dirvar.allocation = symbol->m_allocation; |
| |
| char prefix = symbol->m_global_scope_p ? '&' : '%'; |
| |
| if (symbol->m_decl && TREE_CODE (symbol->m_decl) == RESULT_DECL) |
| { |
| if (res_name_offset == 0) |
| res_name_offset = brig_emit_string (symbol->m_name, '%'); |
| name_offset = res_name_offset; |
| } |
| else if (symbol->m_name) |
| name_offset = brig_emit_string (symbol->m_name, prefix); |
| else |
| { |
| char buf[64]; |
| snprintf (buf, 64, "__%s_%i", hsa_seg_name (symbol->m_segment), |
| symbol->m_name_number); |
| name_offset = brig_emit_string (buf, prefix); |
| } |
| |
| dirvar.name = lendian32 (name_offset); |
| |
| if (symbol->m_decl && TREE_CODE (symbol->m_decl) == CONST_DECL) |
| { |
| hsa_op_immed *tmp = new hsa_op_immed (DECL_INITIAL (symbol->m_decl)); |
| dirvar.init = lendian32 (enqueue_op (tmp)); |
| } |
| else |
| dirvar.init = 0; |
| dirvar.type = lendian16 (symbol->m_type); |
| dirvar.segment = symbol->m_segment; |
| dirvar.align = symbol->m_align; |
| dirvar.linkage = symbol->m_linkage; |
| dirvar.dim.lo = symbol->m_dim; |
| dirvar.dim.hi = symbol->m_dim >> 32; |
| |
| /* Global variables are just declared and linked via HSA runtime. */ |
| if (symbol->m_linkage != BRIG_ALLOCATION_PROGRAM) |
| dirvar.modifier |= BRIG_VARIABLE_DEFINITION; |
| dirvar.reserved = 0; |
| |
| if (symbol->m_cst_value) |
| { |
| dirvar.modifier |= BRIG_VARIABLE_CONST; |
| dirvar.init = lendian32 (enqueue_op (symbol->m_cst_value)); |
| } |
| |
| symbol->m_directive_offset = brig_code.add (&dirvar, sizeof (dirvar)); |
| return symbol->m_directive_offset; |
| } |
| |
| /* Emit directives describing either a function declaration or definition F and |
| return the produced BrigDirectiveExecutable structure. The function does |
| not take into account any instructions when calculating nextModuleEntry |
| field of the produced BrigDirectiveExecutable structure so when emitting |
| actual definitions, this field needs to be updated after all of the function |
| is actually added to the code section. */ |
| |
| static BrigDirectiveExecutable * |
| emit_function_directives (hsa_function_representation *f, bool is_declaration) |
| { |
| struct BrigDirectiveExecutable fndir; |
| unsigned name_offset, inarg_off, scoped_off, next_toplev_off; |
| int count = 0; |
| void *ptr_to_fndir; |
| hsa_symbol *sym; |
| |
| if (!f->m_declaration_p) |
| for (int i = 0; f->m_global_symbols.iterate (i, &sym); i++) |
| { |
| gcc_assert (!sym->m_emitted_to_brig); |
| sym->m_emitted_to_brig = true; |
| emit_directive_variable (sym); |
| brig_insn_count++; |
| } |
| |
| name_offset = brig_emit_string (f->m_name, '&'); |
| inarg_off = brig_code.total_size + sizeof (fndir) |
| + (f->m_output_arg ? sizeof (struct BrigDirectiveVariable) : 0); |
| scoped_off = inarg_off |
| + f->m_input_args.length () * sizeof (struct BrigDirectiveVariable); |
| |
| if (!f->m_declaration_p) |
| { |
| count += f->m_spill_symbols.length (); |
| count += f->m_private_variables.length (); |
| } |
| |
| next_toplev_off = scoped_off + count * sizeof (struct BrigDirectiveVariable); |
| |
| memset (&fndir, 0, sizeof (fndir)); |
| fndir.base.byteCount = lendian16 (sizeof (fndir)); |
| fndir.base.kind = lendian16 (f->m_kern_p ? BRIG_KIND_DIRECTIVE_KERNEL |
| : BRIG_KIND_DIRECTIVE_FUNCTION); |
| fndir.name = lendian32 (name_offset); |
| fndir.inArgCount = lendian16 (f->m_input_args.length ()); |
| fndir.outArgCount = lendian16 (f->m_output_arg ? 1 : 0); |
| fndir.firstInArg = lendian32 (inarg_off); |
| fndir.firstCodeBlockEntry = lendian32 (scoped_off); |
| fndir.nextModuleEntry = lendian32 (next_toplev_off); |
| fndir.linkage = f->get_linkage (); |
| if (!f->m_declaration_p) |
| fndir.modifier |= BRIG_EXECUTABLE_DEFINITION; |
| memset (&fndir.reserved, 0, sizeof (fndir.reserved)); |
| |
| /* Once we put a definition of function_offsets, we should not overwrite |
| it with a declaration of the function. */ |
| if (f->m_internal_fn == NULL) |
| { |
| if (!function_offsets->get (f->m_decl) || !is_declaration) |
| function_offsets->put (f->m_decl, brig_code.total_size); |
| } |
| else |
| { |
| /* Internal function. */ |
| hsa_internal_fn **slot |
| = hsa_emitted_internal_decls->find_slot (f->m_internal_fn, INSERT); |
| hsa_internal_fn *int_fn = new hsa_internal_fn (f->m_internal_fn); |
| int_fn->m_offset = brig_code.total_size; |
| *slot = int_fn; |
| } |
| |
| brig_code.add (&fndir, sizeof (fndir), &ptr_to_fndir); |
| |
| if (f->m_output_arg) |
| emit_directive_variable (f->m_output_arg); |
| for (unsigned i = 0; i < f->m_input_args.length (); i++) |
| emit_directive_variable (f->m_input_args[i]); |
| |
| if (!f->m_declaration_p) |
| { |
| for (int i = 0; f->m_spill_symbols.iterate (i, &sym); i++) |
| { |
| emit_directive_variable (sym); |
| brig_insn_count++; |
| } |
| for (unsigned i = 0; i < f->m_private_variables.length (); i++) |
| { |
| emit_directive_variable (f->m_private_variables[i]); |
| brig_insn_count++; |
| } |
| } |
| |
| return (BrigDirectiveExecutable *) ptr_to_fndir; |
| } |
| |
| /* Emit a label directive for the given HBB. We assume it is about to start on |
| the current offset in the code section. */ |
| |
| static void |
| emit_bb_label_directive (hsa_bb *hbb) |
| { |
| struct BrigDirectiveLabel lbldir; |
| |
| lbldir.base.byteCount = lendian16 (sizeof (lbldir)); |
| lbldir.base.kind = lendian16 (BRIG_KIND_DIRECTIVE_LABEL); |
| char buf[32]; |
| snprintf (buf, 32, "BB_%u_%i", DECL_UID (current_function_decl), |
| hbb->m_index); |
| lbldir.name = lendian32 (brig_emit_string (buf, '@')); |
| |
| hbb->m_label_ref.m_directive_offset = brig_code.add (&lbldir, |
| sizeof (lbldir)); |
| brig_insn_count++; |
| } |
| |
| /* Map a normal HSAIL type to the type of the equivalent BRIG operand |
| holding such, for constants and registers. */ |
| |
| static BrigType16_t |
| regtype_for_type (BrigType16_t t) |
| { |
| switch (t) |
| { |
| case BRIG_TYPE_B1: |
| return BRIG_TYPE_B1; |
| |
| case BRIG_TYPE_U8: |
| case BRIG_TYPE_U16: |
| case BRIG_TYPE_U32: |
| case BRIG_TYPE_S8: |
| case BRIG_TYPE_S16: |
| case BRIG_TYPE_S32: |
| case BRIG_TYPE_B8: |
| case BRIG_TYPE_B16: |
| case BRIG_TYPE_B32: |
| case BRIG_TYPE_F16: |
| case BRIG_TYPE_F32: |
| case BRIG_TYPE_U8X4: |
| case BRIG_TYPE_U16X2: |
| case BRIG_TYPE_S8X4: |
| case BRIG_TYPE_S16X2: |
| case BRIG_TYPE_F16X2: |
| return BRIG_TYPE_B32; |
| |
| case BRIG_TYPE_U64: |
| case BRIG_TYPE_S64: |
| case BRIG_TYPE_F64: |
| case BRIG_TYPE_B64: |
| case BRIG_TYPE_U8X8: |
| case BRIG_TYPE_U16X4: |
| case BRIG_TYPE_U32X2: |
| case BRIG_TYPE_S8X8: |
| case BRIG_TYPE_S16X4: |
| case BRIG_TYPE_S32X2: |
| case BRIG_TYPE_F16X4: |
| case BRIG_TYPE_F32X2: |
| return BRIG_TYPE_B64; |
| |
| case BRIG_TYPE_B128: |
| case BRIG_TYPE_U8X16: |
| case BRIG_TYPE_U16X8: |
| case BRIG_TYPE_U32X4: |
| case BRIG_TYPE_U64X2: |
| case BRIG_TYPE_S8X16: |
| case BRIG_TYPE_S16X8: |
| case BRIG_TYPE_S32X4: |
| case BRIG_TYPE_S64X2: |
| case BRIG_TYPE_F16X8: |
| case BRIG_TYPE_F32X4: |
| case BRIG_TYPE_F64X2: |
| return BRIG_TYPE_B128; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Return the length of the BRIG type TYPE that is going to be streamed out as |
| an immediate constant (so it must not be B1). */ |
| |
| unsigned |
| hsa_get_imm_brig_type_len (BrigType16_t type) |
| { |
| BrigType16_t base_type = type & BRIG_TYPE_BASE_MASK; |
| BrigType16_t pack_type = type & BRIG_TYPE_PACK_MASK; |
| |
| switch (pack_type) |
| { |
| case BRIG_TYPE_PACK_NONE: |
| break; |
| case BRIG_TYPE_PACK_32: |
| return 4; |
| case BRIG_TYPE_PACK_64: |
| return 8; |
| case BRIG_TYPE_PACK_128: |
| return 16; |
| default: |
| gcc_unreachable (); |
| } |
| |
| switch (base_type) |
| { |
| case BRIG_TYPE_U8: |
| case BRIG_TYPE_S8: |
| case BRIG_TYPE_B8: |
| return 1; |
| case BRIG_TYPE_U16: |
| case BRIG_TYPE_S16: |
| case BRIG_TYPE_F16: |
| case BRIG_TYPE_B16: |
| return 2; |
| case BRIG_TYPE_U32: |
| case BRIG_TYPE_S32: |
| case BRIG_TYPE_F32: |
| case BRIG_TYPE_B32: |
| return 4; |
| case BRIG_TYPE_U64: |
| case BRIG_TYPE_S64: |
| case BRIG_TYPE_F64: |
| case BRIG_TYPE_B64: |
| return 8; |
| case BRIG_TYPE_B128: |
| return 16; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Emit one scalar VALUE to the buffer DATA intended for BRIG emission. |
| If NEED_LEN is not equal to zero, shrink or extend the value |
| to NEED_LEN bytes. Return how many bytes were written. */ |
| |
| static int |
| emit_immediate_scalar_to_buffer (tree value, char *data, unsigned need_len) |
| { |
| union hsa_bytes bytes; |
| |
| memset (&bytes, 0, sizeof (bytes)); |
| tree type = TREE_TYPE (value); |
| gcc_checking_assert (TREE_CODE (type) != VECTOR_TYPE); |
| |
| unsigned data_len = tree_to_uhwi (TYPE_SIZE (type)) / BITS_PER_UNIT; |
| if (INTEGRAL_TYPE_P (type) |
| || (POINTER_TYPE_P (type) && TREE_CODE (value) == INTEGER_CST)) |
| switch (data_len) |
| { |
| case 1: |
| bytes.b8 = (uint8_t) TREE_INT_CST_LOW (value); |
| break; |
| case 2: |
| bytes.b16 = (uint16_t) TREE_INT_CST_LOW (value); |
| break; |
| case 4: |
| bytes.b32 = (uint32_t) TREE_INT_CST_LOW (value); |
| break; |
| case 8: |
| bytes.b64 = (uint64_t) TREE_INT_CST_LOW (value); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| else if (SCALAR_FLOAT_TYPE_P (type)) |
| { |
| if (data_len == 2) |
| { |
| sorry ("Support for HSA does not implement immediate 16 bit FPU " |
| "operands"); |
| return 2; |
| } |
| unsigned int_len = GET_MODE_SIZE (SCALAR_FLOAT_TYPE_MODE (type)); |
| /* There are always 32 bits in each long, no matter the size of |
| the hosts long. */ |
| long tmp[6]; |
| |
| real_to_target (tmp, TREE_REAL_CST_PTR (value), TYPE_MODE (type)); |
| |
| if (int_len == 4) |
| bytes.b32 = (uint32_t) tmp[0]; |
| else |
| { |
| bytes.b64 = (uint64_t)(uint32_t) tmp[1]; |
| bytes.b64 <<= 32; |
| bytes.b64 |= (uint32_t) tmp[0]; |
| } |
| } |
| else |
| gcc_unreachable (); |
| |
| int len; |
| if (need_len == 0) |
| len = data_len; |
| else |
| len = need_len; |
| |
| memcpy (data, &bytes, len); |
| return len; |
| } |
| |
| char * |
| hsa_op_immed::emit_to_buffer (unsigned *brig_repr_size) |
| { |
| char *brig_repr; |
| *brig_repr_size = hsa_get_imm_brig_type_len (m_type); |
| |
| if (m_tree_value != NULL_TREE) |
| { |
| /* Update brig_repr_size for special tree values. */ |
| if (TREE_CODE (m_tree_value) == STRING_CST) |
| *brig_repr_size = TREE_STRING_LENGTH (m_tree_value); |
| else if (TREE_CODE (m_tree_value) == CONSTRUCTOR) |
| *brig_repr_size |
| = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (m_tree_value))); |
| |
| unsigned total_len = *brig_repr_size; |
| |
| /* As we can have a constructor with fewer elements, fill the memory |
| with zeros. */ |
| brig_repr = XCNEWVEC (char, total_len); |
| char *p = brig_repr; |
| |
| if (TREE_CODE (m_tree_value) == VECTOR_CST) |
| { |
| /* Variable-length vectors aren't supported. */ |
| int i, num = VECTOR_CST_NELTS (m_tree_value).to_constant (); |
| for (i = 0; i < num; i++) |
| { |
| tree v = VECTOR_CST_ELT (m_tree_value, i); |
| unsigned actual = emit_immediate_scalar_to_buffer (v, p, 0); |
| total_len -= actual; |
| p += actual; |
| } |
| /* Vectors should have the exact size. */ |
| gcc_assert (total_len == 0); |
| } |
| else if (TREE_CODE (m_tree_value) == STRING_CST) |
| memcpy (brig_repr, TREE_STRING_POINTER (m_tree_value), |
| TREE_STRING_LENGTH (m_tree_value)); |
| else if (TREE_CODE (m_tree_value) == COMPLEX_CST) |
| { |
| gcc_assert (total_len % 2 == 0); |
| unsigned actual; |
| actual |
| = emit_immediate_scalar_to_buffer (TREE_REALPART (m_tree_value), p, |
| total_len / 2); |
| |
| gcc_assert (actual == total_len / 2); |
| p += actual; |
| |
| actual |
| = emit_immediate_scalar_to_buffer (TREE_IMAGPART (m_tree_value), p, |
| total_len / 2); |
| gcc_assert (actual == total_len / 2); |
| } |
| else if (TREE_CODE (m_tree_value) == CONSTRUCTOR) |
| { |
| unsigned len = CONSTRUCTOR_NELTS (m_tree_value); |
| for (unsigned i = 0; i < len; i++) |
| { |
| tree v = CONSTRUCTOR_ELT (m_tree_value, i)->value; |
| unsigned actual = emit_immediate_scalar_to_buffer (v, p, 0); |
| total_len -= actual; |
| p += actual; |
| } |
| } |
| else |
| emit_immediate_scalar_to_buffer (m_tree_value, p, total_len); |
| } |
| else |
| { |
| hsa_bytes bytes; |
| |
| switch (*brig_repr_size) |
| { |
| case 1: |
| bytes.b8 = (uint8_t) m_int_value; |
| break; |
| case 2: |
| bytes.b16 = (uint16_t) m_int_value; |
| break; |
| case 4: |
| bytes.b32 = (uint32_t) m_int_value; |
| break; |
| case 8: |
| bytes.b64 = (uint64_t) m_int_value; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| brig_repr = XNEWVEC (char, *brig_repr_size); |
| memcpy (brig_repr, &bytes, *brig_repr_size); |
| } |
| |
| return brig_repr; |
| } |
| |
| /* Emit an immediate BRIG operand IMM. The BRIG type of the immediate might |
| have been massaged to comply with various HSA/BRIG type requirements, so the |
| only important aspect of that is the length (because HSAIL might expect |
| smaller constants or become bit-data). The data should be represented |
| according to what is in the tree representation. */ |
| |
| static void |
| emit_immediate_operand (hsa_op_immed *imm) |
| { |
| unsigned brig_repr_size; |
| char *brig_repr = imm->emit_to_buffer (&brig_repr_size); |
| struct BrigOperandConstantBytes out; |
| |
| memset (&out, 0, sizeof (out)); |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_CONSTANT_BYTES); |
| uint32_t byteCount = lendian32 (brig_repr_size); |
| out.type = lendian16 (imm->m_type); |
| out.bytes = lendian32 (brig_data.add (&byteCount, sizeof (byteCount))); |
| brig_operand.add (&out, sizeof (out)); |
| brig_data.add (brig_repr, brig_repr_size); |
| brig_data.round_size_up (4); |
| |
| free (brig_repr); |
| } |
| |
| /* Emit a register BRIG operand REG. */ |
| |
| static void |
| emit_register_operand (hsa_op_reg *reg) |
| { |
| struct BrigOperandRegister out; |
| |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_REGISTER); |
| out.regNum = lendian32 (reg->m_hard_num); |
| |
| switch (regtype_for_type (reg->m_type)) |
| { |
| case BRIG_TYPE_B32: |
| out.regKind = BRIG_REGISTER_KIND_SINGLE; |
| break; |
| case BRIG_TYPE_B64: |
| out.regKind = BRIG_REGISTER_KIND_DOUBLE; |
| break; |
| case BRIG_TYPE_B128: |
| out.regKind = BRIG_REGISTER_KIND_QUAD; |
| break; |
| case BRIG_TYPE_B1: |
| out.regKind = BRIG_REGISTER_KIND_CONTROL; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| brig_operand.add (&out, sizeof (out)); |
| } |
| |
| /* Emit an address BRIG operand ADDR. */ |
| |
| static void |
| emit_address_operand (hsa_op_address *addr) |
| { |
| struct BrigOperandAddress out; |
| |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_ADDRESS); |
| out.symbol = addr->m_symbol |
| ? lendian32 (emit_directive_variable (addr->m_symbol)) : 0; |
| out.reg = addr->m_reg ? lendian32 (enqueue_op (addr->m_reg)) : 0; |
| |
| if (sizeof (addr->m_imm_offset) == 8) |
| { |
| out.offset.lo = lendian32 (addr->m_imm_offset); |
| out.offset.hi = lendian32 (addr->m_imm_offset >> 32); |
| } |
| else |
| { |
| gcc_assert (sizeof (addr->m_imm_offset) == 4); |
| out.offset.lo = lendian32 (addr->m_imm_offset); |
| out.offset.hi = 0; |
| } |
| |
| brig_operand.add (&out, sizeof (out)); |
| } |
| |
| /* Emit a code reference operand REF. */ |
| |
| static void |
| emit_code_ref_operand (hsa_op_code_ref *ref) |
| { |
| struct BrigOperandCodeRef out; |
| |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_CODE_REF); |
| out.ref = lendian32 (ref->m_directive_offset); |
| brig_operand.add (&out, sizeof (out)); |
| } |
| |
| /* Emit a code list operand CODE_LIST. */ |
| |
| static void |
| emit_code_list_operand (hsa_op_code_list *code_list) |
| { |
| struct BrigOperandCodeList out; |
| unsigned args = code_list->m_offsets.length (); |
| |
| for (unsigned i = 0; i < args; i++) |
| gcc_assert (code_list->m_offsets[i]); |
| |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_CODE_LIST); |
| |
| uint32_t byteCount = lendian32 (4 * args); |
| |
| out.elements = lendian32 (brig_data.add (&byteCount, sizeof (byteCount))); |
| brig_data.add (code_list->m_offsets.address (), args * sizeof (uint32_t)); |
| brig_data.round_size_up (4); |
| brig_operand.add (&out, sizeof (out)); |
| } |
| |
| /* Emit an operand list operand OPERAND_LIST. */ |
| |
| static void |
| emit_operand_list_operand (hsa_op_operand_list *operand_list) |
| { |
| struct BrigOperandOperandList out; |
| unsigned args = operand_list->m_offsets.length (); |
| |
| for (unsigned i = 0; i < args; i++) |
| gcc_assert (operand_list->m_offsets[i]); |
| |
| out.base.byteCount = lendian16 (sizeof (out)); |
| out.base.kind = lendian16 (BRIG_KIND_OPERAND_OPERAND_LIST); |
| |
| uint32_t byteCount = lendian32 (4 * args); |
| |
| out.elements = lendian32 (brig_data.add (&byteCount, sizeof (byteCount))); |
| brig_data.add (operand_list->m_offsets.address (), args * sizeof (uint32_t)); |
| brig_data.round_size_up (4); |
| brig_operand.add (&out, sizeof (out)); |
| } |
| |
| /* Emit all operands queued for writing. */ |
| |
| static void |
| emit_queued_operands (void) |
| { |
| for (hsa_op_base *op = op_queue.first_op; op; op = op->m_next) |
| { |
| gcc_assert (op->m_brig_op_offset == brig_operand.total_size); |
| if (hsa_op_immed *imm = dyn_cast <hsa_op_immed *> (op)) |
| emit_immediate_operand (imm); |
| else if (hsa_op_reg *reg = dyn_cast <hsa_op_reg *> (op)) |
| emit_register_operand (reg); |
| else if (hsa_op_address *addr = dyn_cast <hsa_op_address *> (op)) |
| emit_address_operand (addr); |
| 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 if (hsa_op_operand_list *l = dyn_cast <hsa_op_operand_list *> (op)) |
| emit_operand_list_operand (l); |
| else |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Emit directives describing the function that is used for |
| a function declaration. */ |
| |
| static BrigDirectiveExecutable * |
| emit_function_declaration (tree decl) |
| { |
| hsa_function_representation *f = hsa_generate_function_declaration (decl); |
| |
| BrigDirectiveExecutable *e = emit_function_directives (f, true); |
| emit_queued_operands (); |
| |
| delete f; |
| |
| return e; |
| } |
| |
| /* Emit directives describing the function that is used for |
| an internal function declaration. */ |
| |
| static BrigDirectiveExecutable * |
| emit_internal_fn_decl (hsa_internal_fn *fn) |
| { |
| hsa_function_representation *f = hsa_generate_internal_fn_decl (fn); |
| |
| BrigDirectiveExecutable *e = emit_function_directives (f, true); |
| emit_queued_operands (); |
| |
| delete f; |
| |
| return e; |
| } |
| |
| /* Enqueue all operands of INSN and return offset to BRIG data section |
| to list of operand offsets. */ |
| |
| static unsigned |
| emit_insn_operands (hsa_insn_basic *insn) |
| { |
| auto_vec<BrigOperandOffset32_t, HSA_BRIG_INT_STORAGE_OPERANDS> |
| operand_offsets; |
| |
| unsigned l = insn->operand_count (); |
| |
| /* We have N operands so use 4 * N for the byte_count. */ |
| uint32_t byte_count = lendian32 (4 * l); |
| unsigned offset = brig_data.add (&byte_count, sizeof (byte_count)); |
| if (l > 0) |
| { |
| operand_offsets.safe_grow (l); |
| for (unsigned i = 0; i < l; i++) |
| operand_offsets[i] = lendian32 (enqueue_op (insn->get_op (i))); |
| |
| brig_data.add (operand_offsets.address (), |
| l * sizeof (BrigOperandOffset32_t)); |
| } |
| brig_data.round_size_up (4); |
| return offset; |
| } |
| |
| /* Enqueue operand OP0, OP1, OP2 (if different from NULL) and return offset |
| to BRIG data section to list of operand offsets. */ |
| |
| static unsigned |
| emit_operands (hsa_op_base *op0, hsa_op_base *op1 = NULL, |
| hsa_op_base *op2 = NULL) |
| { |
| auto_vec<BrigOperandOffset32_t, HSA_BRIG_INT_STORAGE_OPERANDS> |
| operand_offsets; |
| |
| gcc_checking_assert (op0 != NULL); |
| operand_offsets.safe_push (enqueue_op (op0)); |
| |
| if (op1 != NULL) |
| { |
| operand_offsets.safe_push (enqueue_op (op1)); |
| if (op2 != NULL) |
| operand_offsets.safe_push (enqueue_op (op2)); |
| } |
| |
| unsigned l = operand_offsets.length (); |
| |
| /* We have N operands so use 4 * N for the byte_count. */ |
| uint32_t byte_count = lendian32 (4 * l); |
| |
| unsigned offset = brig_data.add (&byte_count, sizeof (byte_count)); |
| brig_data.add (operand_offsets.address (), |
| l * sizeof (BrigOperandOffset32_t)); |
| |
| brig_data.round_size_up (4); |
| |
| return offset; |
| } |
| |
| /* Emit an HSA memory instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_memory_insn (hsa_insn_mem *mem) |
| { |
| struct BrigInstMem repr; |
| gcc_checking_assert (mem->operand_count () == 2); |
| |
| hsa_op_address *addr = as_a <hsa_op_address *> (mem->get_op (1)); |
| |
| /* This is necessary because of the erroneous typedef of |
| BrigMemoryModifier8_t which introduces padding which may then contain |
| random stuff (which we do not want so that we can test things don't |
| change). */ |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_MEM); |
| repr.base.opcode = lendian16 (mem->m_opcode); |
| repr.base.type = lendian16 (mem->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (mem)); |
| |
| if (addr->m_symbol) |
| repr.segment = addr->m_symbol->m_segment; |
| else |
| repr.segment = BRIG_SEGMENT_FLAT; |
| repr.modifier = 0; |
| repr.equivClass = mem->m_equiv_class; |
| repr.align = mem->m_align; |
| if (mem->m_opcode == BRIG_OPCODE_LD) |
| repr.width = BRIG_WIDTH_1; |
| else |
| repr.width = BRIG_WIDTH_NONE; |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA signal memory instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_signal_insn (hsa_insn_signal *mem) |
| { |
| struct BrigInstSignal repr; |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_SIGNAL); |
| repr.base.opcode = lendian16 (mem->m_opcode); |
| repr.base.type = lendian16 (mem->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (mem)); |
| |
| repr.memoryOrder = mem->m_memory_order; |
| repr.signalOperation = mem->m_signalop; |
| repr.signalType = hsa_machine_large_p () ? BRIG_TYPE_SIG64 : BRIG_TYPE_SIG32; |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA atomic memory instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_atomic_insn (hsa_insn_atomic *mem) |
| { |
| struct BrigInstAtomic repr; |
| |
| /* Either operand[0] or operand[1] must be an address operand. */ |
| hsa_op_address *addr = NULL; |
| if (is_a <hsa_op_address *> (mem->get_op (0))) |
| addr = as_a <hsa_op_address *> (mem->get_op (0)); |
| else |
| addr = as_a <hsa_op_address *> (mem->get_op (1)); |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_ATOMIC); |
| repr.base.opcode = lendian16 (mem->m_opcode); |
| repr.base.type = lendian16 (mem->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (mem)); |
| |
| if (addr->m_symbol) |
| repr.segment = addr->m_symbol->m_segment; |
| else |
| repr.segment = BRIG_SEGMENT_FLAT; |
| repr.memoryOrder = mem->m_memoryorder; |
| repr.memoryScope = mem->m_memoryscope; |
| repr.atomicOperation = mem->m_atomicop; |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA LDA instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_addr_insn (hsa_insn_basic *insn) |
| { |
| struct BrigInstAddr repr; |
| |
| hsa_op_address *addr = as_a <hsa_op_address *> (insn->get_op (1)); |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_ADDR); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| repr.base.type = lendian16 (insn->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (insn)); |
| |
| if (addr->m_symbol) |
| repr.segment = addr->m_symbol->m_segment; |
| else |
| repr.segment = BRIG_SEGMENT_FLAT; |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA segment conversion instruction and all necessary directives, |
| schedule necessary operands for writing. */ |
| |
| static void |
| emit_segment_insn (hsa_insn_seg *seg) |
| { |
| struct BrigInstSegCvt repr; |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_SEG_CVT); |
| repr.base.opcode = lendian16 (seg->m_opcode); |
| repr.base.type = lendian16 (seg->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (seg)); |
| repr.sourceType = lendian16 (as_a <hsa_op_reg *> (seg->get_op (1))->m_type); |
| repr.segment = seg->m_segment; |
| repr.modifier = 0; |
| |
| brig_code.add (&repr, sizeof (repr)); |
| |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA alloca instruction and all necessary directives, |
| schedule necessary operands for writing. */ |
| |
| static void |
| emit_alloca_insn (hsa_insn_alloca *alloca) |
| { |
| struct BrigInstMem repr; |
| gcc_checking_assert (alloca->operand_count () == 2); |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_MEM); |
| repr.base.opcode = lendian16 (alloca->m_opcode); |
| repr.base.type = lendian16 (alloca->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (alloca)); |
| repr.segment = BRIG_SEGMENT_PRIVATE; |
| repr.modifier = 0; |
| repr.equivClass = 0; |
| repr.align = alloca->m_align; |
| repr.width = BRIG_WIDTH_NONE; |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA comparison instruction and all necessary directives, |
| schedule necessary operands for writing. */ |
| |
| static void |
| emit_cmp_insn (hsa_insn_cmp *cmp) |
| { |
| struct BrigInstCmp repr; |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_CMP); |
| repr.base.opcode = lendian16 (cmp->m_opcode); |
| repr.base.type = lendian16 (cmp->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (cmp)); |
| |
| if (is_a <hsa_op_reg *> (cmp->get_op (1))) |
| repr.sourceType |
| = lendian16 (as_a <hsa_op_reg *> (cmp->get_op (1))->m_type); |
| else |
| repr.sourceType |
| = lendian16 (as_a <hsa_op_immed *> (cmp->get_op (1))->m_type); |
| repr.modifier = 0; |
| repr.compare = cmp->m_compare; |
| repr.pack = 0; |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA generic branching/sycnronization instruction. */ |
| |
| static void |
| emit_generic_branch_insn (hsa_insn_br *br) |
| { |
| struct BrigInstBr repr; |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BR); |
| repr.base.opcode = lendian16 (br->m_opcode); |
| repr.width = br->m_width; |
| repr.base.type = lendian16 (br->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (br)); |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA conditional branching instruction and all necessary directives, |
| schedule necessary operands for writing. */ |
| |
| static void |
| emit_cond_branch_insn (hsa_insn_cbr *br) |
| { |
| struct BrigInstBr repr; |
| |
| basic_block target = NULL; |
| edge_iterator ei; |
| edge e; |
| |
| /* At the moment we only handle direct conditional jumps. */ |
| gcc_assert (br->m_opcode == BRIG_OPCODE_CBR); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BR); |
| repr.base.opcode = lendian16 (br->m_opcode); |
| repr.width = br->m_width; |
| /* For Conditional jumps the type is always B1. */ |
| repr.base.type = lendian16 (BRIG_TYPE_B1); |
| |
| FOR_EACH_EDGE (e, ei, br->m_bb->succs) |
| if (e->flags & EDGE_TRUE_VALUE) |
| { |
| target = e->dest; |
| break; |
| } |
| gcc_assert (target); |
| |
| repr.base.operands |
| = lendian32 (emit_operands (br->get_op (0), |
| &hsa_bb_for_bb (target)->m_label_ref)); |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA unconditional jump branching instruction that points to |
| a label REFERENCE. */ |
| |
| static void |
| emit_unconditional_jump (hsa_op_code_ref *reference) |
| { |
| struct BrigInstBr repr; |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BR); |
| repr.base.opcode = lendian16 (BRIG_OPCODE_BR); |
| repr.base.type = lendian16 (BRIG_TYPE_NONE); |
| /* Direct branches to labels must be width(all). */ |
| repr.width = BRIG_WIDTH_ALL; |
| |
| repr.base.operands = lendian32 (emit_operands (reference)); |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA switch jump instruction that uses a jump table to |
| jump to a destination label. */ |
| |
| static void |
| emit_switch_insn (hsa_insn_sbr *sbr) |
| { |
| struct BrigInstBr repr; |
| |
| gcc_assert (sbr->m_opcode == BRIG_OPCODE_SBR); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BR); |
| repr.base.opcode = lendian16 (sbr->m_opcode); |
| repr.width = BRIG_WIDTH_1; |
| /* For Conditional jumps the type is always B1. */ |
| hsa_op_reg *index = as_a <hsa_op_reg *> (sbr->get_op (0)); |
| repr.base.type = lendian16 (index->m_type); |
| repr.base.operands |
| = lendian32 (emit_operands (sbr->get_op (0), sbr->m_label_code_list)); |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit a HSA convert instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_cvt_insn (hsa_insn_cvt *insn) |
| { |
| struct BrigInstCvt repr; |
| BrigType16_t srctype; |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_CVT); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| repr.base.type = lendian16 (insn->m_type); |
| repr.base.operands = lendian32 (emit_insn_operands (insn)); |
| |
| if (is_a <hsa_op_reg *> (insn->get_op (1))) |
| srctype = as_a <hsa_op_reg *> (insn->get_op (1))->m_type; |
| else |
| srctype = as_a <hsa_op_immed *> (insn->get_op (1))->m_type; |
| repr.sourceType = lendian16 (srctype); |
| repr.modifier = 0; |
| /* float to smaller float requires a rounding setting (we default |
| to 'near'. */ |
| if (hsa_type_float_p (insn->m_type) |
| && (!hsa_type_float_p (srctype) |
| || ((insn->m_type & BRIG_TYPE_BASE_MASK) |
| < (srctype & BRIG_TYPE_BASE_MASK)))) |
| repr.round = BRIG_ROUND_FLOAT_NEAR_EVEN; |
| else if (hsa_type_integer_p (insn->m_type) && |
| hsa_type_float_p (srctype)) |
| repr.round = BRIG_ROUND_INTEGER_ZERO; |
| else |
| repr.round = BRIG_ROUND_NONE; |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit call instruction INSN, where this instruction must be closed |
| within a call block instruction. */ |
| |
| static void |
| emit_call_insn (hsa_insn_call *call) |
| { |
| struct BrigInstBr repr; |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BR); |
| repr.base.opcode = lendian16 (BRIG_OPCODE_CALL); |
| repr.base.type = lendian16 (BRIG_TYPE_NONE); |
| |
| repr.base.operands |
| = lendian32 (emit_operands (call->m_result_code_list, &call->m_func, |
| call->m_args_code_list)); |
| |
| /* Internal functions have not set m_called_function. */ |
| if (call->m_called_function) |
| { |
| function_linkage_pair pair (call->m_called_function, |
| call->m_func.m_brig_op_offset); |
| function_call_linkage.safe_push (pair); |
| } |
| else |
| { |
| hsa_internal_fn *slot |
| = hsa_emitted_internal_decls->find (call->m_called_internal_fn); |
| gcc_assert (slot); |
| gcc_assert (slot->m_offset > 0); |
| call->m_func.m_directive_offset = slot->m_offset; |
| } |
| |
| repr.width = BRIG_WIDTH_ALL; |
| memset (&repr.reserved, 0, sizeof (repr.reserved)); |
| |
| brig_code.add (&repr, sizeof (repr)); |
| brig_insn_count++; |
| } |
| |
| /* Emit argument block directive. */ |
| |
| static void |
| emit_arg_block_insn (hsa_insn_arg_block *insn) |
| { |
| switch (insn->m_kind) |
| { |
| case BRIG_KIND_DIRECTIVE_ARG_BLOCK_START: |
| { |
| struct BrigDirectiveArgBlock repr; |
| repr.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.kind = lendian16 (insn->m_kind); |
| brig_code.add (&repr, sizeof (repr)); |
| |
| for (unsigned i = 0; i < insn->m_call_insn->m_input_args.length (); i++) |
| { |
| insn->m_call_insn->m_args_code_list->m_offsets[i] |
| = lendian32 (emit_directive_variable |
| (insn->m_call_insn->m_input_args[i])); |
| brig_insn_count++; |
| } |
| |
| if (insn->m_call_insn->m_output_arg) |
| { |
| insn->m_call_insn->m_result_code_list->m_offsets[0] |
| = lendian32 (emit_directive_variable |
| (insn->m_call_insn->m_output_arg)); |
| brig_insn_count++; |
| } |
| |
| break; |
| } |
| case BRIG_KIND_DIRECTIVE_ARG_BLOCK_END: |
| { |
| struct BrigDirectiveArgBlock repr; |
| repr.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.kind = lendian16 (insn->m_kind); |
| brig_code.add (&repr, sizeof (repr)); |
| break; |
| } |
| default: |
| gcc_unreachable (); |
| } |
| |
| brig_insn_count++; |
| } |
| |
| /* Emit comment directive. */ |
| |
| static void |
| emit_comment_insn (hsa_insn_comment *insn) |
| { |
| struct BrigDirectiveComment repr; |
| memset (&repr, 0, sizeof (repr)); |
| |
| repr.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.kind = lendian16 (insn->m_opcode); |
| repr.name = brig_emit_string (insn->m_comment, '\0', false); |
| brig_code.add (&repr, sizeof (repr)); |
| } |
| |
| /* Emit queue instruction INSN. */ |
| |
| static void |
| emit_queue_insn (hsa_insn_queue *insn) |
| { |
| BrigInstQueue repr; |
| memset (&repr, 0, sizeof (repr)); |
| |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_QUEUE); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| repr.base.type = lendian16 (insn->m_type); |
| repr.segment = insn->m_segment; |
| repr.memoryOrder = insn->m_memory_order; |
| repr.base.operands = lendian32 (emit_insn_operands (insn)); |
| brig_data.round_size_up (4); |
| brig_code.add (&repr, sizeof (repr)); |
| |
| brig_insn_count++; |
| } |
| |
| /* Emit source type instruction INSN. */ |
| |
| static void |
| emit_srctype_insn (hsa_insn_srctype *insn) |
| { |
| /* We assume that BrigInstMod has a BrigInstBasic prefix. */ |
| struct BrigInstSourceType repr; |
| unsigned operand_count = insn->operand_count (); |
| gcc_checking_assert (operand_count >= 2); |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.sourceType = lendian16 (insn->m_source_type); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_SOURCE_TYPE); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| repr.base.type = lendian16 (insn->m_type); |
| |
| repr.base.operands = lendian32 (emit_insn_operands (insn)); |
| brig_code.add (&repr, sizeof (struct BrigInstSourceType)); |
| brig_insn_count++; |
| } |
| |
| /* Emit packed instruction INSN. */ |
| |
| static void |
| emit_packed_insn (hsa_insn_packed *insn) |
| { |
| /* We assume that BrigInstMod has a BrigInstBasic prefix. */ |
| struct BrigInstSourceType repr; |
| unsigned operand_count = insn->operand_count (); |
| gcc_checking_assert (operand_count >= 2); |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.sourceType = lendian16 (insn->m_source_type); |
| repr.base.base.byteCount = lendian16 (sizeof (repr)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_SOURCE_TYPE); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| repr.base.type = lendian16 (insn->m_type); |
| |
| if (insn->m_opcode == BRIG_OPCODE_COMBINE) |
| { |
| /* Create operand list for packed type. */ |
| for (unsigned i = 1; i < operand_count; i++) |
| { |
| gcc_checking_assert (insn->get_op (i)); |
| insn->m_operand_list->m_offsets[i - 1] |
| = lendian32 (enqueue_op (insn->get_op (i))); |
| } |
| |
| repr.base.operands = lendian32 (emit_operands (insn->get_op (0), |
| insn->m_operand_list)); |
| } |
| else if (insn->m_opcode == BRIG_OPCODE_EXPAND) |
| { |
| /* Create operand list for packed type. */ |
| for (unsigned i = 0; i < operand_count - 1; i++) |
| { |
| gcc_checking_assert (insn->get_op (i)); |
| insn->m_operand_list->m_offsets[i] |
| = lendian32 (enqueue_op (insn->get_op (i))); |
| } |
| |
| unsigned ops = emit_operands (insn->m_operand_list, |
| insn->get_op (insn->operand_count () - 1)); |
| repr.base.operands = lendian32 (ops); |
| } |
| |
| |
| brig_code.add (&repr, sizeof (struct BrigInstSourceType)); |
| brig_insn_count++; |
| } |
| |
| /* Emit a basic HSA instruction and all necessary directives, schedule |
| necessary operands for writing. */ |
| |
| static void |
| emit_basic_insn (hsa_insn_basic *insn) |
| { |
| /* We assume that BrigInstMod has a BrigInstBasic prefix. */ |
| struct BrigInstMod repr; |
| BrigType16_t type; |
| |
| memset (&repr, 0, sizeof (repr)); |
| repr.base.base.byteCount = lendian16 (sizeof (BrigInstBasic)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_BASIC); |
| repr.base.opcode = lendian16 (insn->m_opcode); |
| switch (insn->m_opcode) |
| { |
| /* And the bit-logical operations need bit types and whine about |
| arithmetic types :-/ */ |
| case BRIG_OPCODE_AND: |
| case BRIG_OPCODE_OR: |
| case BRIG_OPCODE_XOR: |
| case BRIG_OPCODE_NOT: |
| type = regtype_for_type (insn->m_type); |
| break; |
| default: |
| type = insn->m_type; |
| break; |
| } |
| repr.base.type = lendian16 (type); |
| repr.base.operands = lendian32 (emit_insn_operands (insn)); |
| |
| if (hsa_type_packed_p (type)) |
| { |
| if (hsa_type_float_p (type) |
| && !hsa_opcode_floating_bit_insn_p (insn->m_opcode)) |
| repr.round = BRIG_ROUND_FLOAT_NEAR_EVEN; |
| else |
| repr.round = 0; |
| /* We assume that destination and sources agree in packing layout. */ |
| if (insn->num_used_ops () >= 2) |
| repr.pack = BRIG_PACK_PP; |
| else |
| repr.pack = BRIG_PACK_P; |
| repr.reserved = 0; |
| repr.base.base.byteCount = lendian16 (sizeof (BrigInstMod)); |
| repr.base.base.kind = lendian16 (BRIG_KIND_INST_MOD); |
| brig_code.add (&repr, sizeof (struct BrigInstMod)); |
| } |
| else |
| brig_code.add (&repr, sizeof (struct BrigInstBasic)); |
| brig_insn_count++; |
| } |
| |
| /* Emit an HSA instruction and all necessary directives, schedule necessary |
| operands for writing. */ |
| |
| static void |
| emit_insn (hsa_insn_basic *insn) |
| { |
| gcc_assert (!is_a <hsa_insn_phi *> (insn)); |
| |
| insn->m_brig_offset = brig_code.total_size; |
| |
| if (hsa_insn_signal *signal = dyn_cast <hsa_insn_signal *> (insn)) |
| emit_signal_insn (signal); |
| else if (hsa_insn_atomic *atom = dyn_cast <hsa_insn_atomic *> (insn)) |
| emit_atomic_insn (atom); |
| else if (hsa_insn_mem *mem = dyn_cast <hsa_insn_mem *> (insn)) |
| emit_memory_insn (mem); |
| else if (insn->m_opcode == BRIG_OPCODE_LDA) |
| emit_addr_insn (insn); |
| else if (hsa_insn_seg *seg = dyn_cast <hsa_insn_seg *> (insn)) |
| emit_segment_insn (seg); |
| else if (hsa_insn_cmp *cmp = dyn_cast <hsa_insn_cmp *> (insn)) |
| emit_cmp_insn (cmp); |
| else if (hsa_insn_cbr *br = dyn_cast <hsa_insn_cbr *> (insn)) |
| emit_cond_branch_insn (br); |
| else if (hsa_insn_sbr *sbr = dyn_cast <hsa_insn_sbr *> (insn)) |
| { |
| if (switch_instructions == NULL) |
| switch_instructions = new vec <hsa_insn_sbr *> (); |
| |
| switch_instructions->safe_push (sbr); |
| emit_switch_insn (sbr); |
| } |
| else if (hsa_insn_br *br = dyn_cast <hsa_insn_br *> (insn)) |
| emit_generic_branch_insn (br); |
| else if (hsa_insn_arg_block *block = dyn_cast <hsa_insn_arg_block *> (insn)) |
| emit_arg_block_insn (block); |
| else if (hsa_insn_call *call = dyn_cast <hsa_insn_call *> (insn)) |
| emit_call_insn (call); |
| else if (hsa_insn_comment *comment = dyn_cast <hsa_insn_comment *> (insn)) |
| emit_comment_insn (comment); |
| else if (hsa_insn_queue *queue = dyn_cast <hsa_insn_queue *> (insn)) |
| emit_queue_insn (queue); |
| else if (hsa_insn_srctype *srctype = dyn_cast <hsa_insn_srctype *> (insn)) |
| emit_srctype_insn (srctype); |
| else if (hsa_insn_packed *packed = dyn_cast <hsa_insn_packed *> (insn)) |
| emit_packed_insn (packed); |
| else if (hsa_insn_cvt *cvt = dyn_cast <hsa_insn_cvt *> (insn)) |
| emit_cvt_insn (cvt); |
| else if (hsa_insn_alloca *alloca = dyn_cast <hsa_insn_alloca *> (insn)) |
| emit_alloca_insn (alloca); |
| else |
| emit_basic_insn (insn); |
| } |
| |
| /* We have just finished emitting BB and are about to emit NEXT_BB if non-NULL, |
| or we are about to finish emitting code, if it is NULL. If the fall through |
| edge from BB does not lead to NEXT_BB, emit an unconditional jump. */ |
| |
| static void |
| perhaps_emit_branch (basic_block bb, basic_block next_bb) |
| { |
| basic_block t_bb = NULL, ff = NULL; |
| |
| edge_iterator ei; |
| edge e; |
| |
| /* If the last instruction of BB is a switch, ignore emission of all |
| edges. */ |
| if (hsa_bb_for_bb (bb)->m_last_insn |
| && is_a <hsa_insn_sbr *> (hsa_bb_for_bb (bb)->m_last_insn)) |
| return; |
| |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| if (e->flags & EDGE_TRUE_VALUE) |
| { |
| gcc_assert (!t_bb); |
| t_bb = e->dest; |
| } |
| else |
| { |
| gcc_assert (!ff); |
| ff = e->dest; |
| } |
| |
| if (!ff || ff == next_bb || ff == EXIT_BLOCK_PTR_FOR_FN (cfun)) |
| return; |
| |
| emit_unconditional_jump (&hsa_bb_for_bb (ff)->m_label_ref); |
| } |
| |
| /* Emit the a function with name NAME to the various brig sections. */ |
| |
| void |
| hsa_brig_emit_function (void) |
| { |
| basic_block bb, prev_bb; |
| hsa_insn_basic *insn; |
| BrigDirectiveExecutable *ptr_to_fndir; |
| |
| brig_init (); |
| |
| brig_insn_count = 0; |
| memset (&op_queue, 0, sizeof (op_queue)); |
| op_queue.projected_size = brig_operand.total_size; |
| |
| if (!function_offsets) |
| function_offsets = new hash_map<tree, BrigCodeOffset32_t> (); |
| |
| if (!emitted_declarations) |
| emitted_declarations = new hash_map <tree, BrigDirectiveExecutable *> (); |
| |
| for (unsigned i = 0; i < hsa_cfun->m_called_functions.length (); i++) |
| { |
| tree called = hsa_cfun->m_called_functions[i]; |
| |
| /* If the function has no definition, emit a declaration. */ |
| if (!emitted_declarations->get (called)) |
| { |
| BrigDirectiveExecutable *e = emit_function_declaration (called); |
| emitted_declarations->put (called, e); |
| } |
| } |
| |
| for (unsigned i = 0; i < hsa_cfun->m_called_internal_fns.length (); i++) |
| { |
| hsa_internal_fn *called = hsa_cfun->m_called_internal_fns[i]; |
| emit_internal_fn_decl (called); |
| } |
| |
| ptr_to_fndir = emit_function_directives (hsa_cfun, false); |
| for (insn = hsa_bb_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun))->m_first_insn; |
| insn; |
| insn = insn->m_next) |
| emit_insn (insn); |
| prev_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| perhaps_emit_branch (prev_bb, bb); |
| emit_bb_label_directive (hsa_bb_for_bb (bb)); |
| for (insn = hsa_bb_for_bb (bb)->m_first_insn; insn; insn = insn->m_next) |
| emit_insn (insn); |
| prev_bb = bb; |
| } |
| perhaps_emit_branch (prev_bb, NULL); |
| ptr_to_fndir->nextModuleEntry = lendian32 (brig_code.total_size); |
| |
| /* Fill up label references for all sbr instructions. */ |
| if (switch_instructions) |
| { |
| for (unsigned i = 0; i < switch_instructions->length (); i++) |
| { |
| hsa_insn_sbr *sbr = (*switch_instructions)[i]; |
| for (unsigned j = 0; j < sbr->m_jump_table.length (); j++) |
| { |
| hsa_bb *hbb = hsa_bb_for_bb (sbr->m_jump_table[j]); |
| sbr->m_label_code_list->m_offsets[j] |
| = hbb->m_label_ref.m_directive_offset; |
| } |
| } |
| |
| switch_instructions->release (); |
| delete switch_instructions; |
| switch_instructions = NULL; |
| } |
| |
| if (dump_file) |
| { |
| fprintf (dump_file, "------- After BRIG emission: -------\n"); |
| dump_hsa_cfun (dump_file); |
| } |
| |
| emit_queued_operands (); |
| } |
| |
| /* Emit all OMP symbols related to OMP. */ |
| |
| void |
| hsa_brig_emit_omp_symbols (void) |
| { |
| brig_init (); |
| emit_directive_variable (hsa_num_threads); |
| } |
| |
| /* Create and return __hsa_global_variables symbol that contains |
| all informations consumed by libgomp to link global variables |
| with their string names used by an HSA kernel. */ |
| |
| static tree |
| hsa_output_global_variables () |
| { |
| unsigned l = hsa_global_variable_symbols->elements (); |
| |
| tree variable_info_type = make_node (RECORD_TYPE); |
| tree id_f1 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("name"), ptr_type_node); |
| DECL_CHAIN (id_f1) = NULL_TREE; |
| tree id_f2 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("omp_data_size"), |
| ptr_type_node); |
| DECL_CHAIN (id_f2) = id_f1; |
| finish_builtin_struct (variable_info_type, "__hsa_variable_info", id_f2, |
| NULL_TREE); |
| |
| tree int_num_of_global_vars; |
| int_num_of_global_vars = build_int_cst (uint32_type_node, l); |
| tree global_vars_num_index_type = build_index_type (int_num_of_global_vars); |
| tree global_vars_array_type = build_array_type (variable_info_type, |
| global_vars_num_index_type); |
| TYPE_ARTIFICIAL (global_vars_array_type) = 1; |
| |
| vec<constructor_elt, va_gc> *global_vars_vec = NULL; |
| |
| for (hash_table <hsa_noop_symbol_hasher>::iterator it |
| = hsa_global_variable_symbols->begin (); |
| it != hsa_global_variable_symbols->end (); ++it) |
| { |
| unsigned len = strlen ((*it)->m_name); |
| char *copy = XNEWVEC (char, len + 2); |
| copy[0] = '&'; |
| memcpy (copy + 1, (*it)->m_name, len); |
| copy[len + 1] = '\0'; |
| len++; |
| hsa_sanitize_name (copy); |
| |
| tree var_name = build_string (len, copy); |
| TREE_TYPE (var_name) |
| = build_array_type (char_type_node, build_index_type (size_int (len))); |
| free (copy); |
| |
| vec<constructor_elt, va_gc> *variable_info_vec = NULL; |
| CONSTRUCTOR_APPEND_ELT (variable_info_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type (TREE_TYPE (var_name)), |
| var_name)); |
| CONSTRUCTOR_APPEND_ELT (variable_info_vec, NULL_TREE, |
| build_fold_addr_expr ((*it)->m_decl)); |
| |
| tree variable_info_ctor = build_constructor (variable_info_type, |
| variable_info_vec); |
| |
| CONSTRUCTOR_APPEND_ELT (global_vars_vec, NULL_TREE, |
| variable_info_ctor); |
| } |
| |
| tree global_vars_ctor = build_constructor (global_vars_array_type, |
| global_vars_vec); |
| |
| char tmp_name[64]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_global_variables", 1); |
| tree global_vars_table = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| global_vars_array_type); |
| TREE_STATIC (global_vars_table) = 1; |
| TREE_READONLY (global_vars_table) = 1; |
| TREE_PUBLIC (global_vars_table) = 0; |
| DECL_ARTIFICIAL (global_vars_table) = 1; |
| DECL_IGNORED_P (global_vars_table) = 1; |
| DECL_EXTERNAL (global_vars_table) = 0; |
| TREE_CONSTANT (global_vars_table) = 1; |
| DECL_INITIAL (global_vars_table) = global_vars_ctor; |
| varpool_node::finalize_decl (global_vars_table); |
| |
| return global_vars_table; |
| } |
| |
| /* Create __hsa_host_functions and __hsa_kernels that contain |
| all informations consumed by libgomp to register all kernels |
| in the BRIG binary. */ |
| |
| static void |
| hsa_output_kernels (tree *host_func_table, tree *kernels) |
| { |
| unsigned map_count = hsa_get_number_decl_kernel_mappings (); |
| |
| tree int_num_of_kernels; |
| int_num_of_kernels = build_int_cst (uint32_type_node, map_count); |
| tree kernel_num_index_type = build_index_type (int_num_of_kernels); |
| tree host_functions_array_type = build_array_type (ptr_type_node, |
| kernel_num_index_type); |
| TYPE_ARTIFICIAL (host_functions_array_type) = 1; |
| |
| vec<constructor_elt, va_gc> *host_functions_vec = NULL; |
| for (unsigned i = 0; i < map_count; ++i) |
| { |
| tree decl = hsa_get_decl_kernel_mapping_decl (i); |
| tree host_fn = build_fold_addr_expr (hsa_get_host_function (decl)); |
| CONSTRUCTOR_APPEND_ELT (host_functions_vec, NULL_TREE, host_fn); |
| } |
| tree host_functions_ctor = build_constructor (host_functions_array_type, |
| host_functions_vec); |
| char tmp_name[64]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_host_functions", 1); |
| tree hsa_host_func_table = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| host_functions_array_type); |
| TREE_STATIC (hsa_host_func_table) = 1; |
| TREE_READONLY (hsa_host_func_table) = 1; |
| TREE_PUBLIC (hsa_host_func_table) = 0; |
| DECL_ARTIFICIAL (hsa_host_func_table) = 1; |
| DECL_IGNORED_P (hsa_host_func_table) = 1; |
| DECL_EXTERNAL (hsa_host_func_table) = 0; |
| TREE_CONSTANT (hsa_host_func_table) = 1; |
| DECL_INITIAL (hsa_host_func_table) = host_functions_ctor; |
| varpool_node::finalize_decl (hsa_host_func_table); |
| *host_func_table = hsa_host_func_table; |
| |
| /* Following code emits list of kernel_info structures. */ |
| |
| tree kernel_info_type = make_node (RECORD_TYPE); |
| tree id_f1 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("name"), ptr_type_node); |
| DECL_CHAIN (id_f1) = NULL_TREE; |
| tree id_f2 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("omp_data_size"), |
| unsigned_type_node); |
| DECL_CHAIN (id_f2) = id_f1; |
| tree id_f3 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("gridified_kernel_p"), |
| boolean_type_node); |
| DECL_CHAIN (id_f3) = id_f2; |
| tree id_f4 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("kernel_dependencies_count"), |
| unsigned_type_node); |
| DECL_CHAIN (id_f4) = id_f3; |
| tree id_f5 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("kernel_dependencies"), |
| build_pointer_type (build_pointer_type |
| (char_type_node))); |
| DECL_CHAIN (id_f5) = id_f4; |
| finish_builtin_struct (kernel_info_type, "__hsa_kernel_info", id_f5, |
| NULL_TREE); |
| |
| int_num_of_kernels = build_int_cstu (uint32_type_node, map_count); |
| tree kernel_info_vector_type |
| = build_array_type (kernel_info_type, |
| build_index_type (int_num_of_kernels)); |
| TYPE_ARTIFICIAL (kernel_info_vector_type) = 1; |
| |
| vec<constructor_elt, va_gc> *kernel_info_vector_vec = NULL; |
| tree kernel_dependencies_vector_type = NULL; |
| |
| for (unsigned i = 0; i < map_count; ++i) |
| { |
| tree kernel = hsa_get_decl_kernel_mapping_decl (i); |
| char *name = hsa_get_decl_kernel_mapping_name (i); |
| unsigned len = strlen (name); |
| char *copy = XNEWVEC (char, len + 2); |
| copy[0] = '&'; |
| memcpy (copy + 1, name, len); |
| copy[len + 1] = '\0'; |
| len++; |
| |
| tree kern_name = build_string (len, copy); |
| TREE_TYPE (kern_name) |
| = build_array_type (char_type_node, build_index_type (size_int (len))); |
| free (copy); |
| |
| unsigned omp_size = hsa_get_decl_kernel_mapping_omp_size (i); |
| tree omp_data_size = build_int_cstu (unsigned_type_node, omp_size); |
| bool gridified_kernel_p = hsa_get_decl_kernel_mapping_gridified (i); |
| tree gridified_kernel_p_tree = build_int_cstu (boolean_type_node, |
| gridified_kernel_p); |
| unsigned count = 0; |
| vec<constructor_elt, va_gc> *kernel_dependencies_vec = NULL; |
| if (hsa_decl_kernel_dependencies) |
| { |
| vec<const char *> **slot; |
| slot = hsa_decl_kernel_dependencies->get (kernel); |
| if (slot) |
| { |
| vec <const char *> *dependencies = *slot; |
| count = dependencies->length (); |
| |
| kernel_dependencies_vector_type |
| = build_array_type (build_pointer_type (char_type_node), |
| build_index_type (size_int (count))); |
| TYPE_ARTIFICIAL (kernel_dependencies_vector_type) = 1; |
| |
| for (unsigned j = 0; j < count; j++) |
| { |
| const char *d = (*dependencies)[j]; |
| len = strlen (d); |
| tree dependency_name = build_string (len, d); |
| TREE_TYPE (dependency_name) |
| = build_array_type (char_type_node, |
| build_index_type (size_int (len))); |
| |
| CONSTRUCTOR_APPEND_ELT |
| (kernel_dependencies_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type (TREE_TYPE (dependency_name)), |
| dependency_name)); |
| } |
| } |
| } |
| |
| tree dependencies_count = build_int_cstu (unsigned_type_node, count); |
| |
| vec<constructor_elt, va_gc> *kernel_info_vec = NULL; |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type (TREE_TYPE |
| (kern_name)), |
| kern_name)); |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, omp_data_size); |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, |
| gridified_kernel_p_tree); |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, dependencies_count); |
| |
| if (count > 0) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_dependencies_list", i); |
| gcc_checking_assert (kernel_dependencies_vector_type); |
| tree dependencies_list = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| kernel_dependencies_vector_type); |
| |
| TREE_STATIC (dependencies_list) = 1; |
| TREE_READONLY (dependencies_list) = 1; |
| TREE_PUBLIC (dependencies_list) = 0; |
| DECL_ARTIFICIAL (dependencies_list) = 1; |
| DECL_IGNORED_P (dependencies_list) = 1; |
| DECL_EXTERNAL (dependencies_list) = 0; |
| TREE_CONSTANT (dependencies_list) = 1; |
| DECL_INITIAL (dependencies_list) |
| = build_constructor (kernel_dependencies_vector_type, |
| kernel_dependencies_vec); |
| varpool_node::finalize_decl (dependencies_list); |
| |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type |
| (TREE_TYPE (dependencies_list)), |
| dependencies_list)); |
| } |
| else |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vec, NULL_TREE, null_pointer_node); |
| |
| tree kernel_info_ctor = build_constructor (kernel_info_type, |
| kernel_info_vec); |
| |
| CONSTRUCTOR_APPEND_ELT (kernel_info_vector_vec, NULL_TREE, |
| kernel_info_ctor); |
| } |
| |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_kernels", 1); |
| tree hsa_kernels = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| kernel_info_vector_type); |
| |
| TREE_STATIC (hsa_kernels) = 1; |
| TREE_READONLY (hsa_kernels) = 1; |
| TREE_PUBLIC (hsa_kernels) = 0; |
| DECL_ARTIFICIAL (hsa_kernels) = 1; |
| DECL_IGNORED_P (hsa_kernels) = 1; |
| DECL_EXTERNAL (hsa_kernels) = 0; |
| TREE_CONSTANT (hsa_kernels) = 1; |
| DECL_INITIAL (hsa_kernels) = build_constructor (kernel_info_vector_type, |
| kernel_info_vector_vec); |
| varpool_node::finalize_decl (hsa_kernels); |
| *kernels = hsa_kernels; |
| } |
| |
| /* Create a static constructor that will register out brig stuff with |
| libgomp. */ |
| |
| static void |
| hsa_output_libgomp_mapping (tree brig_decl) |
| { |
| unsigned kernel_count = hsa_get_number_decl_kernel_mappings (); |
| unsigned global_variable_count = hsa_global_variable_symbols->elements (); |
| |
| tree kernels; |
| tree host_func_table; |
| |
| hsa_output_kernels (&host_func_table, &kernels); |
| tree global_vars = hsa_output_global_variables (); |
| |
| tree hsa_image_desc_type = make_node (RECORD_TYPE); |
| tree id_f1 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("brig_module"), ptr_type_node); |
| DECL_CHAIN (id_f1) = NULL_TREE; |
| tree id_f2 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("kernel_count"), |
| unsigned_type_node); |
| |
| DECL_CHAIN (id_f2) = id_f1; |
| tree id_f3 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("hsa_kernel_infos"), |
| ptr_type_node); |
| DECL_CHAIN (id_f3) = id_f2; |
| tree id_f4 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("global_variable_count"), |
| unsigned_type_node); |
| DECL_CHAIN (id_f4) = id_f3; |
| tree id_f5 = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("hsa_global_variable_infos"), |
| ptr_type_node); |
| DECL_CHAIN (id_f5) = id_f4; |
| finish_builtin_struct (hsa_image_desc_type, "__hsa_image_desc", id_f5, |
| NULL_TREE); |
| TYPE_ARTIFICIAL (hsa_image_desc_type) = 1; |
| |
| vec<constructor_elt, va_gc> *img_desc_vec = NULL; |
| CONSTRUCTOR_APPEND_ELT (img_desc_vec, NULL_TREE, |
| build_fold_addr_expr (brig_decl)); |
| CONSTRUCTOR_APPEND_ELT (img_desc_vec, NULL_TREE, |
| build_int_cstu (unsigned_type_node, kernel_count)); |
| CONSTRUCTOR_APPEND_ELT (img_desc_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type (TREE_TYPE (kernels)), |
| kernels)); |
| CONSTRUCTOR_APPEND_ELT (img_desc_vec, NULL_TREE, |
| build_int_cstu (unsigned_type_node, |
| global_variable_count)); |
| CONSTRUCTOR_APPEND_ELT (img_desc_vec, NULL_TREE, |
| build1 (ADDR_EXPR, |
| build_pointer_type (TREE_TYPE (global_vars)), |
| global_vars)); |
| |
| tree img_desc_ctor = build_constructor (hsa_image_desc_type, img_desc_vec); |
| |
| char tmp_name[64]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_img_descriptor", 1); |
| tree hsa_img_descriptor = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| hsa_image_desc_type); |
| TREE_STATIC (hsa_img_descriptor) = 1; |
| TREE_READONLY (hsa_img_descriptor) = 1; |
| TREE_PUBLIC (hsa_img_descriptor) = 0; |
| DECL_ARTIFICIAL (hsa_img_descriptor) = 1; |
| DECL_IGNORED_P (hsa_img_descriptor) = 1; |
| DECL_EXTERNAL (hsa_img_descriptor) = 0; |
| TREE_CONSTANT (hsa_img_descriptor) = 1; |
| DECL_INITIAL (hsa_img_descriptor) = img_desc_ctor; |
| varpool_node::finalize_decl (hsa_img_descriptor); |
| |
| /* Construct the "host_table" libgomp expects. */ |
| tree index_type = build_index_type (build_int_cst (integer_type_node, 4)); |
| tree libgomp_host_table_type = build_array_type (ptr_type_node, index_type); |
| TYPE_ARTIFICIAL (libgomp_host_table_type) = 1; |
| vec<constructor_elt, va_gc> *libgomp_host_table_vec = NULL; |
| tree host_func_table_addr = build_fold_addr_expr (host_func_table); |
| CONSTRUCTOR_APPEND_ELT (libgomp_host_table_vec, NULL_TREE, |
| host_func_table_addr); |
| offset_int func_table_size |
| = wi::to_offset (TYPE_SIZE_UNIT (ptr_type_node)) * kernel_count; |
| CONSTRUCTOR_APPEND_ELT (libgomp_host_table_vec, NULL_TREE, |
| fold_build2 (POINTER_PLUS_EXPR, |
| TREE_TYPE (host_func_table_addr), |
| host_func_table_addr, |
| build_int_cst (size_type_node, |
| func_table_size.to_uhwi |
| ()))); |
| CONSTRUCTOR_APPEND_ELT (libgomp_host_table_vec, NULL_TREE, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (libgomp_host_table_vec, NULL_TREE, null_pointer_node); |
| tree libgomp_host_table_ctor = build_constructor (libgomp_host_table_type, |
| libgomp_host_table_vec); |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, "__hsa_libgomp_host_table", 1); |
| tree hsa_libgomp_host_table = build_decl (UNKNOWN_LOCATION, VAR_DECL, |
| get_identifier (tmp_name), |
| libgomp_host_table_type); |
| |
| TREE_STATIC (hsa_libgomp_host_table) = 1; |
| TREE_READONLY (hsa_libgomp_host_table) = 1; |
| TREE_PUBLIC (hsa_libgomp_host_table) = 0; |
| DECL_ARTIFICIAL (hsa_libgomp_host_table) = 1; |
| DECL_IGNORED_P (hsa_libgomp_host_table) = 1; |
| DECL_EXTERNAL (hsa_libgomp_host_table) = 0; |
| TREE_CONSTANT (hsa_libgomp_host_table) = 1; |
| DECL_INITIAL (hsa_libgomp_host_table) = libgomp_host_table_ctor; |
| varpool_node::finalize_decl (hsa_libgomp_host_table); |
| |
| /* Generate an initializer with a call to the registration routine. */ |
| |
| tree offload_register |
| = builtin_decl_explicit (BUILT_IN_GOMP_OFFLOAD_REGISTER); |
| gcc_checking_assert (offload_register); |
| |
| tree *hsa_ctor_stmts = hsa_get_ctor_statements (); |
| append_to_statement_list |
| (build_call_expr (offload_register, 4, |
| build_int_cstu (unsigned_type_node, |
| GOMP_VERSION_PACK (GOMP_VERSION, |
| GOMP_VERSION_HSA)), |
| build_fold_addr_expr (hsa_libgomp_host_table), |
| build_int_cst (integer_type_node, GOMP_DEVICE_HSA), |
| build_fold_addr_expr (hsa_img_descriptor)), |
| hsa_ctor_stmts); |
| |
| cgraph_build_static_cdtor ('I', *hsa_ctor_stmts, DEFAULT_INIT_PRIORITY); |
| |
| tree offload_unregister |
| = builtin_decl_explicit (BUILT_IN_GOMP_OFFLOAD_UNREGISTER); |
| gcc_checking_assert (offload_unregister); |
| |
| tree *hsa_dtor_stmts = hsa_get_dtor_statements (); |
| append_to_statement_list |
| (build_call_expr (offload_unregister, 4, |
| build_int_cstu (unsigned_type_node, |
| GOMP_VERSION_PACK (GOMP_VERSION, |
| GOMP_VERSION_HSA)), |
| build_fold_addr_expr (hsa_libgomp_host_table), |
| build_int_cst (integer_type_node, GOMP_DEVICE_HSA), |
| build_fold_addr_expr (hsa_img_descriptor)), |
| hsa_dtor_stmts); |
| cgraph_build_static_cdtor ('D', *hsa_dtor_stmts, DEFAULT_INIT_PRIORITY); |
| } |
| |
| /* Emit the brig module we have compiled to a section in the final assembly and |
| also create a compile unit static constructor that will register the brig |
| module with libgomp. */ |
| |
| void |
| hsa_output_brig (void) |
| { |
| section *saved_section; |
| |
| 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); |
| gcc_assert (*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 = lendian32 (*func_offset); |
| } |
| |
| /* Iterate all function declarations and if we meet a function that should |
| have module linkage and we are unable to emit HSAIL for the function, |
| then change the linkage to program linkage. Doing so, we will emit |
| a valid BRIG image. */ |
| if (hsa_failed_functions != NULL && emitted_declarations != NULL) |
| for (hash_map <tree, BrigDirectiveExecutable *>::iterator it |
| = emitted_declarations->begin (); |
| it != emitted_declarations->end (); |
| ++it) |
| { |
| if (hsa_failed_functions->contains ((*it).first)) |
| (*it).second->linkage = BRIG_LINKAGE_PROGRAM; |
| } |
| |
| saved_section = in_section; |
| |
| switch_to_section (get_section (BRIG_ELF_SECTION_NAME, SECTION_NOTYPE, NULL)); |
| char tmp_name[64]; |
| ASM_GENERATE_INTERNAL_LABEL (tmp_name, BRIG_LABEL_STRING, 1); |
| ASM_OUTPUT_LABEL (asm_out_file, tmp_name); |
| tree brig_id = get_identifier (tmp_name); |
| tree brig_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, brig_id, |
| char_type_node); |
| SET_DECL_ASSEMBLER_NAME (brig_decl, brig_id); |
| TREE_ADDRESSABLE (brig_decl) = 1; |
| TREE_READONLY (brig_decl) = 1; |
| DECL_ARTIFICIAL (brig_decl) = 1; |
| DECL_IGNORED_P (brig_decl) = 1; |
| TREE_STATIC (brig_decl) = 1; |
| TREE_PUBLIC (brig_decl) = 0; |
| TREE_USED (brig_decl) = 1; |
| DECL_INITIAL (brig_decl) = brig_decl; |
| TREE_ASM_WRITTEN (brig_decl) = 1; |
| |
| BrigModuleHeader module_header; |
| memcpy (&module_header.identification, "HSA BRIG", |
| sizeof (module_header.identification)); |
| module_header.brigMajor = lendian32 (BRIG_VERSION_BRIG_MAJOR); |
| module_header.brigMinor = lendian32 (BRIG_VERSION_BRIG_MINOR); |
| uint64_t section_index[3]; |
| |
| int data_padding, code_padding, operand_padding; |
| data_padding = HSA_SECTION_ALIGNMENT |
| - brig_data.total_size % HSA_SECTION_ALIGNMENT; |
| code_padding = HSA_SECTION_ALIGNMENT |
| - brig_code.total_size % HSA_SECTION_ALIGNMENT; |
| operand_padding = HSA_SECTION_ALIGNMENT |
| - brig_operand.total_size % HSA_SECTION_ALIGNMENT; |
| |
| uint64_t module_size = sizeof (module_header) |
| + sizeof (section_index) |
| + brig_data.total_size |
| + data_padding |
| + brig_code.total_size |
| + code_padding |
| + brig_operand.total_size |
| + operand_padding; |
| gcc_assert ((module_size % 16) == 0); |
| module_header.byteCount = lendian64 (module_size); |
| memset (&module_header.hash, 0, sizeof (module_header.hash)); |
| module_header.reserved = 0; |
| module_header.sectionCount = lendian32 (3); |
| module_header.sectionIndex = lendian64 (sizeof (module_header)); |
| assemble_string ((const char *) &module_header, sizeof (module_header)); |
| uint64_t off = sizeof (module_header) + sizeof (section_index); |
| section_index[0] = lendian64 (off); |
| off += brig_data.total_size + data_padding; |
| section_index[1] = lendian64 (off); |
| off += brig_code.total_size + code_padding; |
| section_index[2] = lendian64 (off); |
| assemble_string ((const char *) §ion_index, sizeof (section_index)); |
| |
| char padding[HSA_SECTION_ALIGNMENT]; |
| memset (padding, 0, sizeof (padding)); |
| |
| brig_data.output (); |
| assemble_string (padding, data_padding); |
| brig_code.output (); |
| assemble_string (padding, code_padding); |
| brig_operand.output (); |
| assemble_string (padding, operand_padding); |
| |
| if (saved_section) |
| switch_to_section (saved_section); |
| |
| hsa_output_libgomp_mapping (brig_decl); |
| |
| hsa_free_decl_kernel_mapping (); |
| brig_release_data (); |
| hsa_deinit_compilation_unit_data (); |
| |
| delete emitted_declarations; |
| emitted_declarations = NULL; |
| delete function_offsets; |
| function_offsets = NULL; |
| } |