1 /* brig-util.cc -- gccbrig utility functions
2    Copyright (C) 2016-2021 Free Software Foundation, Inc.
3    Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4    for General Processor Tech.
5 
6 This file is part of GCC.
7 
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12 
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3.  If not see
20 <http://www.gnu.org/licenses/>.  */
21 
22 #include <sstream>
23 
24 #include "stdint.h"
25 #include "hsa-brig-format.h"
26 #include "brig-util.h"
27 #include "errors.h"
28 #include "diagnostic-core.h"
29 #include "print-tree.h"
30 
31 bool
has_variable(const std::string & name) const32 group_variable_offset_index::has_variable (const std::string &name) const
33 {
34   varname_offset_table::const_iterator i = m_group_offsets.find (name);
35   return i != m_group_offsets.end ();
36 }
37 
38 /* Adds a new group segment variable.  */
39 
40 void
add(const std::string & name,size_t size,size_t alignment)41 group_variable_offset_index::add (const std::string &name, size_t size,
42 				  size_t alignment)
43 {
44   size_t align_padding = m_next_group_offset % alignment == 0 ?
45     0 : (alignment - m_next_group_offset % alignment);
46   m_next_group_offset += align_padding;
47   m_group_offsets[name] = m_next_group_offset;
48   m_next_group_offset += size;
49 }
50 
51 size_t
segment_offset(const std::string & name) const52 group_variable_offset_index::segment_offset (const std::string &name) const
53 {
54   varname_offset_table::const_iterator i = m_group_offsets.find (name);
55   gcc_assert (i != m_group_offsets.end ());
56   return (*i).second;
57 }
58 
59 /* Return true if operand number OPNUM of instruction with OPCODE is an output.
60    False if it is an input.  Some code reused from Martin Jambor's gcc-hsa
61    tree.  */
62 
63 bool
gccbrig_hsa_opcode_op_output_p(BrigOpcode16_t opcode,int opnum)64 gccbrig_hsa_opcode_op_output_p (BrigOpcode16_t opcode, int opnum)
65 {
66   switch (opcode)
67     {
68     case BRIG_OPCODE_BR:
69     case BRIG_OPCODE_SBR:
70     case BRIG_OPCODE_CBR:
71     case BRIG_OPCODE_ST:
72     case BRIG_OPCODE_ATOMICNORET:
73     case BRIG_OPCODE_SIGNALNORET:
74     case BRIG_OPCODE_INITFBAR:
75     case BRIG_OPCODE_JOINFBAR:
76     case BRIG_OPCODE_WAITFBAR:
77     case BRIG_OPCODE_ARRIVEFBAR:
78     case BRIG_OPCODE_LEAVEFBAR:
79     case BRIG_OPCODE_RELEASEFBAR:
80     case BRIG_OPCODE_DEBUGTRAP:
81       return false;
82     default:
83       return opnum == 0;
84     }
85 }
86 
87 unsigned
gccbrig_hsa_type_bit_size(BrigType16_t t)88 gccbrig_hsa_type_bit_size (BrigType16_t t)
89 {
90 
91   unsigned pack_type = t & ~BRIG_TYPE_BASE_MASK;
92 
93   if (pack_type == BRIG_TYPE_PACK_32)
94     return 32;
95   else if (pack_type == BRIG_TYPE_PACK_64)
96     return 64;
97   else if (pack_type == BRIG_TYPE_PACK_128)
98     return 128;
99 
100   switch (t)
101     {
102     case BRIG_TYPE_NONE:
103       return 0;
104 
105     case BRIG_TYPE_B1:
106       return 1;
107 
108     case BRIG_TYPE_U8:
109     case BRIG_TYPE_S8:
110     case BRIG_TYPE_B8:
111       return 8;
112 
113     case BRIG_TYPE_U16:
114     case BRIG_TYPE_S16:
115     case BRIG_TYPE_B16:
116     case BRIG_TYPE_F16:
117       return 16;
118 
119     case BRIG_TYPE_U32:
120     case BRIG_TYPE_S32:
121     case BRIG_TYPE_B32:
122     case BRIG_TYPE_F32:
123     case BRIG_TYPE_U8X4:
124     case BRIG_TYPE_U16X2:
125     case BRIG_TYPE_S8X4:
126     case BRIG_TYPE_S16X2:
127     case BRIG_TYPE_F16X2:
128     case BRIG_TYPE_SIG32:
129       return 32;
130 
131     case BRIG_TYPE_U64:
132     case BRIG_TYPE_S64:
133     case BRIG_TYPE_F64:
134     case BRIG_TYPE_B64:
135     case BRIG_TYPE_U8X8:
136     case BRIG_TYPE_U16X4:
137     case BRIG_TYPE_U32X2:
138     case BRIG_TYPE_S8X8:
139     case BRIG_TYPE_S16X4:
140     case BRIG_TYPE_S32X2:
141     case BRIG_TYPE_F16X4:
142     case BRIG_TYPE_F32X2:
143     case BRIG_TYPE_SIG64:
144       return 64;
145 
146     case BRIG_TYPE_B128:
147     case BRIG_TYPE_U8X16:
148     case BRIG_TYPE_U16X8:
149     case BRIG_TYPE_U32X4:
150     case BRIG_TYPE_U64X2:
151     case BRIG_TYPE_S8X16:
152     case BRIG_TYPE_S16X8:
153     case BRIG_TYPE_S32X4:
154     case BRIG_TYPE_S64X2:
155     case BRIG_TYPE_F16X8:
156     case BRIG_TYPE_F32X4:
157     case BRIG_TYPE_F64X2:
158       return 128;
159 
160     default:
161       printf ("HMM %d %x\n", t, t);
162       gcc_unreachable ();
163     }
164 }
165 
166 /* gcc-hsa borrowed code ENDS.  */
167 
168 uint64_t
gccbrig_to_uint64_t(const BrigUInt64 & brig_type)169 gccbrig_to_uint64_t (const BrigUInt64 &brig_type)
170 {
171   return (uint64_t (brig_type.hi) << 32) | uint64_t (brig_type.lo);
172 }
173 
174 int
gccbrig_reg_size(const BrigOperandRegister * brig_reg)175 gccbrig_reg_size (const BrigOperandRegister *brig_reg)
176 {
177   switch (brig_reg->regKind)
178     {
179     case BRIG_REGISTER_KIND_CONTROL:
180       return 1;
181     case BRIG_REGISTER_KIND_SINGLE:
182       return 32;
183     case BRIG_REGISTER_KIND_DOUBLE:
184       return 64;
185     case BRIG_REGISTER_KIND_QUAD:
186       return 128;
187     default:
188       gcc_unreachable ();
189       break;
190     }
191 }
192 
193 std::string
gccbrig_reg_name(const BrigOperandRegister * reg)194 gccbrig_reg_name (const BrigOperandRegister *reg)
195 {
196   std::ostringstream strstr;
197   switch (reg->regKind)
198     {
199     case BRIG_REGISTER_KIND_CONTROL:
200       strstr << 'c';
201       break;
202     case BRIG_REGISTER_KIND_SINGLE:
203       strstr << 's';
204       break;
205     case BRIG_REGISTER_KIND_DOUBLE:
206       strstr << 'd';
207       break;
208     case BRIG_REGISTER_KIND_QUAD:
209       strstr << 'q';
210       break;
211     default:
212       gcc_unreachable ();
213       return "";
214     }
215   strstr << reg->regNum;
216   return strstr.str ();
217 }
218 
219 std::string
gccbrig_type_name(BrigType16_t type)220 gccbrig_type_name (BrigType16_t type)
221 {
222   switch (type)
223     {
224     case BRIG_TYPE_U8:
225       return "u8";
226     case BRIG_TYPE_U16:
227       return "u16";
228     case BRIG_TYPE_U32:
229       return "u32";
230     case BRIG_TYPE_U64:
231       return "u64";
232     case BRIG_TYPE_S8:
233       return "s8";
234     case BRIG_TYPE_S16:
235       return "s16";
236     case BRIG_TYPE_S32:
237       return "s32";
238     case BRIG_TYPE_S64:
239       return "s64";
240     default:
241       gcc_unreachable ();
242       break;
243     }
244 }
245 
246 std::string
gccbrig_segment_name(BrigSegment8_t segment)247 gccbrig_segment_name (BrigSegment8_t segment)
248 {
249   if (segment == BRIG_SEGMENT_GLOBAL)
250     return "global";
251   else if (segment == BRIG_SEGMENT_GROUP)
252     return "group";
253   else if (segment == BRIG_SEGMENT_PRIVATE)
254     return "private";
255   else
256     gcc_unreachable ();
257 }
258 
259 bool
gccbrig_is_float_type(BrigType16_t type)260 gccbrig_is_float_type (BrigType16_t type)
261 {
262   return (type == BRIG_TYPE_F32 || type == BRIG_TYPE_F64
263 	  || type == BRIG_TYPE_F16);
264 }
265 
266 BrigType16_t
gccbrig_tree_type_to_hsa_type(tree tree_type)267 gccbrig_tree_type_to_hsa_type (tree tree_type)
268 {
269   if (INTEGRAL_TYPE_P (tree_type))
270     {
271       if (TYPE_UNSIGNED (tree_type))
272 	{
273 	  switch (int_size_in_bytes (tree_type))
274 	    {
275 	    case 1:
276 	      return BRIG_TYPE_U8;
277 	    case 2:
278 	      return BRIG_TYPE_U16;
279 	    case 4:
280 	      return BRIG_TYPE_U32;
281 	    case 8:
282 	      return BRIG_TYPE_U64;
283 	    default:
284 	      break;
285 	    }
286 	}
287       else
288 	{
289 	  switch (int_size_in_bytes (tree_type))
290 	    {
291 	    case 1:
292 	      return BRIG_TYPE_S8;
293 	    case 2:
294 	      return BRIG_TYPE_S16;
295 	    case 4:
296 	      return BRIG_TYPE_S32;
297 	    case 8:
298 	      return BRIG_TYPE_S64;
299 	    default:
300 	      break;
301 	    }
302 	}
303     }
304   else if (VECTOR_TYPE_P (tree_type))
305     {
306       tree element_type = TREE_TYPE (tree_type);
307       size_t element_size = int_size_in_bytes (element_type) * 8;
308       BrigType16_t brig_element_type;
309       switch (element_size)
310 	{
311 	case 8:
312 	  brig_element_type
313 	    = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U8 : BRIG_TYPE_S8;
314 	  break;
315 	case 16:
316 	  brig_element_type
317 	    = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U16 : BRIG_TYPE_S16;
318 	  break;
319 	case 32:
320 	  brig_element_type
321 	    = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U32 : BRIG_TYPE_S32;
322 	  break;
323 	case 64:
324 	  brig_element_type
325 	    = TYPE_UNSIGNED (element_type) ? BRIG_TYPE_U64 : BRIG_TYPE_S64;
326 	  break;
327 	default:
328 	  gcc_unreachable ();
329 	}
330 
331       BrigType16_t pack_type;
332       switch (int_size_in_bytes (tree_type) * 8)
333 	{
334 	case 32:
335 	  pack_type = BRIG_TYPE_PACK_32;
336 	  break;
337 	case 64:
338 	  pack_type = BRIG_TYPE_PACK_64;
339 	  break;
340 	case 128:
341 	  pack_type = BRIG_TYPE_PACK_128;
342 	  break;
343 	default:
344 	  gcc_unreachable ();
345 	}
346       return brig_element_type | pack_type;
347     }
348   gcc_unreachable ();
349 }
350 
351 /* Returns true in case the operation is a "bit level" operation,
352    that is, not having operand type depending semantical differences.  */
353 
354 bool
gccbrig_is_bit_operation(BrigOpcode16_t opcode)355 gccbrig_is_bit_operation (BrigOpcode16_t opcode)
356 {
357   return opcode == BRIG_OPCODE_CMOV || opcode == BRIG_OPCODE_SHUFFLE
358 	 || opcode == BRIG_OPCODE_UNPACK || opcode == BRIG_OPCODE_UNPACKLO
359 	 || opcode == BRIG_OPCODE_UNPACKHI || opcode == BRIG_OPCODE_ST
360 	 || opcode == BRIG_OPCODE_PACK;
361 }
362 
363 /* The program scope definition can be left external within the
364    kernel binary which means it must be defined by the host via
365    HSA runtime.  For these we have special treatment:
366    Create additional pointer indirection when accessing the variable
367    value from kernel code through a generated pointer
368    __gccbrig_ptr_variable_name.  The pointer value then can be set either
369    within the kernel binary (in case of a later linked in definition)
370    or from the host.  */
371 
372 bool
gccbrig_might_be_host_defined_var_p(const BrigDirectiveVariable * brigVar)373 gccbrig_might_be_host_defined_var_p (const BrigDirectiveVariable *brigVar)
374 {
375   bool is_definition = brigVar->modifier & BRIG_VARIABLE_DEFINITION;
376   return (brigVar->segment == BRIG_SEGMENT_GLOBAL
377 	  || brigVar->segment == BRIG_SEGMENT_READONLY) && !is_definition
378     && brigVar->linkage == BRIG_LINKAGE_PROGRAM
379     && (brigVar->allocation == BRIG_ALLOCATION_PROGRAM
380 	|| brigVar->allocation == BRIG_ALLOCATION_AGENT);
381 }
382 
383 /* Produce a GENERIC type for the given HSA/BRIG type.  Returns the element
384    type in case of vector instructions.  */
385 
386 tree
gccbrig_tree_type_for_hsa_type(BrigType16_t brig_type)387 gccbrig_tree_type_for_hsa_type (BrigType16_t brig_type)
388 {
389   tree tree_type = NULL_TREE;
390 
391   if (hsa_type_packed_p (brig_type))
392     {
393       /* The element type is encoded in the bottom 5 bits.  */
394       BrigType16_t inner_brig_type = brig_type & BRIG_TYPE_BASE_MASK;
395 
396       unsigned full_size = gccbrig_hsa_type_bit_size (brig_type);
397 
398       if (inner_brig_type == BRIG_TYPE_F16)
399 	return build_vector_type (gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U16),
400 				  full_size / 16);
401 
402       tree inner_type = gccbrig_tree_type_for_hsa_type (inner_brig_type);
403 
404       unsigned inner_size = gccbrig_hsa_type_bit_size (inner_brig_type);
405       unsigned nunits = full_size / inner_size;
406       tree_type = build_vector_type (inner_type, nunits);
407     }
408   else
409     {
410       switch (brig_type)
411 	{
412 	case BRIG_TYPE_NONE:
413 	  tree_type = void_type_node;
414 	  break;
415 	case BRIG_TYPE_B1:
416 	  tree_type = boolean_type_node;
417 	  break;
418 	case BRIG_TYPE_S8:
419 	case BRIG_TYPE_S16:
420 	case BRIG_TYPE_S32:
421 	case BRIG_TYPE_S64:
422 	  /* Ensure a fixed width integer.  */
423 	  tree_type
424 	    = build_nonstandard_integer_type
425 	    (gccbrig_hsa_type_bit_size (brig_type), false);
426 	  break;
427 	case BRIG_TYPE_U8:
428 	  return unsigned_char_type_node;
429 	case BRIG_TYPE_U16:
430 	case BRIG_TYPE_U32:
431 	case BRIG_TYPE_U64:
432 	case BRIG_TYPE_B8: /* Handle bit vectors as unsigned ints.  */
433 	case BRIG_TYPE_B16:
434 	case BRIG_TYPE_B32:
435 	case BRIG_TYPE_B64:
436 	case BRIG_TYPE_B128:
437 	case BRIG_TYPE_SIG32: /* Handle signals as integers for now.  */
438 	case BRIG_TYPE_SIG64:
439 	  tree_type = build_nonstandard_integer_type
440 	    (gccbrig_hsa_type_bit_size (brig_type), true);
441 	  break;
442 	case BRIG_TYPE_F16:
443 	  tree_type = uint16_type_node;
444 	  break;
445 	case BRIG_TYPE_F32:
446 	  /* TODO: make sure that the alignment of the float are at least as
447 	     strict than mandated by HSA, and conform to IEEE (like mandated
448 	     by HSA).  */
449 	  tree_type = float_type_node;
450 	  break;
451 	case BRIG_TYPE_F64:
452 	  tree_type = double_type_node;
453 	  break;
454 	case BRIG_TYPE_SAMP:
455 	case BRIG_TYPE_ROIMG:
456 	case BRIG_TYPE_WOIMG:
457 	case BRIG_TYPE_RWIMG:
458 	  {
459 	    /* Handle images and samplers as target-specific blobs of data
460 	       that should be allocated earlier on from the runtime side.
461 	       Create a void* that should be initialized to point to the blobs
462 	       by the kernel launcher.  Images and samplers are accessed
463 	       via builtins that take void* as the reference.  TODO: who and
464 	       how these arrays should be initialized?  */
465 	    tree void_ptr = build_pointer_type (void_type_node);
466 	    return void_ptr;
467 	  }
468 	default:
469 	  gcc_unreachable ();
470 	  break;
471 	}
472     }
473 
474   /* Drop const qualifiers.  */
475   return tree_type;
476 }
477 
478 /* Calculates numeric identifier for the HSA register REG.
479 
480    Returned value is bound to [0, BRIG_2_TREE_HSAIL_TOTAL_REG_COUNT].  */
481 
482 size_t
gccbrig_hsa_reg_id(const BrigOperandRegister & reg)483 gccbrig_hsa_reg_id (const BrigOperandRegister &reg)
484 {
485   size_t offset = reg.regNum;
486   switch (reg.regKind)
487     {
488     case BRIG_REGISTER_KIND_QUAD:
489       offset
490 	+= BRIG_2_TREE_HSAIL_D_REG_COUNT + BRIG_2_TREE_HSAIL_S_REG_COUNT
491 	+ BRIG_2_TREE_HSAIL_C_REG_COUNT;
492       break;
493     case BRIG_REGISTER_KIND_DOUBLE:
494       offset += BRIG_2_TREE_HSAIL_S_REG_COUNT + BRIG_2_TREE_HSAIL_C_REG_COUNT;
495       break;
496     case BRIG_REGISTER_KIND_SINGLE:
497       offset += BRIG_2_TREE_HSAIL_C_REG_COUNT;
498     case BRIG_REGISTER_KIND_CONTROL:
499       break;
500     default:
501       gcc_unreachable ();
502       break;
503     }
504   return offset;
505 }
506 
507 std::string
gccbrig_hsa_reg_name_from_id(size_t reg_id)508 gccbrig_hsa_reg_name_from_id (size_t reg_id)
509 {
510   char reg_name[32];
511   long unsigned int reg_hash = (long unsigned int) reg_id;
512   if (reg_hash < BRIG_2_TREE_HSAIL_C_REG_COUNT)
513     {
514       sprintf (reg_name, "$c%lu", reg_hash);
515       return reg_name;
516     }
517 
518   reg_hash -= BRIG_2_TREE_HSAIL_C_REG_COUNT;
519   if (reg_hash < BRIG_2_TREE_HSAIL_S_REG_COUNT)
520     {
521       sprintf (reg_name, "$s%lu", reg_hash);
522       return reg_name;
523     }
524 
525   reg_hash -= BRIG_2_TREE_HSAIL_S_REG_COUNT;
526   if (reg_hash < BRIG_2_TREE_HSAIL_D_REG_COUNT)
527     {
528       sprintf (reg_name, "$d%lu", reg_hash);
529       return reg_name;
530     }
531 
532    reg_hash -= BRIG_2_TREE_HSAIL_D_REG_COUNT;
533    if (reg_hash < BRIG_2_TREE_HSAIL_Q_REG_COUNT)
534     {
535       sprintf (reg_name, "$q%lu", reg_hash);
536       return reg_name;
537     }
538 
539   gcc_unreachable ();
540   return "$??";
541 }
542 
543 /* Prints statistics of register usage to stdout.  */
544 
545 void
gccbrig_print_reg_use_info(FILE * dump,const regs_use_index & info)546 gccbrig_print_reg_use_info (FILE *dump, const regs_use_index &info)
547 {
548   regs_use_index::const_iterator begin_it = info.begin ();
549   regs_use_index::const_iterator end_it = info.end ();
550   for (regs_use_index::const_iterator it = begin_it; it != end_it; it++)
551     {
552       std::string hsa_reg = gccbrig_hsa_reg_name_from_id (it->first);
553       printf ("%s:\n", hsa_reg.c_str ());
554       const reg_use_info &info = it->second;
555       typedef std::vector<std::pair<tree, size_t> >::const_iterator reg_use_it;
556       reg_use_it begin_it2 = info.m_type_refs.begin ();
557       reg_use_it end_it2 = info.m_type_refs.end ();
558       for (reg_use_it it2 = begin_it2; it2 != end_it2; it2++)
559 	{
560 	  fprintf (dump, "(%lu) ", (long unsigned int) it2->second);
561 	  print_node_brief (dump, "", it2->first, 0);
562 	  fprintf (dump, "\n");
563 	}
564     }
565 }
566 
567 /* Return true if TYPE is a packed HSA type.  */
568 
569 bool
hsa_type_packed_p(BrigType16_t type)570 hsa_type_packed_p (BrigType16_t type)
571 {
572   return (type & BRIG_TYPE_PACK_MASK) != BRIG_TYPE_PACK_NONE;
573 }
574 
575