| /* brig-util.cc -- gccbrig utility functions |
| Copyright (C) 2016-2018 Free Software Foundation, Inc. |
| Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com> |
| for General Processor Tech. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <sstream> |
| |
| #include "stdint.h" |
| #include "hsa-brig-format.h" |
| #include "brig-util.h" |
| #include "errors.h" |
| #include "diagnostic-core.h" |
| #include "print-tree.h" |
| |
| bool |
| group_variable_offset_index::has_variable (const std::string &name) const |
| { |
| varname_offset_table::const_iterator i = m_group_offsets.find (name); |
| return i != m_group_offsets.end (); |
| } |
| |
| /* Adds a new group segment variable. */ |
| |
| void |
| group_variable_offset_index::add (const std::string &name, size_t size, |
| size_t alignment) |
| { |
| size_t align_padding = m_next_group_offset % alignment == 0 ? |
| 0 : (alignment - m_next_group_offset % alignment); |
| m_next_group_offset += align_padding; |
| m_group_offsets[name] = m_next_group_offset; |
| m_next_group_offset += size; |
| } |
| |
| size_t |
| group_variable_offset_index::segment_offset (const std::string &name) const |
| { |
| varname_offset_table::const_iterator i = m_group_offsets.find (name); |
| gcc_assert (i != m_group_offsets.end ()); |
| return (*i).second; |
| } |
| |
| /* Return true if operand number OPNUM of instruction with OPCODE is an output. |
| False if it is an input. Some code reused from Martin Jambor's gcc-hsa |
| tree. */ |
| |
| bool |
| gccbrig_hsa_opcode_op_output_p (BrigOpcode16_t opcode, int opnum) |
| { |
| switch (opcode) |
| { |
| case BRIG_OPCODE_BR: |
| case BRIG_OPCODE_SBR: |
| case BRIG_OPCODE_CBR: |
| case BRIG_OPCODE_ST: |
| case BRIG_OPCODE_ATOMICNORET: |
| case BRIG_OPCODE_SIGNALNORET: |
| case BRIG_OPCODE_INITFBAR: |
| case BRIG_OPCODE_JOINFBAR: |
| case BRIG_OPCODE_WAITFBAR: |
| case BRIG_OPCODE_ARRIVEFBAR: |
| case BRIG_OPCODE_LEAVEFBAR: |
| case BRIG_OPCODE_RELEASEFBAR: |
| case BRIG_OPCODE_DEBUGTRAP: |
| return false; |
| default: |
| return opnum == 0; |
| } |
| } |
| |
| unsigned |
| gccbrig_hsa_type_bit_size (BrigType16_t t) |
| { |
| |
| unsigned pack_type = t & ~BRIG_TYPE_BASE_MASK; |
| |
| if (pack_type == BRIG_TYPE_PACK_32) |
| return 32; |
| else if (pack_type == BRIG_TYPE_PACK_64) |
| return 64; |
| else if (pack_type == BRIG_TYPE_PACK_128) |
| return 128; |
| |
| switch (t) |
| { |
| case BRIG_TYPE_NONE: |
| return 0; |
| |
| case BRIG_TYPE_B1: |
| return 1; |
| |
| case BRIG_TYPE_U8: |
| case BRIG_TYPE_S8: |
| case BRIG_TYPE_B8: |
| return 8; |
| |
| case BRIG_TYPE_U16: |
| case BRIG_TYPE_S16: |
| case BRIG_TYPE_B16: |
| case BRIG_TYPE_F16: |
| return 16; |
| |
| case BRIG_TYPE_U32: |
| case BRIG_TYPE_S32: |
| case BRIG_TYPE_B32: |
| case BRIG_TYPE_F32: |
| case BRIG_TYPE_U8X4: |
| case BRIG_TYPE_U16X2: |
| case BRIG_TYPE_S8X4: |
| case BRIG_TYPE_S16X2: |
| case BRIG_TYPE_F16X2: |
| case BRIG_TYPE_SIG32: |
| return 32; |
| |
| 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: |
| case BRIG_TYPE_SIG64: |
| return 64; |
| |
| 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 128; |
| |
| default: |
| printf ("HMM %d %x\n", t, t); |
| gcc_unreachable (); |
| } |
| } |
| |
| /* gcc-hsa borrowed code ENDS. */ |
| |
| uint64_t |
| gccbrig_to_uint64_t (const BrigUInt64 &brig_type) |
| { |
| return (uint64_t (brig_type.hi) << 32) | uint64_t (brig_type.lo); |
| } |
| |
| int |
| gccbrig_reg_size (const BrigOperandRegister *brig_reg) |
| { |
| switch (brig_reg->regKind) |
| { |
| case BRIG_REGISTER_KIND_CONTROL: |
| return 1; |
| case BRIG_REGISTER_KIND_SINGLE: |
| return 32; |
| case BRIG_REGISTER_KIND_DOUBLE: |
| return 64; |
| case BRIG_REGISTER_KIND_QUAD: |
| return 128; |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| } |
| |
| std::string |
| gccbrig_reg_name (const BrigOperandRegister *reg) |
| { |
| std::ostringstream strstr; |
| switch (reg->regKind) |
| { |
| case BRIG_REGISTER_KIND_CONTROL: |
| strstr << 'c'; |
| break; |
| case BRIG_REGISTER_KIND_SINGLE: |
| strstr << 's'; |
| break; |
| case BRIG_REGISTER_KIND_DOUBLE: |
| strstr << 'd'; |
| break; |
| case BRIG_REGISTER_KIND_QUAD: |
| strstr << 'q'; |
| break; |
| default: |
| gcc_unreachable (); |
| return ""; |
| } |
| strstr << reg->regNum; |
| return strstr.str (); |
| } |
| |
| std::string |
| gccbrig_type_name (BrigType16_t type) |
| { |
| switch (type) |
| { |
| case BRIG_TYPE_U8: |
| return "u8"; |
| case BRIG_TYPE_U16: |
| return "u16"; |
| case BRIG_TYPE_U32: |
| return "u32"; |
| case BRIG_TYPE_U64: |
| return "u64"; |
| case BRIG_TYPE_S8: |
| return "s8"; |
| case BRIG_TYPE_S16: |
| return "s16"; |
| case BRIG_TYPE_S32: |
| return "s32"; |
| case BRIG_TYPE_S64: |
| return "s64"; |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| } |
| |
| std::string |
| gccbrig_segment_name (BrigSegment8_t segment) |
| { |
| if (segment == BRIG_SEGMENT_GLOBAL) |
| return "global"; |
| else if (segment == BRIG_SEGMENT_GROUP) |
| return "group"; |
| else if (segment == BRIG_SEGMENT_PRIVATE) |
| return "private"; |
| else |
| gcc_unreachable (); |
| } |
| |
| bool |
| gccbrig_is_float_type (BrigType16_t type) |
| { |
| return (type == BRIG_TYPE_F32 || type == BRIG_TYPE_F64 |
| || type == BRIG_TYPE_F16); |
| } |
| |
| BrigType16_t |
| gccbrig_tree_type_to_hsa_type (tree tree_type) |
| { |
| if (INTEGRAL_TYPE_P (tree_type)) |
| { |
| if (TYPE_UNSIGNED (tree_type)) |
| { |
| switch (int_size_in_bytes (tree_type)) |
| { |
| case 1: |
| return BRIG_TYPE_U8; |
| case 2: |
| return BRIG_TYPE_U16; |
| case 4: |
| return BRIG_TYPE_U32; |
| case 8: |
| return BRIG_TYPE_U64; |
| default: |
| break; |
| } |
| } |
| else |
| { |
| switch (int_size_in_bytes (tree_type)) |
| { |
| case 1: |
| return BRIG_TYPE_S8; |
| case 2: |
| return BRIG_TYPE_S16; |
| case 4: |
| return BRIG_TYPE_S32; |
| case 8: |
| return BRIG_TYPE_S64; |
| default: |
| break; |
| } |
| } |
| } |
| else if (VECTOR_TYPE_P (tree_type)) |
| { |
| tree element_type = TREE_TYPE (tree_type); |
| size_t element_size = int_size_in_bytes (element_type) * 8; |
| BrigType16_t brig_element_type; |
| switch (element_size) |
| { |
| case 8: |
| brig_element_type |
| = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U8 : BRIG_TYPE_S8; |
| break; |
| case 16: |
| brig_element_type |
| = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U16 : BRIG_TYPE_S16; |
| break; |
| case 32: |
| brig_element_type |
| = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U32 : BRIG_TYPE_S32; |
| break; |
| case 64: |
| brig_element_type |
| = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U64 : BRIG_TYPE_S64; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| BrigType16_t pack_type; |
| switch (int_size_in_bytes (tree_type) * 8) |
| { |
| case 32: |
| pack_type = BRIG_TYPE_PACK_32; |
| break; |
| case 64: |
| pack_type = BRIG_TYPE_PACK_64; |
| break; |
| case 128: |
| pack_type = BRIG_TYPE_PACK_128; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| return brig_element_type | pack_type; |
| } |
| gcc_unreachable (); |
| } |
| |
| /* Returns true in case the operation is a "bit level" operation, |
| that is, not having operand type depending semantical differences. */ |
| |
| bool |
| gccbrig_is_bit_operation (BrigOpcode16_t opcode) |
| { |
| return opcode == BRIG_OPCODE_CMOV || opcode == BRIG_OPCODE_SHUFFLE |
| || opcode == BRIG_OPCODE_UNPACK || opcode == BRIG_OPCODE_UNPACKLO |
| || opcode == BRIG_OPCODE_UNPACKHI || opcode == BRIG_OPCODE_ST |
| || opcode == BRIG_OPCODE_PACK; |
| } |
| |
| /* The program scope definition can be left external within the |
| kernel binary which means it must be defined by the host via |
| HSA runtime. For these we have special treatment: |
| Create additional pointer indirection when accessing the variable |
| value from kernel code through a generated pointer |
| __gccbrig_ptr_variable_name. The pointer value then can be set either |
| within the kernel binary (in case of a later linked in definition) |
| or from the host. */ |
| |
| bool |
| gccbrig_might_be_host_defined_var_p (const BrigDirectiveVariable *brigVar) |
| { |
| bool is_definition = brigVar->modifier & BRIG_VARIABLE_DEFINITION; |
| return (brigVar->segment == BRIG_SEGMENT_GLOBAL |
| || brigVar->segment == BRIG_SEGMENT_READONLY) && !is_definition |
| && brigVar->linkage == BRIG_LINKAGE_PROGRAM |
| && (brigVar->allocation == BRIG_ALLOCATION_PROGRAM |
| || brigVar->allocation == BRIG_ALLOCATION_AGENT); |
| } |
| |
| /* Produce a GENERIC type for the given HSA/BRIG type. Returns the element |
| type in case of vector instructions. */ |
| |
| tree |
| gccbrig_tree_type_for_hsa_type (BrigType16_t brig_type) |
| { |
| tree tree_type = NULL_TREE; |
| |
| if (hsa_type_packed_p (brig_type)) |
| { |
| /* The element type is encoded in the bottom 5 bits. */ |
| BrigType16_t inner_brig_type = brig_type & BRIG_TYPE_BASE_MASK; |
| |
| unsigned full_size = gccbrig_hsa_type_bit_size (brig_type); |
| |
| if (inner_brig_type == BRIG_TYPE_F16) |
| return build_vector_type (gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U16), |
| full_size / 16); |
| |
| tree inner_type = gccbrig_tree_type_for_hsa_type (inner_brig_type); |
| |
| unsigned inner_size = gccbrig_hsa_type_bit_size (inner_brig_type); |
| unsigned nunits = full_size / inner_size; |
| tree_type = build_vector_type (inner_type, nunits); |
| } |
| else |
| { |
| switch (brig_type) |
| { |
| case BRIG_TYPE_NONE: |
| tree_type = void_type_node; |
| break; |
| case BRIG_TYPE_B1: |
| tree_type = boolean_type_node; |
| break; |
| case BRIG_TYPE_S8: |
| case BRIG_TYPE_S16: |
| case BRIG_TYPE_S32: |
| case BRIG_TYPE_S64: |
| /* Ensure a fixed width integer. */ |
| tree_type |
| = build_nonstandard_integer_type |
| (gccbrig_hsa_type_bit_size (brig_type), false); |
| break; |
| case BRIG_TYPE_U8: |
| return unsigned_char_type_node; |
| case BRIG_TYPE_U16: |
| case BRIG_TYPE_U32: |
| case BRIG_TYPE_U64: |
| case BRIG_TYPE_B8: /* Handle bit vectors as unsigned ints. */ |
| case BRIG_TYPE_B16: |
| case BRIG_TYPE_B32: |
| case BRIG_TYPE_B64: |
| case BRIG_TYPE_B128: |
| case BRIG_TYPE_SIG32: /* Handle signals as integers for now. */ |
| case BRIG_TYPE_SIG64: |
| tree_type = build_nonstandard_integer_type |
| (gccbrig_hsa_type_bit_size (brig_type), true); |
| break; |
| case BRIG_TYPE_F16: |
| tree_type = uint16_type_node; |
| break; |
| case BRIG_TYPE_F32: |
| /* TODO: make sure that the alignment of the float are at least as |
| strict than mandated by HSA, and conform to IEEE (like mandated |
| by HSA). */ |
| tree_type = float_type_node; |
| break; |
| case BRIG_TYPE_F64: |
| tree_type = double_type_node; |
| break; |
| case BRIG_TYPE_SAMP: |
| case BRIG_TYPE_ROIMG: |
| case BRIG_TYPE_WOIMG: |
| case BRIG_TYPE_RWIMG: |
| { |
| /* Handle images and samplers as target-specific blobs of data |
| that should be allocated earlier on from the runtime side. |
| Create a void* that should be initialized to point to the blobs |
| by the kernel launcher. Images and samplers are accessed |
| via builtins that take void* as the reference. TODO: who and |
| how these arrays should be initialized? */ |
| tree void_ptr = build_pointer_type (void_type_node); |
| return void_ptr; |
| } |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| } |
| |
| /* Drop const qualifiers. */ |
| return tree_type; |
| } |
| |
| /* Calculates numeric identifier for the HSA register REG. |
| |
| Returned value is bound to [0, BRIG_2_TREE_HSAIL_TOTAL_REG_COUNT]. */ |
| |
| size_t |
| gccbrig_hsa_reg_id (const BrigOperandRegister ®) |
| { |
| size_t offset = reg.regNum; |
| switch (reg.regKind) |
| { |
| case BRIG_REGISTER_KIND_QUAD: |
| offset |
| += BRIG_2_TREE_HSAIL_D_REG_COUNT + BRIG_2_TREE_HSAIL_S_REG_COUNT |
| + BRIG_2_TREE_HSAIL_C_REG_COUNT; |
| break; |
| case BRIG_REGISTER_KIND_DOUBLE: |
| offset += BRIG_2_TREE_HSAIL_S_REG_COUNT + BRIG_2_TREE_HSAIL_C_REG_COUNT; |
| break; |
| case BRIG_REGISTER_KIND_SINGLE: |
| offset += BRIG_2_TREE_HSAIL_C_REG_COUNT; |
| case BRIG_REGISTER_KIND_CONTROL: |
| break; |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| return offset; |
| } |
| |
| std::string |
| gccbrig_hsa_reg_name_from_id (size_t reg_id) |
| { |
| char reg_name[32]; |
| long unsigned int reg_hash = (long unsigned int) reg_id; |
| if (reg_hash < BRIG_2_TREE_HSAIL_C_REG_COUNT) |
| { |
| sprintf (reg_name, "$c%lu", reg_hash); |
| return reg_name; |
| } |
| |
| reg_hash -= BRIG_2_TREE_HSAIL_C_REG_COUNT; |
| if (reg_hash < BRIG_2_TREE_HSAIL_S_REG_COUNT) |
| { |
| sprintf (reg_name, "$s%lu", reg_hash); |
| return reg_name; |
| } |
| |
| reg_hash -= BRIG_2_TREE_HSAIL_S_REG_COUNT; |
| if (reg_hash < BRIG_2_TREE_HSAIL_D_REG_COUNT) |
| { |
| sprintf (reg_name, "$d%lu", reg_hash); |
| return reg_name; |
| } |
| |
| reg_hash -= BRIG_2_TREE_HSAIL_D_REG_COUNT; |
| if (reg_hash < BRIG_2_TREE_HSAIL_Q_REG_COUNT) |
| { |
| sprintf (reg_name, "$q%lu", reg_hash); |
| return reg_name; |
| } |
| |
| gcc_unreachable (); |
| return "$??"; |
| } |
| |
| /* Prints statistics of register usage to stdout. */ |
| |
| void |
| gccbrig_print_reg_use_info (FILE *dump, const regs_use_index &info) |
| { |
| regs_use_index::const_iterator begin_it = info.begin (); |
| regs_use_index::const_iterator end_it = info.end (); |
| for (regs_use_index::const_iterator it = begin_it; it != end_it; it++) |
| { |
| std::string hsa_reg = gccbrig_hsa_reg_name_from_id (it->first); |
| printf ("%s:\n", hsa_reg.c_str ()); |
| const reg_use_info &info = it->second; |
| typedef std::vector<std::pair<tree, size_t> >::const_iterator reg_use_it; |
| reg_use_it begin_it2 = info.m_type_refs.begin (); |
| reg_use_it end_it2 = info.m_type_refs.end (); |
| for (reg_use_it it2 = begin_it2; it2 != end_it2; it2++) |
| { |
| fprintf (dump, "(%lu) ", (long unsigned int) it2->second); |
| print_node_brief (dump, "", it2->first, 0); |
| fprintf (dump, "\n"); |
| } |
| } |
| } |