111
|
1 /* brig2tree.cc -- brig to gcc generic/gimple tree conversion
|
|
2 Copyright (C) 2016-2017 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 <cassert>
|
|
23 #include <iostream>
|
|
24 #include <iomanip>
|
|
25 #include <sstream>
|
|
26
|
|
27 #include "config.h"
|
|
28 #include "system.h"
|
|
29 #include "coretypes.h"
|
|
30 #include "target.h"
|
|
31 #include "function.h"
|
|
32 #include "brig-to-generic.h"
|
|
33 #include "stringpool.h"
|
|
34 #include "tree-iterator.h"
|
|
35 #include "toplev.h"
|
|
36 #include "gimplify.h"
|
|
37 #include "gimple-expr.h"
|
|
38 #include "print-tree.h"
|
|
39 #include "hsa-brig-format.h"
|
|
40 #include "stor-layout.h"
|
|
41 #include "diagnostic-core.h"
|
|
42 #include "brig-code-entry-handler.h"
|
|
43 #include "brig-machine.h"
|
|
44 #include "brig-util.h"
|
|
45 #include "phsa.h"
|
|
46 #include "tree-pretty-print.h"
|
|
47 #include "dumpfile.h"
|
|
48 #include "profile-count.h"
|
|
49 #include "tree-cfg.h"
|
|
50 #include "errors.h"
|
|
51 #include "fold-const.h"
|
|
52 #include "cgraph.h"
|
|
53 #include "dumpfile.h"
|
|
54 #include "tree-pretty-print.h"
|
|
55
|
|
56 extern int gccbrig_verbose;
|
|
57
|
|
58 tree brig_to_generic::s_fp16_type;
|
|
59 tree brig_to_generic::s_fp32_type;
|
|
60 tree brig_to_generic::s_fp64_type;
|
|
61
|
|
62 brig_to_generic::brig_to_generic ()
|
|
63 : m_cf (NULL), m_analyzing (true), m_total_group_segment_usage (0),
|
|
64 m_brig (NULL), m_next_private_offset (0)
|
|
65 {
|
|
66 m_globals = NULL_TREE;
|
|
67
|
|
68 /* Initialize the basic REAL types.
|
|
69 This doesn't work straight away because most of the targets
|
|
70 do not support fp16 natively. Let's by default convert
|
|
71 to fp32 and back before and after each instruction (handle it as
|
|
72 a storage format only), and later add an optimization pass
|
|
73 that removes the extra converts (in case of multiple fp16 ops
|
|
74 in a row). */
|
|
75 s_fp16_type = make_node (REAL_TYPE);
|
|
76 TYPE_PRECISION (s_fp16_type) = 16;
|
|
77 TYPE_SIZE (s_fp16_type) = bitsize_int (16);
|
|
78 TYPE_SIZE_UNIT (s_fp16_type) = size_int (2);
|
|
79 SET_TYPE_ALIGN (s_fp16_type, 16);
|
|
80 layout_type (s_fp16_type);
|
|
81
|
|
82 s_fp32_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_F32);
|
|
83 s_fp64_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_F64);
|
|
84
|
|
85 /* TODO: (machine)query the preferred rounding mode that is set by
|
|
86 the machine by default. This can be redefined by each BRIG module
|
|
87 header. */
|
|
88 m_default_float_rounding_mode = BRIG_ROUND_FLOAT_ZERO;
|
|
89
|
|
90 m_dump_file = dump_begin (TDI_original, &m_dump_flags);
|
|
91 }
|
|
92
|
|
93 class unimplemented_entry_handler : public brig_code_entry_handler
|
|
94 {
|
|
95 public:
|
|
96 unimplemented_entry_handler (brig_to_generic &parent)
|
|
97 : brig_code_entry_handler (parent)
|
|
98 {
|
|
99 }
|
|
100
|
|
101 size_t
|
|
102 operator () (const BrigBase *base)
|
|
103 {
|
|
104 gcc_unreachable ();
|
|
105 return base->byteCount;
|
|
106 }
|
|
107 };
|
|
108
|
|
109 /* Handler for entries that can be (and are) safely skipped for the purposes
|
|
110 of GENERIC generation. */
|
|
111
|
|
112 class skipped_entry_handler : public brig_code_entry_handler
|
|
113 {
|
|
114 public:
|
|
115 skipped_entry_handler (brig_to_generic &parent)
|
|
116 : brig_code_entry_handler (parent)
|
|
117 {
|
|
118 }
|
|
119
|
|
120 size_t
|
|
121 operator () (const BrigBase *base)
|
|
122 {
|
|
123 return base->byteCount;
|
|
124 }
|
|
125 };
|
|
126
|
|
127 /* Helper struct for pairing a BrigKind and a BrigCodeEntryHandler that
|
|
128 should handle its data. */
|
|
129
|
|
130 struct code_entry_handler_info
|
|
131 {
|
|
132 BrigKind kind;
|
|
133 brig_code_entry_handler *handler;
|
|
134 };
|
|
135
|
|
136
|
|
137 /* Finds the BRIG file sections in the currently processed file. */
|
|
138
|
|
139 void
|
|
140 brig_to_generic::find_brig_sections ()
|
|
141 {
|
|
142 m_data = m_code = m_operand = NULL;
|
|
143 const BrigModuleHeader *mheader = (const BrigModuleHeader *) m_brig;
|
|
144
|
|
145 /* Find the positions of the different sections. */
|
|
146 for (uint32_t sec = 0; sec < mheader->sectionCount; ++sec)
|
|
147 {
|
|
148 uint64_t offset
|
|
149 = ((const uint64_t *) (m_brig + mheader->sectionIndex))[sec];
|
|
150
|
|
151 const BrigSectionHeader *section_header
|
|
152 = (const BrigSectionHeader *) (m_brig + offset);
|
|
153
|
|
154 std::string name ((const char *) (§ion_header->name),
|
|
155 section_header->nameLength);
|
|
156
|
|
157 if (sec == BRIG_SECTION_INDEX_DATA && name == "hsa_data")
|
|
158 {
|
|
159 m_data = (const char *) section_header;
|
|
160 m_data_size = section_header->byteCount;
|
|
161 }
|
|
162 else if (sec == BRIG_SECTION_INDEX_CODE && name == "hsa_code")
|
|
163 {
|
|
164 m_code = (const char *) section_header;
|
|
165 m_code_size = section_header->byteCount;
|
|
166 }
|
|
167 else if (sec == BRIG_SECTION_INDEX_OPERAND && name == "hsa_operand")
|
|
168 {
|
|
169 m_operand = (const char *) section_header;
|
|
170 m_operand_size = section_header->byteCount;
|
|
171 }
|
|
172 else
|
|
173 {
|
|
174 gcc_unreachable ();
|
|
175 }
|
|
176 }
|
|
177
|
|
178 if (m_code == NULL)
|
|
179 gcc_unreachable ();
|
|
180 if (m_data == NULL)
|
|
181 gcc_unreachable ();
|
|
182 if (m_operand == NULL)
|
|
183 gcc_unreachable ();
|
|
184
|
|
185 }
|
|
186
|
|
187 /* Does a first pass over the given BRIG to collect data needed for the
|
|
188 actual parsing. Currently this includes only collecting the
|
|
189 group segment variable usage to support the experimental HSA PRM feature
|
|
190 where group variables can be declared also in module and function scope
|
|
191 (in addition to kernel scope).
|
|
192 */
|
|
193
|
|
194 void
|
|
195 brig_to_generic::analyze (const char *brig_blob)
|
|
196 {
|
|
197 const BrigModuleHeader *mheader = (const BrigModuleHeader *) brig_blob;
|
|
198
|
|
199 if (strncmp (mheader->identification, "HSA BRIG", 8) != 0)
|
|
200 fatal_error (UNKNOWN_LOCATION, PHSA_ERROR_PREFIX_INCOMPATIBLE_MODULE
|
|
201 "Unrecognized file format.");
|
|
202 if (mheader->brigMajor != 1 || mheader->brigMinor != 0)
|
|
203 fatal_error (UNKNOWN_LOCATION, PHSA_ERROR_PREFIX_INCOMPATIBLE_MODULE
|
|
204 "BRIG version not supported. BRIG 1.0 required.");
|
|
205
|
|
206 m_brig = brig_blob;
|
|
207
|
|
208 find_brig_sections ();
|
|
209
|
|
210 brig_directive_variable_handler var_handler (*this);
|
|
211 brig_directive_fbarrier_handler fbar_handler (*this);
|
|
212 brig_directive_function_handler func_handler (*this);
|
|
213
|
|
214 /* Need this for grabbing the module names for mangling the
|
|
215 group variable names. */
|
|
216 brig_directive_module_handler module_handler (*this);
|
|
217 skipped_entry_handler skipped_handler (*this);
|
|
218
|
|
219 const BrigSectionHeader *csection_header = (const BrigSectionHeader *) m_code;
|
|
220
|
|
221 code_entry_handler_info handlers[]
|
|
222 = {{BRIG_KIND_DIRECTIVE_VARIABLE, &var_handler},
|
|
223 {BRIG_KIND_DIRECTIVE_FBARRIER, &fbar_handler},
|
|
224 {BRIG_KIND_DIRECTIVE_KERNEL, &func_handler},
|
|
225 {BRIG_KIND_DIRECTIVE_MODULE, &module_handler},
|
|
226 {BRIG_KIND_DIRECTIVE_FUNCTION, &func_handler}};
|
|
227
|
|
228 m_analyzing = true;
|
|
229 for (size_t b = csection_header->headerByteCount; b < m_code_size;)
|
|
230 {
|
|
231 const BrigBase *entry = (const BrigBase *) (m_code + b);
|
|
232
|
|
233 brig_code_entry_handler *handler = &skipped_handler;
|
|
234
|
|
235 if (m_cf != NULL && b >= m_cf->m_brig_def->nextModuleEntry)
|
|
236 {
|
|
237 /* The function definition ended. We can just discard the place
|
|
238 holder function. */
|
|
239 m_total_group_segment_usage += m_cf->m_local_group_variables.size ();
|
|
240 delete m_cf;
|
|
241 m_cf = NULL;
|
|
242 }
|
|
243
|
|
244 /* Find a handler. */
|
|
245 for (size_t i = 0;
|
|
246 i < sizeof (handlers) / sizeof (code_entry_handler_info); ++i)
|
|
247 {
|
|
248 if (handlers[i].kind == entry->kind)
|
|
249 handler = handlers[i].handler;
|
|
250 }
|
|
251
|
|
252 int bytes_processed = (*handler) (entry);
|
|
253 if (bytes_processed == 0)
|
|
254 fatal_error (UNKNOWN_LOCATION, PHSA_ERROR_PREFIX_CORRUPTED_MODULE
|
|
255 "Element with 0 bytes.");
|
|
256 b += bytes_processed;
|
|
257 }
|
|
258
|
|
259 if (m_cf != NULL)
|
|
260 {
|
|
261 m_total_group_segment_usage += m_cf->m_local_group_variables.size ();
|
|
262 delete m_cf;
|
|
263 m_cf = NULL;
|
|
264 }
|
|
265
|
|
266 m_total_group_segment_usage += m_module_group_variables.size ();
|
|
267 m_analyzing = false;
|
|
268 }
|
|
269
|
|
270 /* Parses the given BRIG blob. */
|
|
271
|
|
272 void
|
|
273 brig_to_generic::parse (const char *brig_blob)
|
|
274 {
|
|
275 m_brig = brig_blob;
|
|
276 find_brig_sections ();
|
|
277
|
|
278 brig_basic_inst_handler inst_handler (*this);
|
|
279 brig_branch_inst_handler branch_inst_handler (*this);
|
|
280 brig_cvt_inst_handler cvt_inst_handler (*this);
|
|
281 brig_seg_inst_handler seg_inst_handler (*this);
|
|
282 brig_copy_move_inst_handler copy_move_inst_handler (*this);
|
|
283 brig_signal_inst_handler signal_inst_handler (*this);
|
|
284 brig_atomic_inst_handler atomic_inst_handler (*this);
|
|
285 brig_cmp_inst_handler cmp_inst_handler (*this);
|
|
286 brig_mem_inst_handler mem_inst_handler (*this);
|
|
287 brig_inst_mod_handler inst_mod_handler (*this);
|
|
288 brig_directive_label_handler label_handler (*this);
|
|
289 brig_directive_variable_handler var_handler (*this);
|
|
290 brig_directive_fbarrier_handler fbar_handler (*this);
|
|
291 brig_directive_comment_handler comment_handler (*this);
|
|
292 brig_directive_function_handler func_handler (*this);
|
|
293 brig_directive_control_handler control_handler (*this);
|
|
294 brig_directive_arg_block_handler arg_block_handler (*this);
|
|
295 brig_directive_module_handler module_handler (*this);
|
|
296 brig_lane_inst_handler lane_inst_handler (*this);
|
|
297 brig_queue_inst_handler queue_inst_handler (*this);
|
|
298 skipped_entry_handler skipped_handler (*this);
|
|
299 unimplemented_entry_handler unimplemented_handler (*this);
|
|
300
|
|
301 struct code_entry_handler_info
|
|
302 {
|
|
303 BrigKind kind;
|
|
304 brig_code_entry_handler *handler;
|
|
305 };
|
|
306
|
|
307 /* TODO: Convert to a hash table / map. For now, put the more common
|
|
308 entries to the top to keep the scan fast on average. */
|
|
309 code_entry_handler_info handlers[]
|
|
310 = {{BRIG_KIND_INST_BASIC, &inst_handler},
|
|
311 {BRIG_KIND_INST_CMP, &cmp_inst_handler},
|
|
312 {BRIG_KIND_INST_MEM, &mem_inst_handler},
|
|
313 {BRIG_KIND_INST_MOD, &inst_mod_handler},
|
|
314 {BRIG_KIND_INST_CVT, &cvt_inst_handler},
|
|
315 {BRIG_KIND_INST_SEG_CVT, &seg_inst_handler},
|
|
316 {BRIG_KIND_INST_SEG, &seg_inst_handler},
|
|
317 {BRIG_KIND_INST_ADDR, ©_move_inst_handler},
|
|
318 {BRIG_KIND_INST_SOURCE_TYPE, ©_move_inst_handler},
|
|
319 {BRIG_KIND_INST_ATOMIC, &atomic_inst_handler},
|
|
320 {BRIG_KIND_INST_SIGNAL, &signal_inst_handler},
|
|
321 {BRIG_KIND_INST_BR, &branch_inst_handler},
|
|
322 {BRIG_KIND_INST_LANE, &lane_inst_handler},
|
|
323 {BRIG_KIND_INST_QUEUE, &queue_inst_handler},
|
|
324 /* Assuming fences are not needed. FIXME: call builtins
|
|
325 when porting to a platform where they are. */
|
|
326 {BRIG_KIND_INST_MEM_FENCE, &skipped_handler},
|
|
327 {BRIG_KIND_DIRECTIVE_LABEL, &label_handler},
|
|
328 {BRIG_KIND_DIRECTIVE_VARIABLE, &var_handler},
|
|
329 {BRIG_KIND_DIRECTIVE_ARG_BLOCK_START, &arg_block_handler},
|
|
330 {BRIG_KIND_DIRECTIVE_ARG_BLOCK_END, &arg_block_handler},
|
|
331 {BRIG_KIND_DIRECTIVE_FBARRIER, &fbar_handler},
|
|
332 {BRIG_KIND_DIRECTIVE_COMMENT, &comment_handler},
|
|
333 {BRIG_KIND_DIRECTIVE_KERNEL, &func_handler},
|
|
334 {BRIG_KIND_DIRECTIVE_SIGNATURE, &func_handler},
|
|
335 {BRIG_KIND_DIRECTIVE_FUNCTION, &func_handler},
|
|
336 {BRIG_KIND_DIRECTIVE_INDIRECT_FUNCTION, &func_handler},
|
|
337 {BRIG_KIND_DIRECTIVE_MODULE, &module_handler},
|
|
338 /* Skipping debug locations for now as not needed for conformance. */
|
|
339 {BRIG_KIND_DIRECTIVE_LOC, &skipped_handler},
|
|
340 /* There are no supported pragmas at this moment. */
|
|
341 {BRIG_KIND_DIRECTIVE_PRAGMA, &skipped_handler},
|
|
342 {BRIG_KIND_DIRECTIVE_CONTROL, &control_handler},
|
|
343 {BRIG_KIND_DIRECTIVE_EXTENSION, &skipped_handler},
|
|
344 /* BRIG_KIND_NONE entries are valid anywhere. They can be used
|
|
345 for patching BRIGs before finalization. */
|
|
346 {BRIG_KIND_NONE, &skipped_handler}};
|
|
347
|
|
348 const BrigSectionHeader *csection_header = (const BrigSectionHeader *) m_code;
|
|
349
|
|
350 for (size_t b = csection_header->headerByteCount; b < m_code_size;)
|
|
351 {
|
|
352 const BrigBase *entry = (const BrigBase *) (m_code + b);
|
|
353
|
|
354 brig_code_entry_handler *handler = &unimplemented_handler;
|
|
355
|
|
356 if (m_cf != NULL && b >= m_cf->m_brig_def->nextModuleEntry)
|
|
357 finish_function (); /* The function definition ended. */
|
|
358
|
|
359 /* Find a handler. */
|
|
360 for (size_t i = 0;
|
|
361 i < sizeof (handlers) / sizeof (code_entry_handler_info); ++i)
|
|
362 {
|
|
363 if (handlers[i].kind == entry->kind)
|
|
364 handler = handlers[i].handler;
|
|
365 }
|
|
366 b += (*handler) (entry);
|
|
367 }
|
|
368
|
|
369 finish_function ();
|
|
370 }
|
|
371
|
|
372 const BrigData *
|
|
373 brig_to_generic::get_brig_data_entry (size_t entry_offset) const
|
|
374 {
|
|
375 return (const BrigData *) (m_data + entry_offset);
|
|
376 }
|
|
377
|
|
378 const BrigBase *
|
|
379 brig_to_generic::get_brig_operand_entry (size_t entry_offset) const
|
|
380 {
|
|
381 return (const BrigBase *) (m_operand + entry_offset);
|
|
382 }
|
|
383
|
|
384 const BrigBase *
|
|
385 brig_to_generic::get_brig_code_entry (size_t entry_offset) const
|
|
386 {
|
|
387 return (const BrigBase *) (m_code + entry_offset);
|
|
388 }
|
|
389
|
|
390 void
|
|
391 brig_to_generic::append_global (tree g)
|
|
392 {
|
|
393 if (m_globals == NULL_TREE)
|
|
394 {
|
|
395 m_globals = g;
|
|
396 return;
|
|
397 }
|
|
398 else
|
|
399 {
|
|
400 tree last = tree_last (m_globals);
|
|
401 TREE_CHAIN (last) = g;
|
|
402 }
|
|
403 }
|
|
404
|
|
405 tree
|
|
406 brig_to_generic::global_variable (const std::string &name) const
|
|
407 {
|
|
408 label_index::const_iterator i = m_global_variables.find (name);
|
|
409 if (i == m_global_variables.end ())
|
|
410 return NULL_TREE;
|
|
411 else
|
|
412 return (*i).second;
|
|
413 }
|
|
414
|
|
415 /* Returns a function declaration with the given name. Assumes it has been
|
|
416 created previously via a DirectiveFunction or similar. */
|
|
417
|
|
418 tree
|
|
419 brig_to_generic::function_decl (const std::string &name)
|
|
420 {
|
|
421 label_index::const_iterator i = m_function_index.find (name);
|
|
422 if (i == m_function_index.end ())
|
|
423 return NULL_TREE;
|
|
424 return (*i).second;
|
|
425 }
|
|
426
|
|
427 void
|
|
428 brig_to_generic::add_function_decl (const std::string &name, tree func_decl)
|
|
429 {
|
|
430 m_function_index[name] = func_decl;
|
|
431 }
|
|
432
|
|
433 /* Adds a GENERIC global variable VAR_DECL with the given NAME to the
|
|
434 current module. If we have generated a host def var ptr (a place holder
|
|
435 for variables that are defined by the HSA host code) for this global
|
|
436 variable definition (because there was a declaration earlier which looked
|
|
437 like it might have been a host defined variable), we now have
|
|
438 to assign its address and make it private to allow the references to
|
|
439 point to the defined variable instead. */
|
|
440
|
|
441 void
|
|
442 brig_to_generic::add_global_variable (const std::string &name, tree var_decl)
|
|
443 {
|
|
444 append_global (var_decl);
|
|
445 m_global_variables[name] = var_decl;
|
|
446
|
|
447 std::string host_def_var_name
|
|
448 = std::string (PHSA_HOST_DEF_PTR_PREFIX) + name;
|
|
449 tree host_def_var = global_variable (host_def_var_name);
|
|
450 if (host_def_var == NULL_TREE)
|
|
451 return;
|
|
452
|
|
453 tree ptype = build_pointer_type (TREE_TYPE (var_decl));
|
|
454 tree var_addr = build1 (ADDR_EXPR, ptype, var_decl);
|
|
455
|
|
456 DECL_INITIAL (host_def_var) = var_addr;
|
|
457 TREE_PUBLIC (host_def_var) = 0;
|
|
458 }
|
|
459
|
|
460 /* Adds an indirection pointer for a potential host-defined program scope
|
|
461 variable declaration. */
|
|
462
|
|
463 void
|
|
464 brig_to_generic::add_host_def_var_ptr (const std::string &name, tree var_decl)
|
|
465 {
|
|
466 std::string var_name = std::string (PHSA_HOST_DEF_PTR_PREFIX) + name;
|
|
467
|
|
468 tree name_identifier = get_identifier (var_name.c_str ());
|
|
469
|
|
470 tree ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, name_identifier,
|
|
471 build_pointer_type (TREE_TYPE (var_decl)));
|
|
472 DECL_EXTERNAL (ptr_var) = 0;
|
|
473 DECL_ARTIFICIAL (ptr_var) = 0;
|
|
474
|
|
475 TREE_PUBLIC (ptr_var) = 1;
|
|
476 TREE_USED (ptr_var) = 1;
|
|
477 TREE_ADDRESSABLE (ptr_var) = 1;
|
|
478 TREE_STATIC (ptr_var) = 1;
|
|
479
|
|
480 append_global (ptr_var);
|
|
481 m_global_variables[var_name] = ptr_var;
|
|
482 }
|
|
483
|
|
484 /* Produce a "mangled name" for the given brig function or kernel.
|
|
485 The mangling is used to make unique global symbol name in case of
|
|
486 module scope functions. Program scope functions are not mangled
|
|
487 (except for dropping the leading &), which makes the functions
|
|
488 directly visible for linking using the original function name. */
|
|
489
|
|
490 std::string
|
|
491 brig_to_generic::get_mangled_name
|
|
492 (const BrigDirectiveExecutable *func) const
|
|
493 {
|
|
494 /* Strip the leading &. */
|
|
495 std::string func_name = get_string (func->name).substr (1);
|
|
496 if (func->linkage == BRIG_LINKAGE_MODULE)
|
|
497 {
|
|
498 /* Mangle the module scope function names with the module name and
|
|
499 make them public so they can be queried by the HSA runtime from
|
|
500 the produced binary. Assume it's the currently processed function
|
|
501 we are always referring to. */
|
|
502 func_name = "gccbrig." + m_module_name + "." + func_name;
|
|
503 }
|
|
504 return func_name;
|
|
505 }
|
|
506
|
|
507 std::string
|
|
508 brig_to_generic::get_string (size_t entry_offset) const
|
|
509 {
|
|
510 const BrigData *data_item = get_brig_data_entry (entry_offset);
|
|
511 return std::string ((const char *) &data_item->bytes, data_item->byteCount);
|
|
512 }
|
|
513
|
|
514 /* Adapted from c-semantics.c. */
|
|
515
|
|
516 tree
|
|
517 build_stmt (enum tree_code code, ...)
|
|
518 {
|
|
519 tree ret;
|
|
520 int length, i;
|
|
521 va_list p;
|
|
522 bool side_effects;
|
|
523
|
|
524 /* This function cannot be used to construct variably-sized nodes. */
|
|
525 gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp);
|
|
526
|
|
527 va_start (p, code);
|
|
528
|
|
529 ret = make_node (code);
|
|
530 TREE_TYPE (ret) = void_type_node;
|
|
531 length = TREE_CODE_LENGTH (code);
|
|
532
|
|
533 /* TREE_SIDE_EFFECTS will already be set for statements with
|
|
534 implicit side effects. Here we make sure it is set for other
|
|
535 expressions by checking whether the parameters have side
|
|
536 effects. */
|
|
537
|
|
538 side_effects = false;
|
|
539 for (i = 0; i < length; i++)
|
|
540 {
|
|
541 tree t = va_arg (p, tree);
|
|
542 if (t && !TYPE_P (t))
|
|
543 side_effects |= TREE_SIDE_EFFECTS (t);
|
|
544 TREE_OPERAND (ret, i) = t;
|
|
545 }
|
|
546
|
|
547 TREE_SIDE_EFFECTS (ret) |= side_effects;
|
|
548
|
|
549 va_end (p);
|
|
550 return ret;
|
|
551 }
|
|
552
|
|
553 /* BRIG regs are untyped, but GENERIC is not. We need to add implicit casts
|
|
554 in case treating the operand with an instruction with a type different
|
|
555 than the created reg var type in order to select correct instruction type
|
|
556 later on. This function creates the necessary reinterpret type cast from
|
|
557 a source variable to the destination type. In case no cast is needed to
|
|
558 the same type, SOURCE is returned directly. */
|
|
559
|
|
560 tree
|
|
561 build_reinterpret_cast (tree destination_type, tree source)
|
|
562 {
|
|
563
|
|
564 gcc_assert (source && destination_type && TREE_TYPE (source) != NULL_TREE
|
|
565 && destination_type != NULL_TREE);
|
|
566
|
|
567 tree source_type = TREE_TYPE (source);
|
|
568 if (TREE_CODE (source) == CALL_EXPR)
|
|
569 {
|
|
570 tree func_decl = TREE_OPERAND (TREE_OPERAND (source, 1), 0);
|
|
571 source_type = TREE_TYPE (TREE_TYPE (func_decl));
|
|
572 }
|
|
573
|
|
574 if (destination_type == source_type)
|
|
575 return source;
|
|
576
|
|
577 size_t src_size = int_size_in_bytes (source_type);
|
|
578 size_t dst_size = int_size_in_bytes (destination_type);
|
|
579 if (src_size == dst_size)
|
|
580 return build1 (VIEW_CONVERT_EXPR, destination_type, source);
|
|
581 else if (src_size < dst_size)
|
|
582 {
|
|
583 /* The src_size can be smaller at least with f16 scalars which are
|
|
584 stored to 32b register variables. First convert to an equivalent
|
|
585 size unsigned type, then extend to an unsigned type of the
|
|
586 target width, after which VIEW_CONVERT_EXPR can be used to
|
|
587 force to the target type. */
|
|
588 tree unsigned_temp = build1 (VIEW_CONVERT_EXPR,
|
|
589 get_unsigned_int_type (source_type),
|
|
590 source);
|
|
591 return build1 (VIEW_CONVERT_EXPR, destination_type,
|
|
592 convert (get_unsigned_int_type (destination_type),
|
|
593 unsigned_temp));
|
|
594 }
|
|
595 else
|
|
596 gcc_unreachable ();
|
|
597 return NULL_TREE;
|
|
598 }
|
|
599
|
|
600 /* Returns the finished brig_function for the given generic FUNC_DECL,
|
|
601 or NULL, if not found. */
|
|
602
|
|
603 brig_function *
|
|
604 brig_to_generic::get_finished_function (tree func_decl)
|
|
605 {
|
|
606 std::string func_name
|
|
607 = identifier_to_locale (IDENTIFIER_POINTER (DECL_NAME (func_decl)));
|
|
608 std::map<std::string, brig_function *>::iterator i
|
|
609 = m_finished_functions.find (func_name);
|
|
610 if (i != m_finished_functions.end ())
|
|
611 return (*i).second;
|
|
612 else
|
|
613 return NULL;
|
|
614 }
|
|
615
|
|
616 /* Adds a group variable to a correct book keeping structure depending
|
|
617 on its segment. */
|
|
618
|
|
619 void
|
|
620 brig_to_generic::add_group_variable (const std::string &name, size_t size,
|
|
621 size_t alignment, bool function_scope)
|
|
622 {
|
|
623 /* Module and function scope group region variables are an experimental
|
|
624 feature. We implement module scope group variables with a separate
|
|
625 book keeping inside brig_to_generic which is populated in the 'analyze()'
|
|
626 prepass. This is to ensure we know the group segment offsets when
|
|
627 processing the functions that might refer to them. */
|
|
628 if (!function_scope)
|
|
629 {
|
|
630 if (!m_module_group_variables.has_variable (name))
|
|
631 m_module_group_variables.add (name, size, alignment);
|
|
632 return;
|
|
633 }
|
|
634
|
|
635 if (!m_cf->m_local_group_variables.has_variable (name))
|
|
636 m_cf->m_local_group_variables.add (name, size, alignment);
|
|
637 }
|
|
638
|
|
639 /* Finalizes the currently handled function. Should be called before
|
|
640 setting a new function. */
|
|
641
|
|
642 void
|
|
643 brig_to_generic::finish_function ()
|
|
644 {
|
|
645 if (m_cf == NULL || m_cf->m_func_decl == NULL_TREE)
|
|
646 {
|
|
647 /* It can be a finished func declaration fingerprint, in that case we
|
|
648 don't have m_func_decl. */
|
|
649 m_cf = NULL;
|
|
650 return;
|
|
651 }
|
|
652
|
|
653 if (!m_cf->m_is_kernel)
|
|
654 {
|
|
655 tree bind_expr = m_cf->m_current_bind_expr;
|
|
656 tree stmts = BIND_EXPR_BODY (bind_expr);
|
|
657 m_cf->finish ();
|
|
658 m_cf->emit_metadata (stmts);
|
|
659 dump_function (m_dump_file, m_cf);
|
|
660 gimplify_function_tree (m_cf->m_func_decl);
|
|
661 cgraph_node::finalize_function (m_cf->m_func_decl, true);
|
|
662 }
|
|
663 else
|
|
664 /* Emit the kernel only at the very end so we can analyze the total
|
|
665 group and private memory usage. */
|
|
666 m_kernels.push_back (m_cf);
|
|
667
|
|
668 pop_cfun ();
|
|
669
|
|
670 m_finished_functions[m_cf->m_name] = m_cf;
|
|
671 m_cf = NULL;
|
|
672 }
|
|
673
|
|
674 /* Initializes a new currently handled function. */
|
|
675
|
|
676 void
|
|
677 brig_to_generic::start_function (tree f)
|
|
678 {
|
|
679 if (DECL_STRUCT_FUNCTION (f) == NULL)
|
|
680 push_struct_function (f);
|
|
681 else
|
|
682 push_cfun (DECL_STRUCT_FUNCTION (f));
|
|
683
|
|
684 m_cf->m_func_decl = f;
|
|
685 }
|
|
686
|
|
687 /* Appends a new variable to the current kernel's private segment. */
|
|
688
|
|
689 void
|
|
690 brig_to_generic::append_private_variable (const std::string &name,
|
|
691 size_t size, size_t alignment)
|
|
692 {
|
|
693 /* We need to take care of two cases of alignment with private
|
|
694 variables because of the layout where the same variable for
|
|
695 each work-item is laid out in successive addresses.
|
|
696
|
|
697 1) Ensure the first work-item's variable is in an aligned
|
|
698 offset: */
|
|
699 size_t align_padding = m_next_private_offset % alignment == 0 ?
|
|
700 0 : (alignment - m_next_private_offset % alignment);
|
|
701
|
|
702 /* 2) Each successive per-work-item copy should be aligned.
|
|
703 If the variable has wider alignment than size then we need
|
|
704 to add extra padding to ensure it. The padding must be
|
|
705 included in the size to allow per-work-item offset computation
|
|
706 to find their own aligned copy. */
|
|
707
|
|
708 size_t per_var_padding = size % alignment == 0 ?
|
|
709 0 : (alignment - size % alignment);
|
|
710 m_private_data_sizes[name] = size + per_var_padding;
|
|
711
|
|
712 m_next_private_offset += align_padding;
|
|
713 m_private_offsets[name] = m_next_private_offset;
|
|
714 m_next_private_offset += size + per_var_padding;
|
|
715 }
|
|
716
|
|
717 size_t
|
|
718 brig_to_generic::private_variable_segment_offset
|
|
719 (const std::string &name) const
|
|
720 {
|
|
721 var_offset_table::const_iterator i = m_private_offsets.find (name);
|
|
722 gcc_assert (i != m_private_offsets.end ());
|
|
723 return (*i).second;
|
|
724 }
|
|
725
|
|
726 bool
|
|
727 brig_to_generic::has_private_variable (const std::string &name) const
|
|
728 {
|
|
729 std::map<std::string, size_t>::const_iterator i
|
|
730 = m_private_data_sizes.find (name);
|
|
731 return i != m_private_data_sizes.end ();
|
|
732 }
|
|
733
|
|
734 size_t
|
|
735 brig_to_generic::private_variable_size (const std::string &name) const
|
|
736 {
|
|
737 std::map<std::string, size_t>::const_iterator i
|
|
738 = m_private_data_sizes.find (name);
|
|
739 gcc_assert (i != m_private_data_sizes.end ());
|
|
740 return (*i).second;
|
|
741 }
|
|
742
|
|
743
|
|
744 /* The size of private segment required by a single work-item executing
|
|
745 the currently processed kernel. */
|
|
746
|
|
747 size_t
|
|
748 brig_to_generic::private_segment_size () const
|
|
749 {
|
|
750 return m_next_private_offset;
|
|
751 }
|
|
752
|
|
753 /* Cached builtins indexed by name. */
|
|
754
|
|
755 typedef std::map<std::string, tree> builtin_index;
|
|
756 builtin_index builtin_cache_;
|
|
757
|
|
758 /* Build a call to a builtin function. PDECL is the builtin function to
|
|
759 call. NARGS is the number of input arguments, RETTYPE the built-in
|
|
760 functions return value type, and ... is the list of arguments passed to
|
|
761 the call with type first, then the value. */
|
|
762
|
|
763 tree
|
|
764 call_builtin (tree pdecl, int nargs, tree rettype, ...)
|
|
765 {
|
|
766 if (rettype == error_mark_node)
|
|
767 return error_mark_node;
|
|
768
|
|
769 tree *types = new tree[nargs];
|
|
770 tree *args = new tree[nargs];
|
|
771
|
|
772 va_list ap;
|
|
773 va_start (ap, rettype);
|
|
774 for (int i = 0; i < nargs; ++i)
|
|
775 {
|
|
776 types[i] = va_arg (ap, tree);
|
|
777 tree arg = va_arg (ap, tree);
|
|
778 args[i] = build_reinterpret_cast (types[i], arg);
|
|
779 if (types[i] == error_mark_node || args[i] == error_mark_node)
|
|
780 {
|
|
781 delete[] types;
|
|
782 delete[] args;
|
|
783 va_end (ap);
|
|
784 return error_mark_node;
|
|
785 }
|
|
786 }
|
|
787 va_end (ap);
|
|
788
|
|
789 tree fnptr = build_fold_addr_expr (pdecl);
|
|
790
|
|
791 tree ret = build_call_array (rettype, fnptr, nargs, args);
|
|
792
|
|
793 delete[] types;
|
|
794 delete[] args;
|
|
795
|
|
796 return ret;
|
|
797 }
|
|
798
|
|
799 /* Generate all global declarations. Should be called after the last
|
|
800 BRIG has been fed in. */
|
|
801
|
|
802 void
|
|
803 brig_to_generic::write_globals ()
|
|
804 {
|
|
805 /* Now that the whole BRIG module has been processed, build a launcher
|
|
806 and a metadata section for each built kernel. */
|
|
807 for (size_t i = 0; i < m_kernels.size (); ++i)
|
|
808 {
|
|
809 brig_function *f = m_kernels[i];
|
|
810
|
|
811 /* Finish kernels now that we know the call graphs and their barrier
|
|
812 usage. */
|
|
813 f->finish_kernel ();
|
|
814
|
|
815 dump_function (m_dump_file, f);
|
|
816 gimplify_function_tree (f->m_func_decl);
|
|
817 cgraph_node::finalize_function (f->m_func_decl, true);
|
|
818
|
|
819 f->m_descriptor.is_kernel = 1;
|
|
820 /* TODO: analyze the kernel's actual private and group segment usage
|
|
821 using call graph. Now the mem size is overly
|
|
822 pessimistic in case of multiple kernels in the same module.
|
|
823 */
|
|
824 f->m_descriptor.group_segment_size = m_total_group_segment_usage;
|
|
825 f->m_descriptor.private_segment_size = private_segment_size ();
|
|
826
|
|
827 /* The kernarg size is rounded up to a multiple of 16 according to
|
|
828 the PRM specs. */
|
|
829 f->m_descriptor.kernarg_segment_size = f->m_next_kernarg_offset;
|
|
830 if (f->m_descriptor.kernarg_segment_size % 16 > 0)
|
|
831 f->m_descriptor.kernarg_segment_size
|
|
832 += 16 - f->m_next_kernarg_offset % 16;
|
|
833 f->m_descriptor.kernarg_max_align = f->m_kernarg_max_align;
|
|
834
|
|
835 tree launcher = f->emit_launcher_and_metadata ();
|
|
836
|
|
837 append_global (launcher);
|
|
838
|
|
839 gimplify_function_tree (launcher);
|
|
840 cgraph_node::finalize_function (launcher, true);
|
|
841 pop_cfun ();
|
|
842 }
|
|
843
|
|
844 int no_globals = list_length (m_globals);
|
|
845 tree *vec = new tree[no_globals];
|
|
846
|
|
847 int i = 0;
|
|
848 tree global = m_globals;
|
|
849 while (global)
|
|
850 {
|
|
851 vec[i] = global;
|
|
852 ++i;
|
|
853 global = TREE_CHAIN (global);
|
|
854 }
|
|
855
|
|
856 wrapup_global_declarations (vec, no_globals);
|
|
857
|
|
858 delete[] vec;
|
|
859
|
|
860 }
|
|
861
|
|
862 /* Returns an type with unsigned int elements corresponding to the
|
|
863 size and element count of ORIGINAL_TYPE. */
|
|
864
|
|
865 tree
|
|
866 get_unsigned_int_type (tree original_type)
|
|
867 {
|
|
868 if (VECTOR_TYPE_P (original_type))
|
|
869 {
|
|
870 size_t esize
|
|
871 = int_size_in_bytes (TREE_TYPE (original_type)) * BITS_PER_UNIT;
|
|
872 size_t ecount = TYPE_VECTOR_SUBPARTS (original_type);
|
|
873 return build_vector_type (build_nonstandard_integer_type (esize, true),
|
|
874 ecount);
|
|
875 }
|
|
876 else
|
|
877 return build_nonstandard_integer_type (int_size_in_bytes (original_type)
|
|
878 * BITS_PER_UNIT,
|
|
879 true);
|
|
880 }
|
|
881
|
|
882 void
|
|
883 dump_function (FILE *dump_file, brig_function *f)
|
|
884 {
|
|
885 /* Dump the BRIG-specific tree IR. */
|
|
886 if (dump_file)
|
|
887 {
|
|
888 fprintf (dump_file, "\n;; Function %s", f->m_name.c_str ());
|
|
889 fprintf (dump_file, "\n;; enabled by -%s\n\n",
|
|
890 dump_flag_name (TDI_original));
|
|
891 print_generic_decl (dump_file, f->m_func_decl, 0);
|
|
892 print_generic_expr (dump_file, f->m_current_bind_expr, 0);
|
|
893 fprintf (dump_file, "\n");
|
|
894 }
|
|
895 }
|