/* brig-util.cc -- gccbrig utility functions Copyright (C) 2016-2017 Free Software Foundation, Inc. Contributed by Pekka Jaaskelainen 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 . */ #include #include "stdint.h" #include "hsa-brig-format.h" #include "brig-util.h" #include "errors.h" #include "diagnostic-core.h" /* 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; }