1 /* brig-mem-inst-handler.cc -- brig memory inst handler
2 Copyright (C) 2016-2020 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 "brig-code-entry-handler.h"
23
24 #include "errors.h"
25 #include "brig-util.h"
26 #include "gimple-expr.h"
27 #include "print-tree.h"
28 #include "tree-pretty-print.h"
29 #include "convert.h"
30 #include "diagnostic-core.h"
31
32 tree
build_mem_access(const BrigInstBase * brig_inst,tree addr,tree data)33 brig_mem_inst_handler::build_mem_access (const BrigInstBase *brig_inst,
34 tree addr, tree data)
35 {
36 bool is_load = brig_inst->opcode == BRIG_OPCODE_LD;
37 bool is_store = brig_inst->opcode == BRIG_OPCODE_ST;
38
39 if (!is_load && !is_store)
40 gcc_unreachable ();
41
42 tree instr_type = gccbrig_tree_type_for_hsa_type (brig_inst->type);
43
44 /* In case of {ld,st}_v{2,4}. Note: since 'register' variables may
45 be any type, even a vector type, we distinguish the registers
46 from operand lists by checking for constructor nodes (which
47 operand lists are represented as). */
48 if (VECTOR_TYPE_P (TREE_TYPE (data)) && TREE_CODE (data) == CONSTRUCTOR)
49 instr_type = TREE_TYPE (data);
50
51 tree ptype = build_pointer_type (instr_type);
52
53 /* The HSAIL mem instructions are unaligned by default.
54 TODO: exploit the align modifier, it should lead to faster code.
55 */
56 tree unaligned_type = build_aligned_type (instr_type, 8);
57
58 /* Create a mem ref from the previous result, without offset. */
59 tree mem_ref
60 = build2 (MEM_REF, unaligned_type, addr, build_int_cst (ptype, 0));
61
62 if (is_load)
63 {
64 /* Add a temporary variable so there won't be multiple
65 reads in case of vector unpack. */
66 mem_ref = m_parent.m_cf->add_temp_var ("mem_read", mem_ref);
67 return build_output_assignment (*brig_inst, data, mem_ref);
68 }
69 else
70 {
71 tree stmt = build2 (MODIFY_EXPR, TREE_TYPE (mem_ref), mem_ref, data);
72 return m_parent.m_cf->append_statement (stmt);
73 }
74 return mem_ref;
75 }
76
77 size_t
operator ()(const BrigBase * base)78 brig_mem_inst_handler::operator () (const BrigBase *base)
79 {
80 const BrigInstBase *brig_inst
81 = (const BrigInstBase *) &((const BrigInstBasic *) base)->base;
82
83 if (brig_inst->opcode == BRIG_OPCODE_ALLOCA)
84 {
85 tree_stl_vec operands = build_operands (*brig_inst);
86 size_t alignment = 1;
87 const BrigInstMem *mem_inst = (const BrigInstMem *) brig_inst;
88 if (mem_inst->align != BRIG_ALIGNMENT_NONE)
89 {
90 alignment = 1 << (mem_inst->align - 1);
91 }
92
93 tree align_opr = build_int_cstu (size_type_node, alignment);
94 tree_stl_vec inputs;
95 inputs.push_back (operands[1]);
96 inputs.push_back (align_opr);
97 tree builtin_call
98 = m_parent.m_cf->expand_or_call_builtin (BRIG_OPCODE_ALLOCA,
99 BRIG_TYPE_U32,
100 uint32_type_node, inputs);
101 build_output_assignment (*brig_inst, operands[0], builtin_call);
102 m_parent.m_cf->m_has_allocas = true;
103 return base->byteCount;
104 }
105
106 tree instr_type = gccbrig_tree_type_for_hsa_type (brig_inst->type);
107
108 const BrigData *operand_entries
109 = m_parent.get_brig_data_entry (brig_inst->operands);
110
111 uint32_t data_operand_offset;
112 memcpy (&data_operand_offset, &operand_entries->bytes, 4);
113
114 const BrigBase *operand
115 = m_parent.get_brig_operand_entry (data_operand_offset);
116
117 const BrigData *operandData = NULL;
118
119 bool is_store = brig_inst->opcode == BRIG_OPCODE_ST;
120
121 bool is_three_element_vector_access
122 = operand->kind == BRIG_KIND_OPERAND_OPERAND_LIST
123 && (operandData = m_parent.get_brig_data_entry
124 (((const BrigOperandOperandList *) operand)->elements))
125 && operandData->byteCount / 4 == 3;
126
127 if (is_three_element_vector_access)
128 {
129 /* We need to scalarize the 3-element vector accesses here
130 because gcc assumes the GENERIC vector datatypes are of two exponent
131 size internally. */
132 size_t bytes = operandData->byteCount;
133 const BrigOperandOffset32_t *operand_ptr
134 = (const BrigOperandOffset32_t *) operandData->bytes;
135
136 uint32_t addr_operand_offset;
137 memcpy (&addr_operand_offset, &operand_entries->bytes + 4, 4);
138
139 const BrigOperandAddress *addr_operand
140 = (const BrigOperandAddress *) m_parent.get_brig_operand_entry
141 (addr_operand_offset);
142
143 tree address_base = build_address_operand (*brig_inst, *addr_operand);
144
145 uint32_t address_offset = 0;
146 while (bytes > 0)
147 {
148 BrigOperandOffset32_t offset = *operand_ptr;
149 const BrigBase *operand_element
150 = m_parent.get_brig_operand_entry (offset);
151 tree data
152 = build_tree_operand (*brig_inst, *operand_element, instr_type);
153
154 tree ptr_offset = build_int_cst (size_type_node, address_offset);
155 tree address = build2 (POINTER_PLUS_EXPR, TREE_TYPE (address_base),
156 address_base, ptr_offset);
157
158 if (is_store && TREE_TYPE (data) != instr_type)
159 data = build_resize_convert_view (instr_type, data);
160
161 build_mem_access (brig_inst, address, data);
162
163 address_offset += int_size_in_bytes (instr_type);
164 ++operand_ptr;
165 bytes -= 4;
166 }
167 }
168 else
169 {
170 tree_stl_vec operands = build_operands (*brig_inst);
171
172 tree &data = operands.at (0);
173 tree &addr = operands.at (1);
174 build_mem_access (brig_inst, addr, data);
175 }
176
177 return base->byteCount;
178 }
179