1 /* brig-code-entry-handler.cc -- a gccbrig base class
2 Copyright (C) 2016-2021 Free Software Foundation, Inc.
3 Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4 for General Processor Tech.
6 This file is part of GCC.
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
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
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/>. */
22 #include "brig-code-entry-handler.h"
24 #include "stringpool.h"
25 #include "tree-iterator.h"
27 #include "diagnostic.h"
28 #include "brig-machine.h"
29 #include "brig-util.h"
32 #include "print-tree.h"
33 #include "tree-pretty-print.h"
35 #include "langhooks.h"
36 #include "gimple-expr.h"
38 #include "brig-util.h"
41 #include "brig-builtins.h"
42 #include "fold-const.h"
44 brig_code_entry_handler::brig_code_entry_handler (brig_to_generic
&parent
)
45 : brig_entry_handler (parent
)
49 /* Build a tree operand which is a reference to a piece of code. REF is the
50 original reference as a BRIG object. */
53 brig_code_entry_handler::build_code_ref (const BrigBase
&ref
)
55 if (ref
.kind
== BRIG_KIND_DIRECTIVE_LABEL
)
57 const BrigDirectiveLabel
*brig_label
= (const BrigDirectiveLabel
*) &ref
;
59 const BrigData
*label_name
60 = m_parent
.get_brig_data_entry (brig_label
->name
);
62 std::string
label_str ((const char *) (label_name
->bytes
),
63 label_name
->byteCount
);
64 return m_parent
.m_cf
->label (label_str
);
66 else if (ref
.kind
== BRIG_KIND_DIRECTIVE_FUNCTION
)
68 const BrigDirectiveExecutable
*func
69 = (const BrigDirectiveExecutable
*) &ref
;
70 return m_parent
.function_decl (m_parent
.get_mangled_name (func
));
72 else if (ref
.kind
== BRIG_KIND_DIRECTIVE_FBARRIER
)
74 const BrigDirectiveFbarrier
* fbar
= (const BrigDirectiveFbarrier
*)&ref
;
76 std::string var_name
= m_parent
.get_mangled_name (fbar
);
78 = m_parent
.m_cf
->group_variable_segment_offset (var_name
);
80 tree local_offset
= build_int_cst (uint32_type_node
, offset
);
81 if (m_parent
.m_cf
->m_local_group_variables
.has_variable (var_name
))
83 = build2 (PLUS_EXPR
, uint64_type_node
, local_offset
,
84 convert (uint64_type_node
,
85 m_parent
.m_cf
->m_group_local_offset_arg
));
92 /* Produce a tree operand for the given BRIG_INST and its OPERAND.
93 OPERAND_TYPE should be the operand type in case it should not
94 be dictated by the BrigBase. IS_INPUT indicates if the operand
95 is an input operand or a result. */
98 brig_code_entry_handler::build_tree_operand (const BrigInstBase
&brig_inst
,
99 const BrigBase
&operand
,
100 tree operand_type
, bool is_input
)
102 switch (operand
.kind
)
104 case BRIG_KIND_OPERAND_OPERAND_LIST
:
106 vec
<constructor_elt
, va_gc
> *constructor_vals
= NULL
;
107 const BrigOperandOperandList
&oplist
108 = (const BrigOperandOperandList
&) operand
;
109 const BrigData
*data
= m_parent
.get_brig_data_entry (oplist
.elements
);
110 size_t bytes
= data
->byteCount
;
111 const BrigOperandOffset32_t
*operand_ptr
112 = (const BrigOperandOffset32_t
*) data
->bytes
;
115 BrigOperandOffset32_t offset
= *operand_ptr
;
116 const BrigBase
*operand_element
117 = m_parent
.get_brig_operand_entry (offset
);
119 = build_tree_operand (brig_inst
, *operand_element
, operand_type
);
121 /* In case a vector is used an input, cast the elements to
122 correct size here so we don't need a separate unpack/pack for it.
123 fp16-fp32 conversion is done in build_operands (). */
124 if (is_input
&& TREE_TYPE (element
) != operand_type
)
125 element
= build_resize_convert_view (operand_type
, element
);
127 CONSTRUCTOR_APPEND_ELT (constructor_vals
, NULL_TREE
, element
);
131 size_t element_count
= data
->byteCount
/ 4;
132 tree vec_type
= build_vector_type (operand_type
, element_count
);
134 return build_constructor (vec_type
, constructor_vals
);
136 case BRIG_KIND_OPERAND_CODE_LIST
:
138 /* Build a TREE_VEC of code expressions. */
140 const BrigOperandCodeList
&oplist
141 = (const BrigOperandCodeList
&) operand
;
142 const BrigData
*data
= m_parent
.get_brig_data_entry (oplist
.elements
);
143 size_t bytes
= data
->byteCount
;
144 const BrigOperandOffset32_t
*operand_ptr
145 = (const BrigOperandOffset32_t
*) data
->bytes
;
147 size_t case_index
= 0;
148 size_t element_count
= data
->byteCount
/ 4;
150 /* Create a TREE_VEC out of the labels in the list. */
151 tree vec
= make_tree_vec (element_count
);
155 BrigOperandOffset32_t offset
= *operand_ptr
;
156 const BrigBase
*ref
= m_parent
.get_brig_code_entry (offset
);
157 tree element
= build_code_ref (*ref
);
159 gcc_assert (case_index
< element_count
);
160 TREE_VEC_ELT (vec
, case_index
) = element
;
168 case BRIG_KIND_OPERAND_REGISTER
:
170 const BrigOperandRegister
*brig_reg
171 = (const BrigOperandRegister
*) &operand
;
172 return m_parent
.m_cf
->get_m_var_declfor_reg (brig_reg
);
174 case BRIG_KIND_OPERAND_CONSTANT_BYTES
:
176 const BrigOperandConstantBytes
*brigConst
177 = (const BrigOperandConstantBytes
*) &operand
;
178 /* The constants can be of different type than the instruction
179 and are implicitly casted to the input operand. */
180 return get_tree_cst_for_hsa_operand (brigConst
, NULL_TREE
);
182 case BRIG_KIND_OPERAND_WAVESIZE
:
184 if (!INTEGRAL_TYPE_P (operand_type
))
189 return build_int_cstu (operand_type
, gccbrig_get_target_wavesize ());
191 case BRIG_KIND_OPERAND_CODE_REF
:
193 const BrigOperandCodeRef
*brig_code_ref
194 = (const BrigOperandCodeRef
*) &operand
;
196 const BrigBase
*ref
= m_parent
.get_brig_code_entry (brig_code_ref
->ref
);
198 return build_code_ref (*ref
);
200 case BRIG_KIND_OPERAND_ADDRESS
:
202 return build_address_operand (brig_inst
,
203 (const BrigOperandAddress
&) operand
);
210 /* Build a tree node representing an address reference from a BRIG_INST and its
214 brig_code_entry_handler::build_address_operand
215 (const BrigInstBase
&brig_inst
, const BrigOperandAddress
&addr_operand
)
217 tree instr_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
219 BrigSegment8_t segment
= BRIG_SEGMENT_GLOBAL
;
220 if (brig_inst
.opcode
== BRIG_OPCODE_LDA
)
221 segment
= ((const BrigInstAddr
&) brig_inst
).segment
;
222 else if (brig_inst
.base
.kind
== BRIG_KIND_INST_MEM
)
223 segment
= ((const BrigInstMem
&) brig_inst
).segment
;
224 else if (brig_inst
.base
.kind
== BRIG_KIND_INST_ATOMIC
)
225 segment
= ((const BrigInstAtomic
&) brig_inst
).segment
;
227 tree var_offset
= NULL_TREE
;
228 tree const_offset
= NULL_TREE
;
229 tree symbol_base
= NULL_TREE
;
231 if (addr_operand
.symbol
!= 0)
233 const BrigDirectiveVariable
*arg_symbol
234 = (const BrigDirectiveVariable
*) m_parent
.get_brig_code_entry
235 (addr_operand
.symbol
);
237 std::string var_name
= m_parent
.get_mangled_name (arg_symbol
);
239 if (segment
== BRIG_SEGMENT_KERNARG
)
241 /* Find the offset to the kernarg buffer for the given
242 kernel argument variable. */
243 tree func
= m_parent
.m_cf
->m_func_decl
;
244 /* __args is the first parameter in kernel functions. */
245 symbol_base
= DECL_ARGUMENTS (func
);
246 uint64_t offset
= m_parent
.m_cf
->kernel_arg_offset (arg_symbol
);
248 const_offset
= build_int_cst (size_type_node
, offset
);
250 else if (segment
== BRIG_SEGMENT_GROUP
)
253 = m_parent
.m_cf
->group_variable_segment_offset (var_name
);
254 const_offset
= build_int_cst (size_type_node
, offset
);
256 /* If it's a local group variable reference, substract the local
257 group segment offset to get the group base ptr offset. */
258 if (m_parent
.m_cf
->m_local_group_variables
.has_variable (var_name
))
260 = build2 (PLUS_EXPR
, uint64_type_node
, const_offset
,
261 convert (uint64_type_node
,
262 m_parent
.m_cf
->m_group_local_offset_arg
));
265 else if (segment
== BRIG_SEGMENT_PRIVATE
|| segment
== BRIG_SEGMENT_SPILL
)
267 uint32_t offset
= m_parent
.private_variable_segment_offset (var_name
);
269 /* Compute the offset to the work item's copy:
271 single-wi-offset * local_size + wiflatid * varsize
273 This way the work items have the same variable in
274 successive elements to each other in the segment,
275 helping to achieve autovectorization of loads/stores
278 tree_stl_vec uint32_0
279 = tree_stl_vec (1, build_int_cst (uint32_type_node
, 0));
281 tree_stl_vec uint32_1
282 = tree_stl_vec (1, build_int_cst (uint32_type_node
, 1));
284 tree_stl_vec uint32_2
285 = tree_stl_vec (1, build_int_cst (uint32_type_node
, 2));
288 = build2 (MULT_EXPR
, uint32_type_node
,
289 m_parent
.m_cf
->expand_or_call_builtin
290 (BRIG_OPCODE_WORKGROUPSIZE
, BRIG_TYPE_U32
,
291 uint32_type_node
, uint32_0
),
292 m_parent
.m_cf
->expand_or_call_builtin
293 (BRIG_OPCODE_WORKGROUPSIZE
, BRIG_TYPE_U32
,
294 uint32_type_node
, uint32_1
));
297 = build2 (MULT_EXPR
, uint32_type_node
,
298 m_parent
.m_cf
->expand_or_call_builtin
299 (BRIG_OPCODE_WORKGROUPSIZE
, BRIG_TYPE_U32
,
300 uint32_type_node
, uint32_2
),
304 = build2 (MULT_EXPR
, uint32_type_node
,
305 build_int_cst (uint32_type_node
, offset
), local_size
);
307 tree_stl_vec operands
;
309 = build2 (MULT_EXPR
, uint32_type_node
,
310 build_int_cst (uint32_type_node
,
311 m_parent
.private_variable_size (var_name
)),
312 m_parent
.m_cf
->expand_or_call_builtin
313 (BRIG_OPCODE_WORKITEMFLATID
, BRIG_TYPE_U32
,
314 uint32_type_node
, operands
));
317 = build2 (PLUS_EXPR
, uint32_type_node
, var_region
, pos
);
319 /* In case of LDA this is returned directly as an integer value.
320 For other mem-related instructions, we will convert this segment
321 offset to a flat address by adding it as an offset to a (private
322 or group) base pointer later on. Same applies to group_var_offset. */
324 = m_parent
.m_cf
->add_temp_var ("priv_var_offset",
325 convert (size_type_node
,
328 else if (segment
== BRIG_SEGMENT_ARG
)
331 if (m_parent
.m_cf
->m_ret_value_brig_var
== arg_symbol
)
332 arg_var_decl
= m_parent
.m_cf
->m_ret_temp
;
334 arg_var_decl
= m_parent
.m_cf
->arg_variable (arg_symbol
);
336 gcc_assert (arg_var_decl
!= NULL_TREE
);
338 tree ptype
= build_pointer_type (instr_type
);
340 if (arg_symbol
->type
& BRIG_TYPE_ARRAY
)
343 /* Two different type of array references in case of arguments
344 depending where they are referred at. In the caller (argument
345 segment), the reference is to an array object and
346 in the callee, the array object has been passed as a pointer
347 to the array object. */
349 if (POINTER_TYPE_P (TREE_TYPE (arg_var_decl
)))
350 symbol_base
= build_resize_convert_view (ptype
, arg_var_decl
);
353 /* In case we are referring to an array (the argument in
354 call site), use its element zero as the base address. */
356 = build4 (ARRAY_REF
, TREE_TYPE (TREE_TYPE (arg_var_decl
)),
357 arg_var_decl
, integer_zero_node
, NULL_TREE
,
359 symbol_base
= build1 (ADDR_EXPR
, ptype
, element_zero
);
363 symbol_base
= build1 (ADDR_EXPR
, ptype
, arg_var_decl
);
367 tree global_var_decl
= m_parent
.global_variable (var_name
);
369 /* In case the global variable hasn't been defined (yet),
370 use the host def indirection ptr variable. */
371 if (global_var_decl
== NULL_TREE
)
373 std::string host_ptr_name
374 = std::string (PHSA_HOST_DEF_PTR_PREFIX
) + var_name
;
375 tree host_defined_ptr
= m_parent
.global_variable (host_ptr_name
);
376 gcc_assert (host_defined_ptr
!= NULL_TREE
);
377 symbol_base
= host_defined_ptr
;
381 gcc_assert (global_var_decl
!= NULL_TREE
);
383 tree ptype
= build_pointer_type (instr_type
);
384 symbol_base
= build1 (ADDR_EXPR
, ptype
, global_var_decl
);
389 if (brig_inst
.opcode
!= BRIG_OPCODE_LDA
)
391 /* In case of lda_* we want to return the segment address because it's
392 used as a value, perhaps in address computation and later converted
393 explicitly to a flat address.
395 In case of other instructions with memory operands we produce the flat
396 address directly here (assuming the target does not have a separate
397 address space for group/private segments for now). */
398 if (segment
== BRIG_SEGMENT_GROUP
)
399 symbol_base
= m_parent
.m_cf
->m_group_base_arg
;
400 else if (segment
== BRIG_SEGMENT_PRIVATE
401 || segment
== BRIG_SEGMENT_SPILL
)
403 if (symbol_base
!= NULL_TREE
)
404 symbol_base
= build2 (POINTER_PLUS_EXPR
, ptr_type_node
,
405 m_parent
.m_cf
->m_private_base_arg
,
408 symbol_base
= m_parent
.m_cf
->m_private_base_arg
;
412 if (addr_operand
.reg
!= 0)
414 const BrigOperandRegister
*mem_base_reg
415 = (const BrigOperandRegister
*) m_parent
.get_brig_operand_entry
417 tree base_reg_var
= m_parent
.m_cf
->get_m_var_declfor_reg (mem_base_reg
);
418 tree as_uint
= build_reinterpret_to_uint (base_reg_var
);
419 var_offset
= convert_to_pointer (ptr_type_node
, as_uint
);
421 gcc_assert (var_offset
!= NULL_TREE
);
423 /* The pointer type we use to access the memory. Should be of the
424 width of the load/store instruction, not the target/data
426 tree ptype
= build_pointer_type (instr_type
);
428 gcc_assert (ptype
!= NULL_TREE
);
430 tree addr
= NULL_TREE
;
431 if (symbol_base
!= NULL_TREE
&& var_offset
!= NULL_TREE
)
432 /* The most complex addressing mode: symbol + reg [+ const offset]. */
433 addr
= build2 (POINTER_PLUS_EXPR
, ptr_type_node
,
434 convert (ptr_type_node
, symbol_base
),
435 convert (size_type_node
, var_offset
));
436 else if (var_offset
!= NULL
)
438 else if (symbol_base
!= NULL
)
441 if (const_offset
!= NULL_TREE
)
443 if (addr
== NULL_TREE
)
444 /* At least direct module-scope global group symbol access with LDA
445 has only the const_offset. Group base ptr is not added as LDA should
446 return the segment address, not the flattened one. */
449 addr
= build2 (POINTER_PLUS_EXPR
, ptr_type_node
,
450 addr
, convert (size_type_node
, const_offset
));
453 /* We might have two const offsets in case of group or private arrays
454 which have the first offset to the incoming group/private pointer
455 arg, and the second one an offset to it. It's also legal to have
456 a reference with a zero constant offset but no symbol. I've seen
457 codes that reference kernarg segment like this. Thus, if at this
458 point there is no address expression at all we assume it's an
459 access to offset 0. */
460 uint64_t offs
= gccbrig_to_uint64_t (addr_operand
.offset
);
461 if (offs
> 0 || addr
== NULL_TREE
)
463 /* In large mode, the offset is treated as 32bits unless it's
464 global, readonly or kernarg address space.
466 http://www.hsafoundation.com/html_spec111/HSA_Library.htm
467 #PRM/Topics/02_ProgModel/small_and_large_machine_models.htm
468 #table_machine_model_data_sizes */
470 int is64b_offset
= segment
== BRIG_SEGMENT_GLOBAL
471 || segment
== BRIG_SEGMENT_READONLY
472 || segment
== BRIG_SEGMENT_KERNARG
;
474 /* The original offset is signed and should be sign
475 extended for the pointer arithmetics. */
476 tree const_offset_2
= is64b_offset
477 ? build_int_cst (size_type_node
, offs
)
478 : convert (long_integer_type_node
,
479 build_int_cst (integer_type_node
, offs
));
481 if (addr
== NULL_TREE
)
482 addr
= const_offset_2
;
484 addr
= build2 (POINTER_PLUS_EXPR
, ptr_type_node
,
485 /* Addr can be a constant offset in case this is
486 a private array access. */
487 convert (ptr_type_node
, addr
),
488 convert (size_type_node
, const_offset_2
));
491 gcc_assert (addr
!= NULL_TREE
);
492 return convert_to_pointer (ptype
, addr
);
495 /* Builds a tree operand with the given OPERAND_INDEX for the given
496 BRIG_INST with the desired tree OPERAND_TYPE. OPERAND_TYPE can
497 be NULL in case the type is forced by the BRIG_INST type. */
500 brig_code_entry_handler::build_tree_operand_from_brig
501 (const BrigInstBase
*brig_inst
, tree operand_type
, size_t operand_index
)
503 const BrigData
*operand_entries
504 = m_parent
.get_brig_data_entry (brig_inst
->operands
);
506 uint32_t operand_offset
507 = ((const uint32_t *) &operand_entries
->bytes
)[operand_index
];
508 const BrigBase
*operand_data
509 = m_parent
.get_brig_operand_entry (operand_offset
);
511 bool inputp
= !gccbrig_hsa_opcode_op_output_p (brig_inst
->opcode
,
513 return build_tree_operand (*brig_inst
, *operand_data
, operand_type
, inputp
);
516 /* Builds a single (scalar) constant initialized element of type
517 ELEMENT_TYPE from the buffer pointed to by NEXT_DATA. */
520 brig_code_entry_handler::build_tree_cst_element
521 (BrigType16_t element_type
, const unsigned char *next_data
) const
524 tree tree_element_type
= gccbrig_tree_type_for_hsa_type (element_type
);
527 switch (element_type
)
531 HOST_WIDE_INT low
= *(const uint16_t *) next_data
;
532 cst
= build_int_cst (uint16_type_node
, low
);
538 ieee_single_format
.decode (&ieee_single_format
, &val
,
539 (const long *) next_data
);
540 cst
= build_real (tree_element_type
, val
);
546 data
[0] = *(const uint32_t *) next_data
;
547 data
[1] = *(const uint32_t *) (next_data
+ 4);
549 ieee_double_format
.decode (&ieee_double_format
, &val
, data
);
550 cst
= build_real (tree_element_type
, val
);
558 HOST_WIDE_INT low
= *(const int64_t *) next_data
;
559 cst
= build_int_cst (tree_element_type
, low
);
567 unsigned HOST_WIDE_INT low
= *(const uint64_t *) next_data
;
568 cst
= build_int_cstu (tree_element_type
, low
);
571 case BRIG_TYPE_SIG64
:
573 unsigned HOST_WIDE_INT low
= *(const uint64_t *) next_data
;
574 cst
= build_int_cstu (uint64_type_node
, low
);
577 case BRIG_TYPE_SIG32
:
579 unsigned HOST_WIDE_INT low
= *(const uint64_t *) next_data
;
580 cst
= build_int_cstu (uint32_type_node
, low
);
590 /* Produce a tree constant type for the given BRIG constant (BRIG_CONST).
591 TYPE should be the forced instruction type, otherwise the type is
592 dictated by the BRIG_CONST. */
595 brig_code_entry_handler::get_tree_cst_for_hsa_operand
596 (const BrigOperandConstantBytes
*brig_const
, tree type
) const
598 const BrigData
*data
= m_parent
.get_brig_data_entry (brig_const
->bytes
);
600 tree cst
= NULL_TREE
;
602 if (type
== NULL_TREE
)
603 type
= gccbrig_tree_type_for_hsa_type (brig_const
->type
);
605 /* The type of a single (scalar) element inside an array,
606 vector or an array of vectors. */
607 BrigType16_t scalar_element_type
608 = brig_const
->type
& BRIG_TYPE_BASE_MASK
;
609 tree tree_element_type
= type
;
611 vec
<constructor_elt
, va_gc
> *constructor_vals
= NULL
;
613 if (TREE_CODE (type
) == ARRAY_TYPE
)
614 tree_element_type
= TREE_TYPE (type
);
616 size_t bytes_left
= data
->byteCount
;
617 const unsigned char *next_data
= data
->bytes
;
618 size_t scalar_element_size
619 = gccbrig_hsa_type_bit_size (scalar_element_type
) / BITS_PER_UNIT
;
621 while (bytes_left
> 0)
623 if (VECTOR_TYPE_P (tree_element_type
))
625 /* In case of vector type elements (or sole vectors),
626 create a vector ctor. */
628 = gccbrig_type_vector_subparts (tree_element_type
);
629 if (bytes_left
< scalar_element_size
* element_count
)
630 fatal_error (UNKNOWN_LOCATION
,
631 "Not enough bytes left for the initializer "
632 "(%lu need %lu).", (unsigned long) bytes_left
,
633 (unsigned long) (scalar_element_size
636 vec
<constructor_elt
, va_gc
> *vec_els
= NULL
;
637 for (size_t i
= 0; i
< element_count
; ++i
)
640 = build_tree_cst_element (scalar_element_type
, next_data
);
641 CONSTRUCTOR_APPEND_ELT (vec_els
, NULL_TREE
, element
);
642 bytes_left
-= scalar_element_size
;
643 next_data
+= scalar_element_size
;
645 cst
= build_vector_from_ctor (tree_element_type
, vec_els
);
649 if (bytes_left
< scalar_element_size
)
650 fatal_error (UNKNOWN_LOCATION
,
651 "Not enough bytes left for the initializer "
652 "(%lu need %lu).", (unsigned long) bytes_left
,
653 (unsigned long) scalar_element_size
);
654 cst
= build_tree_cst_element (scalar_element_type
, next_data
);
655 bytes_left
-= scalar_element_size
;
656 next_data
+= scalar_element_size
;
658 CONSTRUCTOR_APPEND_ELT (constructor_vals
, NULL_TREE
, cst
);
661 if (TREE_CODE (type
) == ARRAY_TYPE
)
662 return build_constructor (type
, constructor_vals
);
667 /* Return the matching tree instruction arithmetics type for the
668 given BRIG_TYPE. The aritmethics type is the one with which
669 computation is done (in contrast to the storage type). F16
670 arithmetics type is emulated using F32 for now. */
673 brig_code_entry_handler::get_tree_expr_type_for_hsa_type
674 (BrigType16_t brig_type
) const
676 BrigType16_t brig_inner_type
= brig_type
& BRIG_TYPE_BASE_MASK
;
677 if (brig_inner_type
== BRIG_TYPE_F16
)
679 if (brig_inner_type
== brig_type
)
680 return m_parent
.s_fp32_type
;
681 size_t element_count
= gccbrig_hsa_type_bit_size (brig_type
) / 16;
682 return build_vector_type (m_parent
.s_fp32_type
, element_count
);
685 return gccbrig_tree_type_for_hsa_type (brig_type
);
688 /* Return the correct GENERIC type for storing comparison results
689 of operand with the type given in SOURCE_TYPE. */
692 brig_code_entry_handler::get_comparison_result_type (tree source_type
)
694 if (VECTOR_TYPE_P (source_type
))
696 size_t element_size
= int_size_in_bytes (TREE_TYPE (source_type
));
697 return build_vector_type
698 (build_nonstandard_boolean_type (element_size
* BITS_PER_UNIT
),
699 gccbrig_type_vector_subparts (source_type
));
702 return gccbrig_tree_type_for_hsa_type (BRIG_TYPE_B1
);
705 /* Creates a FP32 to FP16 conversion call, assuming the source and destination
706 are FP32 type variables. */
709 brig_code_entry_handler::build_f2h_conversion (tree source
)
711 return float_to_half () (*this, source
);
714 /* Creates a FP16 to FP32 conversion call, assuming the source and destination
715 are FP32 type variables. */
718 brig_code_entry_handler::build_h2f_conversion (tree source
)
720 return half_to_float () (*this, source
);
723 /* Builds and "normalizes" the dest and source operands for the instruction
724 execution; converts the input operands to the expected instruction type,
725 performs half to float conversions, constant to correct type variable,
726 and flush to zero (if applicable). */
729 brig_code_entry_handler::build_operands (const BrigInstBase
&brig_inst
)
731 return build_or_analyze_operands (brig_inst
, false);
735 brig_code_entry_handler::analyze_operands (const BrigInstBase
&brig_inst
)
737 build_or_analyze_operands (brig_inst
, true);
740 /* Implements both the build_operands () and analyze_operands () call
741 so changes go in tandem. Performs build_operands () when ANALYZE
742 is false. Otherwise, only analyze operands and return empty
745 If analyzing record each HSA register operand with the
746 corresponding resolved operand tree type to
747 brig_to_generic::m_fn_regs_use_index. */
750 brig_code_entry_handler::
751 build_or_analyze_operands (const BrigInstBase
&brig_inst
, bool analyze
)
755 const BrigBase
*base
= &brig_inst
.base
;
757 if (base
->kind
== BRIG_KIND_INST_MOD
)
759 const BrigInstMod
*mod
= (const BrigInstMod
*) base
;
760 ftz
= mod
->modifier
& BRIG_ALU_FTZ
;
762 else if (base
->kind
== BRIG_KIND_INST_CMP
)
764 const BrigInstCmp
*cmp
= (const BrigInstCmp
*) base
;
765 ftz
= cmp
->modifier
& BRIG_ALU_FTZ
;
768 bool is_vec_instr
= hsa_type_packed_p (brig_inst
.type
);
770 size_t element_count
;
773 BrigType16_t brig_element_type
= brig_inst
.type
& BRIG_TYPE_BASE_MASK
;
774 element_count
= gccbrig_hsa_type_bit_size (brig_inst
.type
)
775 / gccbrig_hsa_type_bit_size (brig_element_type
);
780 bool is_fp16_arith
= false;
784 if (base
->kind
== BRIG_KIND_INST_CMP
)
786 const BrigInstCmp
*cmp_inst
= (const BrigInstCmp
*) base
;
787 src_type
= gccbrig_tree_type_for_hsa_type (cmp_inst
->sourceType
);
788 dest_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
790 = (cmp_inst
->sourceType
& BRIG_TYPE_BASE_MASK
) == BRIG_TYPE_F16
;
792 else if (base
->kind
== BRIG_KIND_INST_SOURCE_TYPE
)
794 const BrigInstSourceType
*src_type_inst
795 = (const BrigInstSourceType
*) base
;
796 src_type
= gccbrig_tree_type_for_hsa_type (src_type_inst
->sourceType
);
797 dest_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
799 = (src_type_inst
->sourceType
& BRIG_TYPE_BASE_MASK
) == BRIG_TYPE_F16
800 && !gccbrig_is_bit_operation (brig_inst
.opcode
);
802 else if (base
->kind
== BRIG_KIND_INST_SEG_CVT
)
804 const BrigInstSegCvt
*seg_cvt_inst
= (const BrigInstSegCvt
*) base
;
805 src_type
= gccbrig_tree_type_for_hsa_type (seg_cvt_inst
->sourceType
);
806 dest_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
808 else if (base
->kind
== BRIG_KIND_INST_MEM
)
810 src_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
811 dest_type
= src_type
;
812 /* With mem instructions we don't want to cast the fp16
813 back and forth between fp32, because the load/stores
814 are not specific to the data type. */
815 is_fp16_arith
= false;
817 else if (base
->kind
== BRIG_KIND_INST_CVT
)
819 const BrigInstCvt
*cvt_inst
= (const BrigInstCvt
*) base
;
821 src_type
= gccbrig_tree_type_for_hsa_type (cvt_inst
->sourceType
);
822 dest_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
826 switch (brig_inst
.opcode
)
828 case BRIG_OPCODE_INITFBAR
:
829 case BRIG_OPCODE_JOINFBAR
:
830 case BRIG_OPCODE_WAITFBAR
:
831 case BRIG_OPCODE_ARRIVEFBAR
:
832 case BRIG_OPCODE_LEAVEFBAR
:
833 case BRIG_OPCODE_RELEASEFBAR
:
834 src_type
= uint32_type_node
;
837 src_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
840 dest_type
= src_type
;
842 = !gccbrig_is_bit_operation (brig_inst
.opcode
)
843 && (brig_inst
.type
& BRIG_TYPE_BASE_MASK
) == BRIG_TYPE_F16
;
846 /* Halfs are a tricky special case: their "storage format" is u16, but
847 scalars are stored in 32b regs while packed f16 are... well packed. */
848 tree half_storage_type
= element_count
> 1
849 ? gccbrig_tree_type_for_hsa_type (brig_inst
.type
)
852 const BrigData
*operand_entries
853 = m_parent
.get_brig_data_entry (brig_inst
.operands
);
854 std::vector
<tree
> operands
;
855 for (size_t i
= 0; i
< operand_entries
->byteCount
/ 4; ++i
)
857 uint32_t operand_offset
= ((const uint32_t *) &operand_entries
->bytes
)[i
];
858 const BrigBase
*operand_data
859 = m_parent
.get_brig_operand_entry (operand_offset
);
862 = gccbrig_hsa_opcode_op_output_p (brig_inst
.opcode
, i
);
864 tree operand_type
= is_output
? dest_type
: src_type
;
866 bool half_to_float
= is_fp16_arith
;
868 /* Special cases for operand types. */
869 if ((brig_inst
.opcode
== BRIG_OPCODE_SHL
870 || brig_inst
.opcode
== BRIG_OPCODE_SHR
)
872 /* The shift amount is always a scalar. */
874 = VECTOR_TYPE_P (src_type
) ? TREE_TYPE (src_type
) : src_type
;
875 else if (brig_inst
.opcode
== BRIG_OPCODE_SHUFFLE
)
878 /* HSAIL shuffle inputs the MASK vector as tightly packed bits
879 while GENERIC VEC_PERM_EXPR expects the mask elements to be
880 of the same size as the elements in the input vectors. Let's
881 cast to a scalar type here and convert to the VEC_PERM_EXPR
882 format in instruction handling. There are no arbitrary bit
883 width int types in GENERIC so we cannot use the original
885 operand_type
= uint32_type_node
;
887 /* Always treat the element as unsigned ints to avoid
888 sign extensions/negative offsets with masks, which
889 are expected to be of the same element type as the
890 data in VEC_PERM_EXPR. With shuffles the data type
891 should not matter as it's a "raw operation". */
892 operand_type
= get_unsigned_int_type (operand_type
);
894 else if (brig_inst
.opcode
== BRIG_OPCODE_PACK
)
897 operand_type
= get_unsigned_int_type (dest_type
);
899 operand_type
= get_unsigned_int_type (TREE_TYPE (dest_type
));
901 operand_type
= uint32_type_node
;
903 else if (brig_inst
.opcode
== BRIG_OPCODE_UNPACK
&& i
== 2)
904 operand_type
= uint32_type_node
;
905 else if (brig_inst
.opcode
== BRIG_OPCODE_SAD
&& i
== 3)
906 operand_type
= uint32_type_node
;
907 else if (brig_inst
.opcode
== BRIG_OPCODE_CLASS
&& i
== 2)
909 operand_type
= uint32_type_node
;
910 half_to_float
= false;
912 else if (brig_inst
.opcode
== BRIG_OPCODE_ACTIVELANEPERMUTE
&& i
== 4)
914 operand_type
= uint32_type_node
;
916 else if (half_to_float
)
917 /* Treat the operands as the storage type at this point. */
918 operand_type
= half_storage_type
;
922 if (operand_data
->kind
== BRIG_KIND_OPERAND_REGISTER
)
924 const BrigOperandRegister
&brig_reg
925 = (const BrigOperandRegister
&) *operand_data
;
926 m_parent
.add_reg_used_as_type (brig_reg
, operand_type
);
931 tree operand
= build_tree_operand (brig_inst
, *operand_data
, operand_type
,
933 gcc_assert (operand
);
935 /* Cast/convert the inputs to correct types as expected by the GENERIC
936 opcode instruction. */
940 operand
= build_h2f_conversion
941 (build_resize_convert_view (half_storage_type
, operand
));
942 else if (TREE_CODE (operand
) != LABEL_DECL
943 && TREE_CODE (operand
) != TREE_VEC
944 && operand_data
->kind
!= BRIG_KIND_OPERAND_ADDRESS
945 && operand_data
->kind
!= BRIG_KIND_OPERAND_OPERAND_LIST
)
947 operand
= build_resize_convert_view (operand_type
, operand
);
949 else if (brig_inst
.opcode
== BRIG_OPCODE_SHUFFLE
)
950 /* Force the operand type to be treated as the raw type. */
951 operand
= build_resize_convert_view (operand_type
, operand
);
953 if (brig_inst
.opcode
== BRIG_OPCODE_CMOV
&& i
== 1)
955 /* gcc expects the lower bit to be 1 (or all ones in case of
956 vectors) while CMOV assumes false iff 0. Convert the input
957 here to what gcc likes by generating
958 'operand = operand != 0'. */
959 tree cmp_res_type
= get_comparison_result_type (operand_type
);
960 operand
= build2 (NE_EXPR
, cmp_res_type
, operand
,
961 build_zero_cst (TREE_TYPE (operand
)));
965 operand
= flush_to_zero (is_fp16_arith
) (*this, operand
);
967 operands
.push_back (operand
);
972 /* Build the GENERIC for assigning the result of an instruction to the result
973 "register" (variable). BRIG_INST is the original brig instruction,
974 OUTPUT the result variable/register, INST_EXPR the one producing the
975 result. Required bitcasts and fp32 to fp16 conversions are added as
979 brig_code_entry_handler::build_output_assignment (const BrigInstBase
&brig_inst
,
980 tree output
, tree inst_expr
)
982 /* The result/input type might be different from the output register
983 variable type (can be any type; see get_m_var_declfor_reg @
984 brig-function.cc). */
985 tree output_type
= TREE_TYPE (output
);
986 bool is_fp16
= (brig_inst
.type
& BRIG_TYPE_BASE_MASK
) == BRIG_TYPE_F16
987 && brig_inst
.base
.kind
!= BRIG_KIND_INST_MEM
988 && !gccbrig_is_bit_operation (brig_inst
.opcode
);
992 const BrigBase
*base
= &brig_inst
.base
;
994 if (m_parent
.m_cf
->is_id_val (inst_expr
))
995 inst_expr
= m_parent
.m_cf
->id_val (inst_expr
);
997 tree input_type
= TREE_TYPE (inst_expr
);
999 m_parent
.m_cf
->add_reg_var_update (output
, inst_expr
);
1001 if (base
->kind
== BRIG_KIND_INST_MOD
)
1003 const BrigInstMod
*mod
= (const BrigInstMod
*) base
;
1004 ftz
= mod
->modifier
& BRIG_ALU_FTZ
;
1006 else if (base
->kind
== BRIG_KIND_INST_CMP
)
1008 const BrigInstCmp
*cmp
= (const BrigInstCmp
*) base
;
1009 ftz
= cmp
->modifier
& BRIG_ALU_FTZ
;
1012 if (TREE_CODE (inst_expr
) == CALL_EXPR
)
1014 tree func_decl
= TREE_OPERAND (TREE_OPERAND (inst_expr
, 1), 0);
1015 input_type
= TREE_TYPE (TREE_TYPE (func_decl
));
1018 if (ftz
&& (VECTOR_FLOAT_TYPE_P (TREE_TYPE (inst_expr
))
1019 || SCALAR_FLOAT_TYPE_P (TREE_TYPE (inst_expr
)) || is_fp16
))
1021 /* Ensure we don't duplicate the arithmetics to the arguments of the bit
1022 field reference operators. */
1023 inst_expr
= m_parent
.m_cf
->add_temp_var ("before_ftz", inst_expr
);
1024 inst_expr
= flush_to_zero (is_fp16
) (*this, inst_expr
);
1029 inst_expr
= m_parent
.m_cf
->add_temp_var ("before_f2h", inst_expr
);
1030 tree f2h_output
= build_f2h_conversion (inst_expr
);
1031 tree conv
= build_resize_convert_view (output_type
, f2h_output
);
1032 tree assign
= build2 (MODIFY_EXPR
, output_type
, output
, conv
);
1033 m_parent
.m_cf
->append_statement (assign
);
1036 else if (VECTOR_TYPE_P (output_type
) && TREE_CODE (output
) == CONSTRUCTOR
)
1038 /* Expand/unpack the input value to the given vector elements. */
1040 tree input
= inst_expr
;
1041 tree element_type
= gccbrig_tree_type_for_hsa_type (brig_inst
.type
);
1043 tree last_assign
= NULL_TREE
;
1044 FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (output
), i
, element
)
1047 = build3 (BIT_FIELD_REF
, element_type
, input
,
1048 TYPE_SIZE (element_type
),
1049 bitsize_int (i
* int_size_in_bytes (element_type
)
1053 = build_output_assignment (brig_inst
, element
, element_ref
);
1059 /* All we do here is to bitcast the result and store it to the
1060 'register' (variable). Mainly need to take care of differing
1062 size_t src_width
= int_size_in_bytes (input_type
);
1063 size_t dst_width
= int_size_in_bytes (output_type
);
1064 tree input
= inst_expr
;
1065 /* Integer results are extended to the target register width, using
1066 the same sign as the inst_expr. */
1067 if (INTEGRAL_TYPE_P (TREE_TYPE (input
)) && src_width
!= dst_width
)
1069 bool unsigned_p
= TYPE_UNSIGNED (TREE_TYPE (input
));
1071 = build_nonstandard_integer_type (dst_width
* BITS_PER_UNIT
,
1073 input
= convert_to_integer (resized_type
, input
);
1075 input
= build_resize_convert_view (output_type
, input
);
1076 tree assign
= build2 (MODIFY_EXPR
, output_type
, output
, input
);
1077 m_parent
.m_cf
->append_statement (assign
);
1083 /* Appends a GENERIC statement (STMT) to the currently constructed function. */
1086 brig_code_entry_handler::append_statement (tree stmt
)
1088 m_parent
.m_cf
->append_statement (stmt
);
1091 /* Visits the element(s) in the OPERAND, calling HANDLER to each of them. */
1094 tree_element_unary_visitor::operator () (brig_code_entry_handler
&handler
,
1097 if (VECTOR_TYPE_P (TREE_TYPE (operand
)))
1099 size_t vec_size
= int_size_in_bytes (TREE_TYPE (operand
));
1100 size_t element_size
= int_size_in_bytes (TREE_TYPE (TREE_TYPE (operand
)));
1101 size_t element_count
= vec_size
/ element_size
;
1103 tree input_element_type
= TREE_TYPE (TREE_TYPE (operand
));
1104 tree output_element_type
= NULL_TREE
;
1106 vec
<constructor_elt
, va_gc
> *constructor_vals
= NULL
;
1107 for (size_t i
= 0; i
< element_count
; ++i
)
1109 tree element
= build3 (BIT_FIELD_REF
, input_element_type
, operand
,
1110 TYPE_SIZE (input_element_type
),
1111 bitsize_int (i
* element_size
1114 tree output
= visit_element (handler
, element
);
1115 output_element_type
= TREE_TYPE (output
);
1117 CONSTRUCTOR_APPEND_ELT (constructor_vals
, NULL_TREE
, output
);
1120 tree vec_type
= build_vector_type (output_element_type
, element_count
);
1122 /* build_constructor creates a vector type which is not a vector_cst
1123 that requires compile time constant elements. */
1124 tree vec
= build_constructor (vec_type
, constructor_vals
);
1126 /* Add a temp variable for readability. */
1127 tree tmp_var
= create_tmp_var (vec_type
, "vec_out");
1129 = build2 (MODIFY_EXPR
, TREE_TYPE (tmp_var
), tmp_var
, vec
);
1130 handler
.append_statement (vec_tmp_assign
);
1134 return visit_element (handler
, operand
);
1137 /* Visits the element pair(s) in the OPERAND0 and OPERAND1, calling HANDLER
1141 tree_element_binary_visitor::operator () (brig_code_entry_handler
&handler
,
1142 tree operand0
, tree operand1
)
1144 if (VECTOR_TYPE_P (TREE_TYPE (operand0
)))
1146 gcc_assert (VECTOR_TYPE_P (TREE_TYPE (operand1
)));
1147 size_t vec_size
= int_size_in_bytes (TREE_TYPE (operand0
));
1149 = int_size_in_bytes (TREE_TYPE (TREE_TYPE (operand0
)));
1150 size_t element_count
= vec_size
/ element_size
;
1152 tree input_element_type
= TREE_TYPE (TREE_TYPE (operand0
));
1153 tree output_element_type
= NULL_TREE
;
1155 vec
<constructor_elt
, va_gc
> *constructor_vals
= NULL
;
1156 for (size_t i
= 0; i
< element_count
; ++i
)
1159 tree element0
= build3 (BIT_FIELD_REF
, input_element_type
, operand0
,
1160 TYPE_SIZE (input_element_type
),
1161 bitsize_int (i
* element_size
1164 tree element1
= build3 (BIT_FIELD_REF
, input_element_type
, operand1
,
1165 TYPE_SIZE (input_element_type
),
1166 bitsize_int (i
* element_size
1169 tree output
= visit_element (handler
, element0
, element1
);
1170 output_element_type
= TREE_TYPE (output
);
1172 CONSTRUCTOR_APPEND_ELT (constructor_vals
, NULL_TREE
, output
);
1175 tree vec_type
= build_vector_type (output_element_type
, element_count
);
1177 /* build_constructor creates a vector type which is not a vector_cst
1178 that requires compile time constant elements. */
1179 tree vec
= build_constructor (vec_type
, constructor_vals
);
1181 /* Add a temp variable for readability. */
1182 tree tmp_var
= create_tmp_var (vec_type
, "vec_out");
1184 = build2 (MODIFY_EXPR
, TREE_TYPE (tmp_var
), tmp_var
, vec
);
1185 handler
.append_statement (vec_tmp_assign
);
1189 return visit_element (handler
, operand0
, operand1
);
1192 /* Generates GENERIC code that flushes the visited element to zero. */
1195 flush_to_zero::visit_element (brig_code_entry_handler
&, tree operand
)
1197 size_t size
= int_size_in_bytes (TREE_TYPE (operand
));
1201 = (m_fp16
) ? builtin_decl_explicit (BUILT_IN_HSAIL_FTZ_F32_F16
) :
1202 builtin_decl_explicit (BUILT_IN_HSAIL_FTZ_F32
);
1204 return call_builtin (built_in
, 1, float_type_node
, float_type_node
,
1209 return call_builtin (builtin_decl_explicit (BUILT_IN_HSAIL_FTZ_F64
), 1,
1210 double_type_node
, double_type_node
, operand
);
1217 /* Generates GENERIC code that converts a single precision float to half
1221 float_to_half::visit_element (brig_code_entry_handler
&caller
, tree operand
)
1223 tree built_in
= builtin_decl_explicit (BUILT_IN_HSAIL_F32_TO_F16
);
1225 tree casted_operand
= build_resize_convert_view (uint32_type_node
, operand
);
1227 tree call
= call_builtin (built_in
, 1, uint16_type_node
, uint32_type_node
,
1230 = create_tmp_var (TREE_TYPE (TREE_TYPE (built_in
)), "fp16out");
1231 tree assign
= build2 (MODIFY_EXPR
, TREE_TYPE (output
), output
, call
);
1232 caller
.append_statement (assign
);
1236 /* Generates GENERIC code that converts a half precision float to single
1240 half_to_float::visit_element (brig_code_entry_handler
&caller
, tree operand
)
1242 tree built_in
= builtin_decl_explicit (BUILT_IN_HSAIL_F16_TO_F32
);
1243 tree truncated_source
= convert_to_integer (uint16_type_node
, operand
);
1246 = call_builtin (built_in
, 1, uint32_type_node
, uint16_type_node
,
1249 tree const_fp32_type
1250 = build_type_variant (brig_to_generic::s_fp32_type
, 1, 0);
1252 tree output
= create_tmp_var (const_fp32_type
, "fp32out");
1254 = build_resize_convert_view (brig_to_generic::s_fp32_type
, call
);
1256 tree assign
= build2 (MODIFY_EXPR
, TREE_TYPE (output
), output
, casted_result
);
1258 caller
.append_statement (assign
);
1263 /* Treats the INPUT as SRC_TYPE and sign or zero extends it to DEST_TYPE. */
1266 brig_code_entry_handler::extend_int (tree input
, tree dest_type
, tree src_type
)
1268 /* Extend integer conversions according to the destination's
1269 ext mode. First we need to clip the input register to
1270 the possible smaller integer size to ensure the correct sign
1272 tree clipped_input
= convert_to_integer (src_type
, input
);
1273 tree conversion_result
;
1275 if (TYPE_UNSIGNED (src_type
))
1277 = convert_to_integer (unsigned_type_for (dest_type
), clipped_input
);
1280 = convert_to_integer (signed_type_for (dest_type
), clipped_input
);
1282 /* Treat the result as unsigned so we do not sign extend to the
1283 register width. For some reason this GENERIC sequence sign
1284 extends to the s register:
1286 D.1541 = (signed char) s1;
1287 D.1542 = (signed short) D.1541;
1288 s0 = (unsigned int) D.1542
1291 /* The converted result is then extended to the target register
1292 width, using the same sign as the destination. */
1293 return convert_to_integer (dest_type
, conversion_result
);
1296 /* Returns the integer constant value of the given node.
1297 If it's a cast, looks into the source of the cast. */
1299 brig_code_entry_handler::int_constant_value (tree node
)
1302 if (TREE_CODE (n
) == VIEW_CONVERT_EXPR
)
1303 n
= TREE_OPERAND (n
, 0);
1304 return int_cst_value (n
);