]>
Commit | Line | Data |
---|---|---|
60cbb674 TS |
1 | /* OMP constructs' SIMD clone supporting code. |
2 | ||
99dee823 | 3 | Copyright (C) 2005-2021 Free Software Foundation, Inc. |
60cbb674 TS |
4 | |
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 3, or (at your option) any later | |
10 | version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "system.h" | |
23 | #include "coretypes.h" | |
24 | #include "backend.h" | |
25 | #include "target.h" | |
26 | #include "tree.h" | |
27 | #include "gimple.h" | |
28 | #include "cfghooks.h" | |
29 | #include "alloc-pool.h" | |
30 | #include "tree-pass.h" | |
31 | #include "ssa.h" | |
32 | #include "cgraph.h" | |
33 | #include "pretty-print.h" | |
34 | #include "diagnostic-core.h" | |
35 | #include "fold-const.h" | |
36 | #include "stor-layout.h" | |
37 | #include "cfganal.h" | |
38 | #include "gimplify.h" | |
39 | #include "gimple-iterator.h" | |
40 | #include "gimplify-me.h" | |
41 | #include "gimple-walk.h" | |
42 | #include "langhooks.h" | |
43 | #include "tree-cfg.h" | |
44 | #include "tree-into-ssa.h" | |
45 | #include "tree-dfa.h" | |
46 | #include "cfgloop.h" | |
47 | #include "symbol-summary.h" | |
4d99a848 | 48 | #include "ipa-param-manipulation.h" |
60cbb674 | 49 | #include "tree-eh.h" |
ad200580 | 50 | #include "varasm.h" |
314e6352 ML |
51 | #include "stringpool.h" |
52 | #include "attribs.h" | |
848bb6fc | 53 | #include "omp-simd-clone.h" |
60cbb674 | 54 | |
d8f860ef RS |
55 | /* Return the number of elements in vector type VECTYPE, which is associated |
56 | with a SIMD clone. At present these always have a constant length. */ | |
57 | ||
58 | static unsigned HOST_WIDE_INT | |
59 | simd_clone_subparts (tree vectype) | |
60 | { | |
928686b1 | 61 | return TYPE_VECTOR_SUBPARTS (vectype).to_constant (); |
d8f860ef RS |
62 | } |
63 | ||
60cbb674 TS |
64 | /* Allocate a fresh `simd_clone' and return it. NARGS is the number |
65 | of arguments to reserve space for. */ | |
66 | ||
67 | static struct cgraph_simd_clone * | |
68 | simd_clone_struct_alloc (int nargs) | |
69 | { | |
70 | struct cgraph_simd_clone *clone_info; | |
71 | size_t len = (sizeof (struct cgraph_simd_clone) | |
72 | + nargs * sizeof (struct cgraph_simd_clone_arg)); | |
73 | clone_info = (struct cgraph_simd_clone *) | |
74 | ggc_internal_cleared_alloc (len); | |
75 | return clone_info; | |
76 | } | |
77 | ||
78 | /* Make a copy of the `struct cgraph_simd_clone' in FROM to TO. */ | |
79 | ||
80 | static inline void | |
81 | simd_clone_struct_copy (struct cgraph_simd_clone *to, | |
82 | struct cgraph_simd_clone *from) | |
83 | { | |
84 | memcpy (to, from, (sizeof (struct cgraph_simd_clone) | |
85 | + ((from->nargs - from->inbranch) | |
86 | * sizeof (struct cgraph_simd_clone_arg)))); | |
87 | } | |
88 | ||
ff6686d2 MJ |
89 | /* Fill an empty vector ARGS with parameter types of function FNDECL. This |
90 | uses TYPE_ARG_TYPES if available, otherwise falls back to types of | |
60cbb674 TS |
91 | DECL_ARGUMENTS types. */ |
92 | ||
ff6686d2 MJ |
93 | static void |
94 | simd_clone_vector_of_formal_parm_types (vec<tree> *args, tree fndecl) | |
60cbb674 TS |
95 | { |
96 | if (TYPE_ARG_TYPES (TREE_TYPE (fndecl))) | |
ff6686d2 MJ |
97 | { |
98 | push_function_arg_types (args, TREE_TYPE (fndecl)); | |
99 | return; | |
100 | } | |
101 | push_function_arg_decls (args, fndecl); | |
60cbb674 TS |
102 | unsigned int i; |
103 | tree arg; | |
ff6686d2 MJ |
104 | FOR_EACH_VEC_ELT (*args, i, arg) |
105 | (*args)[i] = TREE_TYPE ((*args)[i]); | |
60cbb674 TS |
106 | } |
107 | ||
108 | /* Given a simd function in NODE, extract the simd specific | |
109 | information from the OMP clauses passed in CLAUSES, and return | |
110 | the struct cgraph_simd_clone * if it should be cloned. *INBRANCH_SPECIFIED | |
111 | is set to TRUE if the `inbranch' or `notinbranch' clause specified, | |
112 | otherwise set to FALSE. */ | |
113 | ||
114 | static struct cgraph_simd_clone * | |
115 | simd_clone_clauses_extract (struct cgraph_node *node, tree clauses, | |
116 | bool *inbranch_specified) | |
117 | { | |
ff6686d2 MJ |
118 | auto_vec<tree> args; |
119 | simd_clone_vector_of_formal_parm_types (&args, node->decl); | |
60cbb674 TS |
120 | tree t; |
121 | int n; | |
122 | *inbranch_specified = false; | |
123 | ||
124 | n = args.length (); | |
125 | if (n > 0 && args.last () == void_type_node) | |
126 | n--; | |
127 | ||
60cbb674 TS |
128 | /* Allocate one more than needed just in case this is an in-branch |
129 | clone which will require a mask argument. */ | |
130 | struct cgraph_simd_clone *clone_info = simd_clone_struct_alloc (n + 1); | |
131 | clone_info->nargs = n; | |
60cbb674 TS |
132 | |
133 | if (!clauses) | |
9dc5773f JJ |
134 | goto out; |
135 | ||
60cbb674 TS |
136 | clauses = TREE_VALUE (clauses); |
137 | if (!clauses || TREE_CODE (clauses) != OMP_CLAUSE) | |
9dc5773f | 138 | goto out; |
60cbb674 TS |
139 | |
140 | for (t = clauses; t; t = OMP_CLAUSE_CHAIN (t)) | |
141 | { | |
142 | switch (OMP_CLAUSE_CODE (t)) | |
143 | { | |
144 | case OMP_CLAUSE_INBRANCH: | |
145 | clone_info->inbranch = 1; | |
146 | *inbranch_specified = true; | |
147 | break; | |
148 | case OMP_CLAUSE_NOTINBRANCH: | |
149 | clone_info->inbranch = 0; | |
150 | *inbranch_specified = true; | |
151 | break; | |
152 | case OMP_CLAUSE_SIMDLEN: | |
153 | clone_info->simdlen | |
154 | = TREE_INT_CST_LOW (OMP_CLAUSE_SIMDLEN_EXPR (t)); | |
155 | break; | |
156 | case OMP_CLAUSE_LINEAR: | |
157 | { | |
158 | tree decl = OMP_CLAUSE_DECL (t); | |
159 | tree step = OMP_CLAUSE_LINEAR_STEP (t); | |
160 | int argno = TREE_INT_CST_LOW (decl); | |
161 | if (OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (t)) | |
162 | { | |
163 | enum cgraph_simd_clone_arg_type arg_type; | |
164 | if (TREE_CODE (args[argno]) == REFERENCE_TYPE) | |
165 | switch (OMP_CLAUSE_LINEAR_KIND (t)) | |
166 | { | |
167 | case OMP_CLAUSE_LINEAR_REF: | |
168 | arg_type | |
169 | = SIMD_CLONE_ARG_TYPE_LINEAR_REF_VARIABLE_STEP; | |
170 | break; | |
171 | case OMP_CLAUSE_LINEAR_UVAL: | |
172 | arg_type | |
173 | = SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP; | |
174 | break; | |
175 | case OMP_CLAUSE_LINEAR_VAL: | |
176 | case OMP_CLAUSE_LINEAR_DEFAULT: | |
177 | arg_type | |
178 | = SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP; | |
179 | break; | |
180 | default: | |
181 | gcc_unreachable (); | |
182 | } | |
183 | else | |
184 | arg_type = SIMD_CLONE_ARG_TYPE_LINEAR_VARIABLE_STEP; | |
185 | clone_info->args[argno].arg_type = arg_type; | |
186 | clone_info->args[argno].linear_step = tree_to_shwi (step); | |
187 | gcc_assert (clone_info->args[argno].linear_step >= 0 | |
188 | && clone_info->args[argno].linear_step < n); | |
189 | } | |
190 | else | |
191 | { | |
192 | if (POINTER_TYPE_P (args[argno])) | |
193 | step = fold_convert (ssizetype, step); | |
194 | if (!tree_fits_shwi_p (step)) | |
195 | { | |
196 | warning_at (OMP_CLAUSE_LOCATION (t), 0, | |
197 | "ignoring large linear step"); | |
60cbb674 TS |
198 | return NULL; |
199 | } | |
200 | else if (integer_zerop (step)) | |
201 | { | |
202 | warning_at (OMP_CLAUSE_LOCATION (t), 0, | |
203 | "ignoring zero linear step"); | |
60cbb674 TS |
204 | return NULL; |
205 | } | |
206 | else | |
207 | { | |
208 | enum cgraph_simd_clone_arg_type arg_type; | |
209 | if (TREE_CODE (args[argno]) == REFERENCE_TYPE) | |
210 | switch (OMP_CLAUSE_LINEAR_KIND (t)) | |
211 | { | |
212 | case OMP_CLAUSE_LINEAR_REF: | |
213 | arg_type | |
214 | = SIMD_CLONE_ARG_TYPE_LINEAR_REF_CONSTANT_STEP; | |
215 | break; | |
216 | case OMP_CLAUSE_LINEAR_UVAL: | |
217 | arg_type | |
218 | = SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP; | |
219 | break; | |
220 | case OMP_CLAUSE_LINEAR_VAL: | |
221 | case OMP_CLAUSE_LINEAR_DEFAULT: | |
222 | arg_type | |
223 | = SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP; | |
224 | break; | |
225 | default: | |
226 | gcc_unreachable (); | |
227 | } | |
228 | else | |
229 | arg_type = SIMD_CLONE_ARG_TYPE_LINEAR_CONSTANT_STEP; | |
230 | clone_info->args[argno].arg_type = arg_type; | |
231 | clone_info->args[argno].linear_step = tree_to_shwi (step); | |
232 | } | |
233 | } | |
234 | break; | |
235 | } | |
236 | case OMP_CLAUSE_UNIFORM: | |
237 | { | |
238 | tree decl = OMP_CLAUSE_DECL (t); | |
239 | int argno = tree_to_uhwi (decl); | |
240 | clone_info->args[argno].arg_type | |
241 | = SIMD_CLONE_ARG_TYPE_UNIFORM; | |
242 | break; | |
243 | } | |
244 | case OMP_CLAUSE_ALIGNED: | |
245 | { | |
b33d65e3 JJ |
246 | /* Ignore aligned (x) for declare simd, for the ABI we really |
247 | need an alignment specified. */ | |
248 | if (OMP_CLAUSE_ALIGNED_ALIGNMENT (t) == NULL_TREE) | |
249 | break; | |
60cbb674 TS |
250 | tree decl = OMP_CLAUSE_DECL (t); |
251 | int argno = tree_to_uhwi (decl); | |
252 | clone_info->args[argno].alignment | |
253 | = TREE_INT_CST_LOW (OMP_CLAUSE_ALIGNED_ALIGNMENT (t)); | |
254 | break; | |
255 | } | |
256 | default: | |
257 | break; | |
258 | } | |
259 | } | |
9dc5773f JJ |
260 | |
261 | out: | |
262 | if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (node->decl)))) | |
263 | { | |
264 | warning_at (DECL_SOURCE_LOCATION (node->decl), 0, | |
265 | "ignoring %<#pragma omp declare simd%> on function " | |
266 | "with %<_Atomic%> qualified return type"); | |
9dc5773f JJ |
267 | return NULL; |
268 | } | |
269 | ||
270 | for (unsigned int argno = 0; argno < clone_info->nargs; argno++) | |
271 | if (TYPE_ATOMIC (args[argno]) | |
272 | && clone_info->args[argno].arg_type != SIMD_CLONE_ARG_TYPE_UNIFORM) | |
273 | { | |
274 | warning_at (DECL_SOURCE_LOCATION (node->decl), 0, | |
275 | "ignoring %<#pragma omp declare simd%> on function " | |
276 | "with %<_Atomic%> qualified non-%<uniform%> argument"); | |
277 | args.release (); | |
278 | return NULL; | |
279 | } | |
280 | ||
60cbb674 TS |
281 | return clone_info; |
282 | } | |
283 | ||
284 | /* Given a SIMD clone in NODE, calculate the characteristic data | |
285 | type and return the coresponding type. The characteristic data | |
286 | type is computed as described in the Intel Vector ABI. */ | |
287 | ||
288 | static tree | |
289 | simd_clone_compute_base_data_type (struct cgraph_node *node, | |
290 | struct cgraph_simd_clone *clone_info) | |
291 | { | |
292 | tree type = integer_type_node; | |
293 | tree fndecl = node->decl; | |
294 | ||
295 | /* a) For non-void function, the characteristic data type is the | |
296 | return type. */ | |
297 | if (TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE) | |
298 | type = TREE_TYPE (TREE_TYPE (fndecl)); | |
299 | ||
300 | /* b) If the function has any non-uniform, non-linear parameters, | |
301 | then the characteristic data type is the type of the first | |
302 | such parameter. */ | |
303 | else | |
304 | { | |
ff6686d2 MJ |
305 | auto_vec<tree> map; |
306 | simd_clone_vector_of_formal_parm_types (&map, fndecl); | |
60cbb674 TS |
307 | for (unsigned int i = 0; i < clone_info->nargs; ++i) |
308 | if (clone_info->args[i].arg_type == SIMD_CLONE_ARG_TYPE_VECTOR) | |
309 | { | |
310 | type = map[i]; | |
311 | break; | |
312 | } | |
60cbb674 TS |
313 | } |
314 | ||
315 | /* c) If the characteristic data type determined by a) or b) above | |
316 | is struct, union, or class type which is pass-by-value (except | |
317 | for the type that maps to the built-in complex data type), the | |
318 | characteristic data type is int. */ | |
319 | if (RECORD_OR_UNION_TYPE_P (type) | |
320 | && !aggregate_value_p (type, NULL) | |
321 | && TREE_CODE (type) != COMPLEX_TYPE) | |
322 | return integer_type_node; | |
323 | ||
324 | /* d) If none of the above three classes is applicable, the | |
325 | characteristic data type is int. */ | |
326 | ||
327 | return type; | |
328 | ||
329 | /* e) For Intel Xeon Phi native and offload compilation, if the | |
330 | resulting characteristic data type is 8-bit or 16-bit integer | |
331 | data type, the characteristic data type is int. */ | |
332 | /* Well, we don't handle Xeon Phi yet. */ | |
333 | } | |
334 | ||
335 | static tree | |
336 | simd_clone_mangle (struct cgraph_node *node, | |
337 | struct cgraph_simd_clone *clone_info) | |
338 | { | |
339 | char vecsize_mangle = clone_info->vecsize_mangle; | |
340 | char mask = clone_info->inbranch ? 'M' : 'N'; | |
abe93733 | 341 | poly_uint64 simdlen = clone_info->simdlen; |
60cbb674 TS |
342 | unsigned int n; |
343 | pretty_printer pp; | |
344 | ||
abe93733 | 345 | gcc_assert (vecsize_mangle && maybe_ne (simdlen, 0U)); |
60cbb674 TS |
346 | |
347 | pp_string (&pp, "_ZGV"); | |
348 | pp_character (&pp, vecsize_mangle); | |
349 | pp_character (&pp, mask); | |
abe93733 YY |
350 | /* For now, simdlen is always constant, while variable simdlen pp 'n'. */ |
351 | unsigned int len = simdlen.to_constant (); | |
352 | pp_decimal_int (&pp, (len)); | |
60cbb674 TS |
353 | |
354 | for (n = 0; n < clone_info->nargs; ++n) | |
355 | { | |
356 | struct cgraph_simd_clone_arg arg = clone_info->args[n]; | |
357 | ||
358 | switch (arg.arg_type) | |
359 | { | |
360 | case SIMD_CLONE_ARG_TYPE_UNIFORM: | |
361 | pp_character (&pp, 'u'); | |
362 | break; | |
363 | case SIMD_CLONE_ARG_TYPE_LINEAR_CONSTANT_STEP: | |
364 | pp_character (&pp, 'l'); | |
365 | goto mangle_linear; | |
366 | case SIMD_CLONE_ARG_TYPE_LINEAR_REF_CONSTANT_STEP: | |
367 | pp_character (&pp, 'R'); | |
368 | goto mangle_linear; | |
369 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP: | |
370 | pp_character (&pp, 'L'); | |
371 | goto mangle_linear; | |
372 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP: | |
373 | pp_character (&pp, 'U'); | |
374 | goto mangle_linear; | |
375 | mangle_linear: | |
376 | gcc_assert (arg.linear_step != 0); | |
377 | if (arg.linear_step > 1) | |
378 | pp_unsigned_wide_integer (&pp, arg.linear_step); | |
379 | else if (arg.linear_step < 0) | |
380 | { | |
381 | pp_character (&pp, 'n'); | |
382 | pp_unsigned_wide_integer (&pp, (-(unsigned HOST_WIDE_INT) | |
383 | arg.linear_step)); | |
384 | } | |
385 | break; | |
386 | case SIMD_CLONE_ARG_TYPE_LINEAR_VARIABLE_STEP: | |
387 | pp_string (&pp, "ls"); | |
388 | pp_unsigned_wide_integer (&pp, arg.linear_step); | |
389 | break; | |
390 | case SIMD_CLONE_ARG_TYPE_LINEAR_REF_VARIABLE_STEP: | |
391 | pp_string (&pp, "Rs"); | |
392 | pp_unsigned_wide_integer (&pp, arg.linear_step); | |
393 | break; | |
394 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP: | |
395 | pp_string (&pp, "Ls"); | |
396 | pp_unsigned_wide_integer (&pp, arg.linear_step); | |
397 | break; | |
398 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP: | |
399 | pp_string (&pp, "Us"); | |
400 | pp_unsigned_wide_integer (&pp, arg.linear_step); | |
401 | break; | |
402 | default: | |
403 | pp_character (&pp, 'v'); | |
404 | } | |
405 | if (arg.alignment) | |
406 | { | |
407 | pp_character (&pp, 'a'); | |
408 | pp_decimal_int (&pp, arg.alignment); | |
409 | } | |
410 | } | |
411 | ||
412 | pp_underscore (&pp); | |
413 | const char *str = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl)); | |
414 | if (*str == '*') | |
415 | ++str; | |
416 | pp_string (&pp, str); | |
417 | str = pp_formatted_text (&pp); | |
418 | ||
419 | /* If there already is a SIMD clone with the same mangled name, don't | |
420 | add another one. This can happen e.g. for | |
421 | #pragma omp declare simd | |
422 | #pragma omp declare simd simdlen(8) | |
423 | int foo (int, int); | |
424 | if the simdlen is assumed to be 8 for the first one, etc. */ | |
425 | for (struct cgraph_node *clone = node->simd_clones; clone; | |
426 | clone = clone->simdclone->next_clone) | |
a01f151f | 427 | if (id_equal (DECL_ASSEMBLER_NAME (clone->decl), str)) |
60cbb674 TS |
428 | return NULL_TREE; |
429 | ||
430 | return get_identifier (str); | |
431 | } | |
432 | ||
433 | /* Create a simd clone of OLD_NODE and return it. */ | |
434 | ||
435 | static struct cgraph_node * | |
436 | simd_clone_create (struct cgraph_node *old_node) | |
437 | { | |
438 | struct cgraph_node *new_node; | |
439 | if (old_node->definition) | |
440 | { | |
441 | if (!old_node->has_gimple_body_p ()) | |
442 | return NULL; | |
443 | old_node->get_body (); | |
444 | new_node = old_node->create_version_clone_with_body (vNULL, NULL, NULL, | |
ff6686d2 | 445 | NULL, NULL, |
60cbb674 TS |
446 | "simdclone"); |
447 | } | |
448 | else | |
449 | { | |
450 | tree old_decl = old_node->decl; | |
451 | tree new_decl = copy_node (old_node->decl); | |
7958186b MP |
452 | DECL_NAME (new_decl) = clone_function_name_numbered (old_decl, |
453 | "simdclone"); | |
60cbb674 TS |
454 | SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); |
455 | SET_DECL_RTL (new_decl, NULL); | |
456 | DECL_STATIC_CONSTRUCTOR (new_decl) = 0; | |
457 | DECL_STATIC_DESTRUCTOR (new_decl) = 0; | |
458 | new_node = old_node->create_version_clone (new_decl, vNULL, NULL); | |
459 | if (old_node->in_other_partition) | |
460 | new_node->in_other_partition = 1; | |
461 | } | |
462 | if (new_node == NULL) | |
463 | return new_node; | |
464 | ||
4d732405 | 465 | set_decl_built_in_function (new_node->decl, NOT_BUILT_IN, 0); |
60cbb674 | 466 | TREE_PUBLIC (new_node->decl) = TREE_PUBLIC (old_node->decl); |
ad200580 JJ |
467 | DECL_COMDAT (new_node->decl) = DECL_COMDAT (old_node->decl); |
468 | DECL_WEAK (new_node->decl) = DECL_WEAK (old_node->decl); | |
469 | DECL_EXTERNAL (new_node->decl) = DECL_EXTERNAL (old_node->decl); | |
470 | DECL_VISIBILITY_SPECIFIED (new_node->decl) | |
471 | = DECL_VISIBILITY_SPECIFIED (old_node->decl); | |
472 | DECL_VISIBILITY (new_node->decl) = DECL_VISIBILITY (old_node->decl); | |
473 | DECL_DLLIMPORT_P (new_node->decl) = DECL_DLLIMPORT_P (old_node->decl); | |
474 | if (DECL_ONE_ONLY (old_node->decl)) | |
475 | make_decl_one_only (new_node->decl, DECL_ASSEMBLER_NAME (new_node->decl)); | |
476 | ||
477 | /* The method cgraph_version_clone_with_body () will force the new | |
478 | symbol local. Undo this, and inherit external visibility from | |
60cbb674 | 479 | the old node. */ |
87f94429 | 480 | new_node->local = old_node->local; |
60cbb674 | 481 | new_node->externally_visible = old_node->externally_visible; |
7a50e708 | 482 | new_node->calls_declare_variant_alt = old_node->calls_declare_variant_alt; |
60cbb674 TS |
483 | |
484 | return new_node; | |
485 | } | |
486 | ||
487 | /* Adjust the return type of the given function to its appropriate | |
488 | vector counterpart. Returns a simd array to be used throughout the | |
489 | function as a return value. */ | |
490 | ||
491 | static tree | |
492 | simd_clone_adjust_return_type (struct cgraph_node *node) | |
493 | { | |
494 | tree fndecl = node->decl; | |
495 | tree orig_rettype = TREE_TYPE (TREE_TYPE (fndecl)); | |
abe93733 | 496 | poly_uint64 veclen; |
60cbb674 TS |
497 | tree t; |
498 | ||
499 | /* Adjust the function return type. */ | |
500 | if (orig_rettype == void_type_node) | |
501 | return NULL_TREE; | |
60cbb674 TS |
502 | t = TREE_TYPE (TREE_TYPE (fndecl)); |
503 | if (INTEGRAL_TYPE_P (t) || POINTER_TYPE_P (t)) | |
504 | veclen = node->simdclone->vecsize_int; | |
505 | else | |
506 | veclen = node->simdclone->vecsize_float; | |
abe93733 YY |
507 | veclen = exact_div (veclen, GET_MODE_BITSIZE (SCALAR_TYPE_MODE (t))); |
508 | if (multiple_p (veclen, node->simdclone->simdlen)) | |
60cbb674 TS |
509 | veclen = node->simdclone->simdlen; |
510 | if (POINTER_TYPE_P (t)) | |
511 | t = pointer_sized_int_node; | |
abe93733 | 512 | if (known_eq (veclen, node->simdclone->simdlen)) |
60cbb674 TS |
513 | t = build_vector_type (t, node->simdclone->simdlen); |
514 | else | |
515 | { | |
516 | t = build_vector_type (t, veclen); | |
abe93733 YY |
517 | t = build_array_type_nelts (t, exact_div (node->simdclone->simdlen, |
518 | veclen)); | |
60cbb674 TS |
519 | } |
520 | TREE_TYPE (TREE_TYPE (fndecl)) = t; | |
521 | if (!node->definition) | |
522 | return NULL_TREE; | |
523 | ||
524 | t = DECL_RESULT (fndecl); | |
525 | /* Adjust the DECL_RESULT. */ | |
526 | gcc_assert (TREE_TYPE (t) != void_type_node); | |
527 | TREE_TYPE (t) = TREE_TYPE (TREE_TYPE (fndecl)); | |
528 | relayout_decl (t); | |
529 | ||
530 | tree atype = build_array_type_nelts (orig_rettype, | |
531 | node->simdclone->simdlen); | |
abe93733 | 532 | if (maybe_ne (veclen, node->simdclone->simdlen)) |
60cbb674 TS |
533 | return build1 (VIEW_CONVERT_EXPR, atype, t); |
534 | ||
535 | /* Set up a SIMD array to use as the return value. */ | |
536 | tree retval = create_tmp_var_raw (atype, "retval"); | |
537 | gimple_add_tmp_var (retval); | |
538 | return retval; | |
539 | } | |
540 | ||
541 | /* Each vector argument has a corresponding array to be used locally | |
542 | as part of the eventual loop. Create such temporary array and | |
543 | return it. | |
544 | ||
545 | PREFIX is the prefix to be used for the temporary. | |
546 | ||
547 | TYPE is the inner element type. | |
548 | ||
549 | SIMDLEN is the number of elements. */ | |
550 | ||
551 | static tree | |
abe93733 | 552 | create_tmp_simd_array (const char *prefix, tree type, poly_uint64 simdlen) |
60cbb674 TS |
553 | { |
554 | tree atype = build_array_type_nelts (type, simdlen); | |
555 | tree avar = create_tmp_var_raw (atype, prefix); | |
556 | gimple_add_tmp_var (avar); | |
557 | return avar; | |
558 | } | |
559 | ||
560 | /* Modify the function argument types to their corresponding vector | |
561 | counterparts if appropriate. Also, create one array for each simd | |
562 | argument to be used locally when using the function arguments as | |
563 | part of the loop. | |
564 | ||
565 | NODE is the function whose arguments are to be adjusted. | |
566 | ||
ff6686d2 MJ |
567 | If NODE does not represent function definition, returns NULL. Otherwise |
568 | returns an adjustment class that will be filled describing how the argument | |
569 | declarations will be remapped. New arguments which are not to be remapped | |
570 | are marked with USER_FLAG. */ | |
60cbb674 | 571 | |
ff6686d2 | 572 | static ipa_param_body_adjustments * |
60cbb674 TS |
573 | simd_clone_adjust_argument_types (struct cgraph_node *node) |
574 | { | |
ff6686d2 | 575 | auto_vec<tree> args; |
60cbb674 TS |
576 | |
577 | if (node->definition) | |
ff6686d2 | 578 | push_function_arg_decls (&args, node->decl); |
60cbb674 | 579 | else |
ff6686d2 | 580 | simd_clone_vector_of_formal_parm_types (&args, node->decl); |
60cbb674 | 581 | struct cgraph_simd_clone *sc = node->simdclone; |
ff6686d2 MJ |
582 | vec<ipa_adjusted_param, va_gc> *new_params = NULL; |
583 | vec_safe_reserve (new_params, sc->nargs); | |
abe93733 YY |
584 | unsigned i, j, k; |
585 | poly_uint64 veclen; | |
60cbb674 TS |
586 | |
587 | for (i = 0; i < sc->nargs; ++i) | |
588 | { | |
ff6686d2 | 589 | ipa_adjusted_param adj; |
60cbb674 TS |
590 | memset (&adj, 0, sizeof (adj)); |
591 | tree parm = args[i]; | |
592 | tree parm_type = node->definition ? TREE_TYPE (parm) : parm; | |
593 | adj.base_index = i; | |
ff6686d2 | 594 | adj.prev_clone_index = i; |
60cbb674 TS |
595 | |
596 | sc->args[i].orig_arg = node->definition ? parm : NULL_TREE; | |
597 | sc->args[i].orig_type = parm_type; | |
598 | ||
599 | switch (sc->args[i].arg_type) | |
600 | { | |
601 | default: | |
602 | /* No adjustment necessary for scalar arguments. */ | |
ff6686d2 | 603 | adj.op = IPA_PARAM_OP_COPY; |
60cbb674 TS |
604 | break; |
605 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP: | |
606 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP: | |
607 | if (node->definition) | |
608 | sc->args[i].simd_array | |
609 | = create_tmp_simd_array (IDENTIFIER_POINTER (DECL_NAME (parm)), | |
610 | TREE_TYPE (parm_type), | |
611 | sc->simdlen); | |
ff6686d2 | 612 | adj.op = IPA_PARAM_OP_COPY; |
60cbb674 TS |
613 | break; |
614 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP: | |
615 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP: | |
616 | case SIMD_CLONE_ARG_TYPE_VECTOR: | |
617 | if (INTEGRAL_TYPE_P (parm_type) || POINTER_TYPE_P (parm_type)) | |
618 | veclen = sc->vecsize_int; | |
619 | else | |
620 | veclen = sc->vecsize_float; | |
abe93733 YY |
621 | veclen = exact_div (veclen, |
622 | GET_MODE_BITSIZE (SCALAR_TYPE_MODE (parm_type))); | |
623 | if (multiple_p (veclen, sc->simdlen)) | |
60cbb674 | 624 | veclen = sc->simdlen; |
ff6686d2 MJ |
625 | adj.op = IPA_PARAM_OP_NEW; |
626 | adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD; | |
60cbb674 TS |
627 | if (POINTER_TYPE_P (parm_type)) |
628 | adj.type = build_vector_type (pointer_sized_int_node, veclen); | |
629 | else | |
630 | adj.type = build_vector_type (parm_type, veclen); | |
631 | sc->args[i].vector_type = adj.type; | |
abe93733 YY |
632 | k = vector_unroll_factor (sc->simdlen, veclen); |
633 | for (j = 1; j < k; j++) | |
60cbb674 | 634 | { |
ff6686d2 | 635 | vec_safe_push (new_params, adj); |
abe93733 | 636 | if (j == 1) |
60cbb674 TS |
637 | { |
638 | memset (&adj, 0, sizeof (adj)); | |
ff6686d2 MJ |
639 | adj.op = IPA_PARAM_OP_NEW; |
640 | adj.user_flag = 1; | |
641 | adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD; | |
60cbb674 | 642 | adj.base_index = i; |
ff6686d2 | 643 | adj.prev_clone_index = i; |
60cbb674 TS |
644 | adj.type = sc->args[i].vector_type; |
645 | } | |
646 | } | |
647 | ||
648 | if (node->definition) | |
649 | sc->args[i].simd_array | |
699e8cb7 JJ |
650 | = create_tmp_simd_array (DECL_NAME (parm) |
651 | ? IDENTIFIER_POINTER (DECL_NAME (parm)) | |
652 | : NULL, parm_type, sc->simdlen); | |
60cbb674 | 653 | } |
ff6686d2 | 654 | vec_safe_push (new_params, adj); |
60cbb674 TS |
655 | } |
656 | ||
657 | if (sc->inbranch) | |
658 | { | |
659 | tree base_type = simd_clone_compute_base_data_type (sc->origin, sc); | |
ff6686d2 | 660 | ipa_adjusted_param adj; |
60cbb674 | 661 | memset (&adj, 0, sizeof (adj)); |
ff6686d2 MJ |
662 | adj.op = IPA_PARAM_OP_NEW; |
663 | adj.user_flag = 1; | |
664 | adj.param_prefix_index = IPA_PARAM_PREFIX_MASK; | |
60cbb674 TS |
665 | |
666 | adj.base_index = i; | |
ff6686d2 | 667 | adj.prev_clone_index = i; |
60cbb674 TS |
668 | if (INTEGRAL_TYPE_P (base_type) || POINTER_TYPE_P (base_type)) |
669 | veclen = sc->vecsize_int; | |
670 | else | |
671 | veclen = sc->vecsize_float; | |
abe93733 YY |
672 | veclen = exact_div (veclen, |
673 | GET_MODE_BITSIZE (SCALAR_TYPE_MODE (base_type))); | |
674 | if (multiple_p (veclen, sc->simdlen)) | |
60cbb674 TS |
675 | veclen = sc->simdlen; |
676 | if (sc->mask_mode != VOIDmode) | |
677 | adj.type | |
678 | = lang_hooks.types.type_for_mode (sc->mask_mode, 1); | |
679 | else if (POINTER_TYPE_P (base_type)) | |
680 | adj.type = build_vector_type (pointer_sized_int_node, veclen); | |
681 | else | |
682 | adj.type = build_vector_type (base_type, veclen); | |
ff6686d2 | 683 | vec_safe_push (new_params, adj); |
60cbb674 | 684 | |
abe93733 YY |
685 | k = vector_unroll_factor (sc->simdlen, veclen); |
686 | for (j = 1; j < k; j++) | |
ff6686d2 | 687 | vec_safe_push (new_params, adj); |
60cbb674 TS |
688 | |
689 | /* We have previously allocated one extra entry for the mask. Use | |
690 | it and fill it. */ | |
691 | sc->nargs++; | |
692 | if (sc->mask_mode != VOIDmode) | |
693 | base_type = boolean_type_node; | |
694 | if (node->definition) | |
695 | { | |
696 | sc->args[i].orig_arg | |
697 | = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL, base_type); | |
698 | if (sc->mask_mode == VOIDmode) | |
699 | sc->args[i].simd_array | |
700 | = create_tmp_simd_array ("mask", base_type, sc->simdlen); | |
abe93733 | 701 | else if (k > 1) |
60cbb674 | 702 | sc->args[i].simd_array |
abe93733 | 703 | = create_tmp_simd_array ("mask", adj.type, k); |
60cbb674 TS |
704 | else |
705 | sc->args[i].simd_array = NULL_TREE; | |
706 | } | |
707 | sc->args[i].orig_type = base_type; | |
708 | sc->args[i].arg_type = SIMD_CLONE_ARG_TYPE_MASK; | |
709 | } | |
710 | ||
711 | if (node->definition) | |
ff6686d2 MJ |
712 | { |
713 | ipa_param_body_adjustments *adjustments | |
714 | = new ipa_param_body_adjustments (new_params, node->decl); | |
715 | ||
716 | adjustments->modify_formal_parameters (); | |
717 | return adjustments; | |
718 | } | |
60cbb674 TS |
719 | else |
720 | { | |
721 | tree new_arg_types = NULL_TREE, new_reversed; | |
722 | bool last_parm_void = false; | |
723 | if (args.length () > 0 && args.last () == void_type_node) | |
724 | last_parm_void = true; | |
725 | ||
726 | gcc_assert (TYPE_ARG_TYPES (TREE_TYPE (node->decl))); | |
ff6686d2 | 727 | j = vec_safe_length (new_params); |
60cbb674 TS |
728 | for (i = 0; i < j; i++) |
729 | { | |
ff6686d2 | 730 | struct ipa_adjusted_param *adj = &(*new_params)[i]; |
60cbb674 | 731 | tree ptype; |
ff6686d2 | 732 | if (adj->op == IPA_PARAM_OP_COPY) |
60cbb674 TS |
733 | ptype = args[adj->base_index]; |
734 | else | |
735 | ptype = adj->type; | |
736 | new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types); | |
737 | } | |
738 | new_reversed = nreverse (new_arg_types); | |
739 | if (last_parm_void) | |
740 | { | |
741 | if (new_reversed) | |
742 | TREE_CHAIN (new_arg_types) = void_list_node; | |
743 | else | |
744 | new_reversed = void_list_node; | |
745 | } | |
e8a70c17 | 746 | TYPE_ARG_TYPES (TREE_TYPE (node->decl)) = new_reversed; |
ff6686d2 | 747 | return NULL; |
60cbb674 | 748 | } |
60cbb674 TS |
749 | } |
750 | ||
751 | /* Initialize and copy the function arguments in NODE to their | |
752 | corresponding local simd arrays. Returns a fresh gimple_seq with | |
753 | the instruction sequence generated. */ | |
754 | ||
755 | static gimple_seq | |
756 | simd_clone_init_simd_arrays (struct cgraph_node *node, | |
ff6686d2 | 757 | ipa_param_body_adjustments *adjustments) |
60cbb674 TS |
758 | { |
759 | gimple_seq seq = NULL; | |
760 | unsigned i = 0, j = 0, k; | |
761 | ||
762 | for (tree arg = DECL_ARGUMENTS (node->decl); | |
763 | arg; | |
764 | arg = DECL_CHAIN (arg), i++, j++) | |
765 | { | |
ff6686d2 | 766 | if ((*adjustments->m_adj_params)[j].op == IPA_PARAM_OP_COPY |
60cbb674 TS |
767 | || POINTER_TYPE_P (TREE_TYPE (arg))) |
768 | continue; | |
769 | ||
770 | node->simdclone->args[i].vector_arg = arg; | |
771 | ||
772 | tree array = node->simdclone->args[i].simd_array; | |
773 | if (node->simdclone->mask_mode != VOIDmode | |
774 | && node->simdclone->args[i].arg_type == SIMD_CLONE_ARG_TYPE_MASK) | |
775 | { | |
776 | if (array == NULL_TREE) | |
777 | continue; | |
778 | unsigned int l | |
779 | = tree_to_uhwi (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (array)))); | |
780 | for (k = 0; k <= l; k++) | |
781 | { | |
782 | if (k) | |
783 | { | |
784 | arg = DECL_CHAIN (arg); | |
785 | j++; | |
786 | } | |
787 | tree t = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (array)), | |
788 | array, size_int (k), NULL, NULL); | |
789 | t = build2 (MODIFY_EXPR, TREE_TYPE (t), t, arg); | |
790 | gimplify_and_add (t, &seq); | |
791 | } | |
792 | continue; | |
793 | } | |
abe93733 YY |
794 | if (known_eq (simd_clone_subparts (TREE_TYPE (arg)), |
795 | node->simdclone->simdlen)) | |
60cbb674 TS |
796 | { |
797 | tree ptype = build_pointer_type (TREE_TYPE (TREE_TYPE (array))); | |
798 | tree ptr = build_fold_addr_expr (array); | |
799 | tree t = build2 (MEM_REF, TREE_TYPE (arg), ptr, | |
800 | build_int_cst (ptype, 0)); | |
801 | t = build2 (MODIFY_EXPR, TREE_TYPE (t), t, arg); | |
802 | gimplify_and_add (t, &seq); | |
803 | } | |
804 | else | |
805 | { | |
d8f860ef | 806 | unsigned int simdlen = simd_clone_subparts (TREE_TYPE (arg)); |
abe93733 YY |
807 | unsigned int times = vector_unroll_factor (node->simdclone->simdlen, |
808 | simdlen); | |
60cbb674 | 809 | tree ptype = build_pointer_type (TREE_TYPE (TREE_TYPE (array))); |
abe93733 | 810 | for (k = 0; k < times; k++) |
60cbb674 TS |
811 | { |
812 | tree ptr = build_fold_addr_expr (array); | |
813 | int elemsize; | |
814 | if (k) | |
815 | { | |
816 | arg = DECL_CHAIN (arg); | |
817 | j++; | |
818 | } | |
b397965c RS |
819 | tree elemtype = TREE_TYPE (TREE_TYPE (arg)); |
820 | elemsize = GET_MODE_SIZE (SCALAR_TYPE_MODE (elemtype)); | |
60cbb674 | 821 | tree t = build2 (MEM_REF, TREE_TYPE (arg), ptr, |
abe93733 | 822 | build_int_cst (ptype, k * elemsize * simdlen)); |
60cbb674 TS |
823 | t = build2 (MODIFY_EXPR, TREE_TYPE (t), t, arg); |
824 | gimplify_and_add (t, &seq); | |
825 | } | |
826 | } | |
827 | } | |
828 | return seq; | |
829 | } | |
830 | ||
831 | /* Callback info for ipa_simd_modify_stmt_ops below. */ | |
832 | ||
833 | struct modify_stmt_info { | |
ff6686d2 | 834 | ipa_param_body_adjustments *adjustments; |
60cbb674 | 835 | gimple *stmt; |
d0fb9ffc | 836 | gimple *after_stmt; |
60cbb674 TS |
837 | /* True if the parent statement was modified by |
838 | ipa_simd_modify_stmt_ops. */ | |
839 | bool modified; | |
840 | }; | |
841 | ||
842 | /* Callback for walk_gimple_op. | |
843 | ||
844 | Adjust operands from a given statement as specified in the | |
845 | adjustments vector in the callback data. */ | |
846 | ||
847 | static tree | |
848 | ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data) | |
849 | { | |
850 | struct walk_stmt_info *wi = (struct walk_stmt_info *) data; | |
851 | struct modify_stmt_info *info = (struct modify_stmt_info *) wi->info; | |
852 | tree *orig_tp = tp; | |
853 | if (TREE_CODE (*tp) == ADDR_EXPR) | |
854 | tp = &TREE_OPERAND (*tp, 0); | |
ff6686d2 MJ |
855 | |
856 | if (TREE_CODE (*tp) == BIT_FIELD_REF | |
857 | || TREE_CODE (*tp) == IMAGPART_EXPR | |
858 | || TREE_CODE (*tp) == REALPART_EXPR) | |
859 | tp = &TREE_OPERAND (*tp, 0); | |
860 | ||
861 | tree repl = NULL_TREE; | |
862 | ipa_param_body_replacement *pbr = NULL; | |
863 | ||
60cbb674 | 864 | if (TREE_CODE (*tp) == PARM_DECL) |
ff6686d2 MJ |
865 | { |
866 | pbr = info->adjustments->get_expr_replacement (*tp, true); | |
867 | if (pbr) | |
868 | repl = pbr->repl; | |
869 | } | |
66146c90 JJ |
870 | else if (TYPE_P (*tp)) |
871 | *walk_subtrees = 0; | |
60cbb674 | 872 | |
ff6686d2 MJ |
873 | if (repl) |
874 | repl = unshare_expr (repl); | |
60cbb674 TS |
875 | else |
876 | { | |
877 | if (tp != orig_tp) | |
878 | { | |
879 | *walk_subtrees = 0; | |
880 | bool modified = info->modified; | |
881 | info->modified = false; | |
882 | walk_tree (tp, ipa_simd_modify_stmt_ops, wi, wi->pset); | |
883 | if (!info->modified) | |
884 | { | |
885 | info->modified = modified; | |
886 | return NULL_TREE; | |
887 | } | |
888 | info->modified = modified; | |
889 | repl = *tp; | |
890 | } | |
891 | else | |
892 | return NULL_TREE; | |
893 | } | |
894 | ||
895 | if (tp != orig_tp) | |
896 | { | |
33813f1d | 897 | if (gimple_code (info->stmt) == GIMPLE_PHI |
ff6686d2 | 898 | && pbr |
33813f1d JJ |
899 | && TREE_CODE (*orig_tp) == ADDR_EXPR |
900 | && TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL | |
ff6686d2 | 901 | && pbr->dummy) |
33813f1d | 902 | { |
ff6686d2 MJ |
903 | gcc_assert (TREE_CODE (pbr->dummy) == SSA_NAME); |
904 | *orig_tp = pbr->dummy; | |
33813f1d JJ |
905 | info->modified = true; |
906 | return NULL_TREE; | |
907 | } | |
908 | ||
60cbb674 TS |
909 | repl = build_fold_addr_expr (repl); |
910 | gimple *stmt; | |
911 | if (is_gimple_debug (info->stmt)) | |
912 | { | |
2d60c0a3 | 913 | tree vexpr = build_debug_expr_decl (TREE_TYPE (repl)); |
60cbb674 | 914 | stmt = gimple_build_debug_source_bind (vexpr, repl, NULL); |
60cbb674 TS |
915 | repl = vexpr; |
916 | } | |
917 | else | |
918 | { | |
919 | stmt = gimple_build_assign (make_ssa_name (TREE_TYPE (repl)), repl); | |
920 | repl = gimple_assign_lhs (stmt); | |
921 | } | |
33813f1d JJ |
922 | gimple_stmt_iterator gsi; |
923 | if (gimple_code (info->stmt) == GIMPLE_PHI) | |
924 | { | |
d0fb9ffc JJ |
925 | if (info->after_stmt) |
926 | gsi = gsi_for_stmt (info->after_stmt); | |
927 | else | |
928 | gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun))); | |
33813f1d | 929 | /* Cache SSA_NAME for next time. */ |
ff6686d2 | 930 | if (pbr |
33813f1d JJ |
931 | && TREE_CODE (*orig_tp) == ADDR_EXPR |
932 | && TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL) | |
ff6686d2 MJ |
933 | { |
934 | gcc_assert (!pbr->dummy); | |
935 | pbr->dummy = repl; | |
936 | } | |
33813f1d JJ |
937 | } |
938 | else | |
939 | gsi = gsi_for_stmt (info->stmt); | |
d0fb9ffc JJ |
940 | if (info->after_stmt) |
941 | gsi_insert_after (&gsi, stmt, GSI_SAME_STMT); | |
942 | else | |
943 | gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); | |
944 | if (gimple_code (info->stmt) == GIMPLE_PHI) | |
945 | info->after_stmt = stmt; | |
60cbb674 TS |
946 | *orig_tp = repl; |
947 | } | |
948 | else if (!useless_type_conversion_p (TREE_TYPE (*tp), TREE_TYPE (repl))) | |
949 | { | |
950 | tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*tp), repl); | |
951 | *tp = vce; | |
952 | } | |
953 | else | |
954 | *tp = repl; | |
955 | ||
956 | info->modified = true; | |
957 | return NULL_TREE; | |
958 | } | |
959 | ||
960 | /* Traverse the function body and perform all modifications as | |
961 | described in ADJUSTMENTS. At function return, ADJUSTMENTS will be | |
962 | modified such that the replacement/reduction value will now be an | |
963 | offset into the corresponding simd_array. | |
964 | ||
965 | This function will replace all function argument uses with their | |
966 | corresponding simd array elements, and ajust the return values | |
967 | accordingly. */ | |
968 | ||
969 | static void | |
970 | ipa_simd_modify_function_body (struct cgraph_node *node, | |
ff6686d2 | 971 | ipa_param_body_adjustments *adjustments, |
60cbb674 TS |
972 | tree retval_array, tree iter) |
973 | { | |
974 | basic_block bb; | |
ff6686d2 | 975 | unsigned int i, j; |
60cbb674 | 976 | |
ff6686d2 MJ |
977 | |
978 | /* Register replacements for every function argument use to an offset into | |
979 | the corresponding simd_array. */ | |
60cbb674 TS |
980 | for (i = 0, j = 0; i < node->simdclone->nargs; ++i, ++j) |
981 | { | |
ff6686d2 MJ |
982 | if (!node->simdclone->args[i].vector_arg |
983 | || (*adjustments->m_adj_params)[j].user_flag) | |
60cbb674 TS |
984 | continue; |
985 | ||
986 | tree basetype = TREE_TYPE (node->simdclone->args[i].orig_arg); | |
987 | tree vectype = TREE_TYPE (node->simdclone->args[i].vector_arg); | |
ff6686d2 MJ |
988 | tree r = build4 (ARRAY_REF, basetype, node->simdclone->args[i].simd_array, |
989 | iter, NULL_TREE, NULL_TREE); | |
990 | adjustments->register_replacement (&(*adjustments->m_adj_params)[j], r); | |
991 | ||
abe93733 YY |
992 | if (multiple_p (node->simdclone->simdlen, simd_clone_subparts (vectype))) |
993 | j += vector_unroll_factor (node->simdclone->simdlen, | |
994 | simd_clone_subparts (vectype)) - 1; | |
60cbb674 TS |
995 | } |
996 | ||
46aa019a | 997 | tree name; |
46aa019a | 998 | FOR_EACH_SSA_NAME (i, name, cfun) |
60cbb674 | 999 | { |
ff6686d2 | 1000 | tree base_var; |
46aa019a | 1001 | if (SSA_NAME_VAR (name) |
ff6686d2 MJ |
1002 | && TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL |
1003 | && (base_var | |
1004 | = adjustments->get_replacement_ssa_base (SSA_NAME_VAR (name)))) | |
60cbb674 | 1005 | { |
ff6686d2 MJ |
1006 | if (SSA_NAME_IS_DEFAULT_DEF (name)) |
1007 | { | |
1008 | tree old_decl = SSA_NAME_VAR (name); | |
1009 | bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); | |
1010 | gimple_stmt_iterator gsi = gsi_after_labels (bb); | |
1011 | tree repl = adjustments->lookup_replacement (old_decl, 0); | |
1012 | gcc_checking_assert (repl); | |
1013 | repl = unshare_expr (repl); | |
1014 | set_ssa_default_def (cfun, old_decl, NULL_TREE); | |
1015 | SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); | |
1016 | SSA_NAME_IS_DEFAULT_DEF (name) = 0; | |
1017 | gimple *stmt = gimple_build_assign (name, repl); | |
1018 | gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); | |
1019 | } | |
1020 | else | |
1021 | SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); | |
60cbb674 TS |
1022 | } |
1023 | } | |
1024 | ||
1025 | struct modify_stmt_info info; | |
1026 | info.adjustments = adjustments; | |
1027 | ||
1028 | FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl)) | |
1029 | { | |
1030 | gimple_stmt_iterator gsi; | |
1031 | ||
33813f1d JJ |
1032 | for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
1033 | { | |
1034 | gphi *phi = as_a <gphi *> (gsi_stmt (gsi)); | |
1035 | int i, n = gimple_phi_num_args (phi); | |
1036 | info.stmt = phi; | |
d0fb9ffc | 1037 | info.after_stmt = NULL; |
33813f1d JJ |
1038 | struct walk_stmt_info wi; |
1039 | memset (&wi, 0, sizeof (wi)); | |
1040 | info.modified = false; | |
1041 | wi.info = &info; | |
1042 | for (i = 0; i < n; ++i) | |
1043 | { | |
1044 | int walk_subtrees = 1; | |
1045 | tree arg = gimple_phi_arg_def (phi, i); | |
1046 | tree op = arg; | |
1047 | ipa_simd_modify_stmt_ops (&op, &walk_subtrees, &wi); | |
1048 | if (op != arg) | |
1049 | { | |
1050 | SET_PHI_ARG_DEF (phi, i, op); | |
1051 | gcc_assert (TREE_CODE (op) == SSA_NAME); | |
1052 | if (gimple_phi_arg_edge (phi, i)->flags & EDGE_ABNORMAL) | |
1053 | SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op) = 1; | |
1054 | } | |
1055 | } | |
1056 | } | |
1057 | ||
60cbb674 TS |
1058 | gsi = gsi_start_bb (bb); |
1059 | while (!gsi_end_p (gsi)) | |
1060 | { | |
1061 | gimple *stmt = gsi_stmt (gsi); | |
1062 | info.stmt = stmt; | |
d0fb9ffc | 1063 | info.after_stmt = NULL; |
60cbb674 TS |
1064 | struct walk_stmt_info wi; |
1065 | ||
1066 | memset (&wi, 0, sizeof (wi)); | |
1067 | info.modified = false; | |
1068 | wi.info = &info; | |
1069 | walk_gimple_op (stmt, ipa_simd_modify_stmt_ops, &wi); | |
1070 | ||
1071 | if (greturn *return_stmt = dyn_cast <greturn *> (stmt)) | |
1072 | { | |
1073 | tree retval = gimple_return_retval (return_stmt); | |
260a9933 JJ |
1074 | edge e = find_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun)); |
1075 | e->flags |= EDGE_FALLTHRU; | |
60cbb674 TS |
1076 | if (!retval) |
1077 | { | |
1078 | gsi_remove (&gsi, true); | |
1079 | continue; | |
1080 | } | |
1081 | ||
1082 | /* Replace `return foo' with `retval_array[iter] = foo'. */ | |
1083 | tree ref = build4 (ARRAY_REF, TREE_TYPE (retval), | |
1084 | retval_array, iter, NULL, NULL); | |
1085 | stmt = gimple_build_assign (ref, retval); | |
1086 | gsi_replace (&gsi, stmt, true); | |
1087 | info.modified = true; | |
1088 | } | |
1089 | ||
1090 | if (info.modified) | |
1091 | { | |
1092 | update_stmt (stmt); | |
66146c90 JJ |
1093 | /* If the above changed the var of a debug bind into something |
1094 | different, remove the debug stmt. We could also for all the | |
1095 | replaced parameters add VAR_DECLs for debug info purposes, | |
1096 | add debug stmts for those to be the simd array accesses and | |
1097 | replace debug stmt var operand with that var. Debugging of | |
1098 | vectorized loops doesn't work too well, so don't bother for | |
1099 | now. */ | |
1100 | if ((gimple_debug_bind_p (stmt) | |
1101 | && !DECL_P (gimple_debug_bind_get_var (stmt))) | |
1102 | || (gimple_debug_source_bind_p (stmt) | |
1103 | && !DECL_P (gimple_debug_source_bind_get_var (stmt)))) | |
1104 | { | |
1105 | gsi_remove (&gsi, true); | |
1106 | continue; | |
1107 | } | |
60cbb674 TS |
1108 | if (maybe_clean_eh_stmt (stmt)) |
1109 | gimple_purge_dead_eh_edges (gimple_bb (stmt)); | |
1110 | } | |
1111 | gsi_next (&gsi); | |
1112 | } | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | /* Helper function of simd_clone_adjust, return linear step addend | |
1117 | of Ith argument. */ | |
1118 | ||
1119 | static tree | |
1120 | simd_clone_linear_addend (struct cgraph_node *node, unsigned int i, | |
1121 | tree addtype, basic_block entry_bb) | |
1122 | { | |
1123 | tree ptype = NULL_TREE; | |
1124 | switch (node->simdclone->args[i].arg_type) | |
1125 | { | |
1126 | case SIMD_CLONE_ARG_TYPE_LINEAR_CONSTANT_STEP: | |
1127 | case SIMD_CLONE_ARG_TYPE_LINEAR_REF_CONSTANT_STEP: | |
1128 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP: | |
1129 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP: | |
1130 | return build_int_cst (addtype, node->simdclone->args[i].linear_step); | |
1131 | case SIMD_CLONE_ARG_TYPE_LINEAR_VARIABLE_STEP: | |
1132 | case SIMD_CLONE_ARG_TYPE_LINEAR_REF_VARIABLE_STEP: | |
1133 | ptype = TREE_TYPE (node->simdclone->args[i].orig_arg); | |
1134 | break; | |
1135 | case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP: | |
1136 | case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP: | |
1137 | ptype = TREE_TYPE (TREE_TYPE (node->simdclone->args[i].orig_arg)); | |
1138 | break; | |
1139 | default: | |
1140 | gcc_unreachable (); | |
1141 | } | |
1142 | ||
1143 | unsigned int idx = node->simdclone->args[i].linear_step; | |
1144 | tree arg = node->simdclone->args[idx].orig_arg; | |
1145 | gcc_assert (is_gimple_reg_type (TREE_TYPE (arg))); | |
1146 | gimple_stmt_iterator gsi = gsi_after_labels (entry_bb); | |
1147 | gimple *g; | |
1148 | tree ret; | |
1149 | if (is_gimple_reg (arg)) | |
1150 | ret = get_or_create_ssa_default_def (cfun, arg); | |
1151 | else | |
1152 | { | |
1153 | g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg)), arg); | |
1154 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1155 | ret = gimple_assign_lhs (g); | |
1156 | } | |
1157 | if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE) | |
1158 | { | |
1159 | g = gimple_build_assign (make_ssa_name (TREE_TYPE (TREE_TYPE (arg))), | |
1160 | build_simple_mem_ref (ret)); | |
1161 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1162 | ret = gimple_assign_lhs (g); | |
1163 | } | |
1164 | if (!useless_type_conversion_p (addtype, TREE_TYPE (ret))) | |
1165 | { | |
1166 | g = gimple_build_assign (make_ssa_name (addtype), NOP_EXPR, ret); | |
1167 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1168 | ret = gimple_assign_lhs (g); | |
1169 | } | |
1170 | if (POINTER_TYPE_P (ptype)) | |
1171 | { | |
1172 | tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptype)); | |
1173 | if (size && TREE_CODE (size) == INTEGER_CST) | |
1174 | { | |
1175 | g = gimple_build_assign (make_ssa_name (addtype), MULT_EXPR, | |
1176 | ret, fold_convert (addtype, size)); | |
1177 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1178 | ret = gimple_assign_lhs (g); | |
1179 | } | |
1180 | } | |
1181 | return ret; | |
1182 | } | |
1183 | ||
1184 | /* Adjust the argument types in NODE to their appropriate vector | |
1185 | counterparts. */ | |
1186 | ||
1187 | static void | |
1188 | simd_clone_adjust (struct cgraph_node *node) | |
1189 | { | |
1190 | push_cfun (DECL_STRUCT_FUNCTION (node->decl)); | |
1191 | ||
e8a70c17 | 1192 | TREE_TYPE (node->decl) = build_distinct_type_copy (TREE_TYPE (node->decl)); |
60cbb674 TS |
1193 | targetm.simd_clone.adjust (node); |
1194 | ||
1195 | tree retval = simd_clone_adjust_return_type (node); | |
ff6686d2 | 1196 | ipa_param_body_adjustments *adjustments |
60cbb674 | 1197 | = simd_clone_adjust_argument_types (node); |
ff6686d2 | 1198 | gcc_assert (adjustments); |
60cbb674 TS |
1199 | |
1200 | push_gimplify_context (); | |
1201 | ||
1202 | gimple_seq seq = simd_clone_init_simd_arrays (node, adjustments); | |
1203 | ||
1204 | /* Adjust all uses of vector arguments accordingly. Adjust all | |
1205 | return values accordingly. */ | |
1206 | tree iter = create_tmp_var (unsigned_type_node, "iter"); | |
1207 | tree iter1 = make_ssa_name (iter); | |
4ce71579 | 1208 | tree iter2 = NULL_TREE; |
60cbb674 | 1209 | ipa_simd_modify_function_body (node, adjustments, retval, iter1); |
ff6686d2 | 1210 | delete adjustments; |
60cbb674 TS |
1211 | |
1212 | /* Initialize the iteration variable. */ | |
1213 | basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); | |
1214 | basic_block body_bb = split_block_after_labels (entry_bb)->dest; | |
1215 | gimple_stmt_iterator gsi = gsi_after_labels (entry_bb); | |
1216 | /* Insert the SIMD array and iv initialization at function | |
1217 | entry. */ | |
1218 | gsi_insert_seq_before (&gsi, seq, GSI_NEW_STMT); | |
1219 | ||
1220 | pop_gimplify_context (NULL); | |
1221 | ||
4ce71579 JJ |
1222 | gimple *g; |
1223 | basic_block incr_bb = NULL; | |
99b1c316 | 1224 | class loop *loop = NULL; |
4ce71579 | 1225 | |
60cbb674 TS |
1226 | /* Create a new BB right before the original exit BB, to hold the |
1227 | iteration increment and the condition/branch. */ | |
4ce71579 JJ |
1228 | if (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)) |
1229 | { | |
1230 | basic_block orig_exit = EDGE_PRED (EXIT_BLOCK_PTR_FOR_FN (cfun), 0)->src; | |
1231 | incr_bb = create_empty_bb (orig_exit); | |
e7a74006 | 1232 | incr_bb->count = profile_count::zero (); |
4ce71579 | 1233 | add_bb_to_loop (incr_bb, body_bb->loop_father); |
260a9933 | 1234 | while (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)) |
4ce71579 | 1235 | { |
260a9933 | 1236 | edge e = EDGE_PRED (EXIT_BLOCK_PTR_FOR_FN (cfun), 0); |
4ce71579 | 1237 | redirect_edge_succ (e, incr_bb); |
e7a74006 | 1238 | incr_bb->count += e->count (); |
4ce71579 JJ |
1239 | } |
1240 | } | |
1241 | else if (node->simdclone->inbranch) | |
1242 | { | |
1243 | incr_bb = create_empty_bb (entry_bb); | |
e7a74006 | 1244 | incr_bb->count = profile_count::zero (); |
4ce71579 JJ |
1245 | add_bb_to_loop (incr_bb, body_bb->loop_father); |
1246 | } | |
1247 | ||
1248 | if (incr_bb) | |
60cbb674 | 1249 | { |
357067f2 | 1250 | make_single_succ_edge (incr_bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0); |
4ce71579 JJ |
1251 | gsi = gsi_last_bb (incr_bb); |
1252 | iter2 = make_ssa_name (iter); | |
1253 | g = gimple_build_assign (iter2, PLUS_EXPR, iter1, | |
1254 | build_int_cst (unsigned_type_node, 1)); | |
1255 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1256 | ||
1257 | /* Mostly annotate the loop for the vectorizer (the rest is done | |
1258 | below). */ | |
1259 | loop = alloc_loop (); | |
1260 | cfun->has_force_vectorize_loops = true; | |
abe93733 YY |
1261 | /* For now, simlen is always constant. */ |
1262 | loop->safelen = node->simdclone->simdlen.to_constant (); | |
4ce71579 JJ |
1263 | loop->force_vectorize = true; |
1264 | loop->header = body_bb; | |
60cbb674 | 1265 | } |
60cbb674 TS |
1266 | |
1267 | /* Branch around the body if the mask applies. */ | |
1268 | if (node->simdclone->inbranch) | |
1269 | { | |
4ce71579 | 1270 | gsi = gsi_last_bb (loop->header); |
60cbb674 TS |
1271 | tree mask_array |
1272 | = node->simdclone->args[node->simdclone->nargs - 1].simd_array; | |
1273 | tree mask; | |
1274 | if (node->simdclone->mask_mode != VOIDmode) | |
1275 | { | |
1276 | tree shift_cnt; | |
1277 | if (mask_array == NULL_TREE) | |
1278 | { | |
1279 | tree arg = node->simdclone->args[node->simdclone->nargs | |
1280 | - 1].vector_arg; | |
1281 | mask = get_or_create_ssa_default_def (cfun, arg); | |
1282 | shift_cnt = iter1; | |
1283 | } | |
1284 | else | |
1285 | { | |
1286 | tree maskt = TREE_TYPE (mask_array); | |
1287 | int c = tree_to_uhwi (TYPE_MAX_VALUE (TYPE_DOMAIN (maskt))); | |
abe93733 YY |
1288 | /* For now, c must be constant here. */ |
1289 | c = exact_div (node->simdclone->simdlen, c + 1).to_constant (); | |
60cbb674 TS |
1290 | int s = exact_log2 (c); |
1291 | gcc_assert (s > 0); | |
1292 | c--; | |
1293 | tree idx = make_ssa_name (TREE_TYPE (iter1)); | |
1294 | g = gimple_build_assign (idx, RSHIFT_EXPR, iter1, | |
1295 | build_int_cst (NULL_TREE, s)); | |
1296 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1297 | mask = make_ssa_name (TREE_TYPE (TREE_TYPE (mask_array))); | |
1298 | tree aref = build4 (ARRAY_REF, | |
1299 | TREE_TYPE (TREE_TYPE (mask_array)), | |
1300 | mask_array, idx, NULL, NULL); | |
1301 | g = gimple_build_assign (mask, aref); | |
1302 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1303 | shift_cnt = make_ssa_name (TREE_TYPE (iter1)); | |
1304 | g = gimple_build_assign (shift_cnt, BIT_AND_EXPR, iter1, | |
1305 | build_int_cst (TREE_TYPE (iter1), c)); | |
1306 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1307 | } | |
1308 | g = gimple_build_assign (make_ssa_name (TREE_TYPE (mask)), | |
1309 | RSHIFT_EXPR, mask, shift_cnt); | |
1310 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1311 | mask = gimple_assign_lhs (g); | |
1312 | g = gimple_build_assign (make_ssa_name (TREE_TYPE (mask)), | |
1313 | BIT_AND_EXPR, mask, | |
1314 | build_int_cst (TREE_TYPE (mask), 1)); | |
1315 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1316 | mask = gimple_assign_lhs (g); | |
1317 | } | |
1318 | else | |
1319 | { | |
1320 | mask = make_ssa_name (TREE_TYPE (TREE_TYPE (mask_array))); | |
1321 | tree aref = build4 (ARRAY_REF, | |
1322 | TREE_TYPE (TREE_TYPE (mask_array)), | |
1323 | mask_array, iter1, NULL, NULL); | |
1324 | g = gimple_build_assign (mask, aref); | |
1325 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
b397965c | 1326 | int bitsize = GET_MODE_BITSIZE (SCALAR_TYPE_MODE (TREE_TYPE (aref))); |
60cbb674 TS |
1327 | if (!INTEGRAL_TYPE_P (TREE_TYPE (aref))) |
1328 | { | |
1329 | aref = build1 (VIEW_CONVERT_EXPR, | |
1330 | build_nonstandard_integer_type (bitsize, 0), | |
1331 | mask); | |
1332 | mask = make_ssa_name (TREE_TYPE (aref)); | |
1333 | g = gimple_build_assign (mask, aref); | |
1334 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1335 | } | |
1336 | } | |
1337 | ||
1338 | g = gimple_build_cond (EQ_EXPR, mask, build_zero_cst (TREE_TYPE (mask)), | |
1339 | NULL, NULL); | |
1340 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
54cb4e20 TV |
1341 | edge e = make_edge (loop->header, incr_bb, EDGE_TRUE_VALUE); |
1342 | e->probability = profile_probability::unlikely ().guessed (); | |
e7a74006 | 1343 | incr_bb->count += e->count (); |
54cb4e20 TV |
1344 | edge fallthru = FALLTHRU_EDGE (loop->header); |
1345 | fallthru->flags = EDGE_FALSE_VALUE; | |
1346 | fallthru->probability = profile_probability::likely ().guessed (); | |
60cbb674 TS |
1347 | } |
1348 | ||
4ce71579 JJ |
1349 | basic_block latch_bb = NULL; |
1350 | basic_block new_exit_bb = NULL; | |
1351 | ||
60cbb674 | 1352 | /* Generate the condition. */ |
4ce71579 JJ |
1353 | if (incr_bb) |
1354 | { | |
1355 | gsi = gsi_last_bb (incr_bb); | |
1356 | g = gimple_build_cond (LT_EXPR, iter2, | |
1357 | build_int_cst (unsigned_type_node, | |
1358 | node->simdclone->simdlen), | |
1359 | NULL, NULL); | |
1360 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
1361 | edge e = split_block (incr_bb, gsi_stmt (gsi)); | |
1362 | latch_bb = e->dest; | |
1363 | new_exit_bb = split_block_after_labels (latch_bb)->dest; | |
1364 | loop->latch = latch_bb; | |
1365 | ||
1366 | redirect_edge_succ (FALLTHRU_EDGE (latch_bb), body_bb); | |
1367 | ||
357067f2 JH |
1368 | edge new_e = make_edge (incr_bb, new_exit_bb, EDGE_FALSE_VALUE); |
1369 | ||
1370 | /* FIXME: Do we need to distribute probabilities for the conditional? */ | |
1371 | new_e->probability = profile_probability::guessed_never (); | |
4ce71579 JJ |
1372 | /* The successor of incr_bb is already pointing to latch_bb; just |
1373 | change the flags. | |
1374 | make_edge (incr_bb, latch_bb, EDGE_TRUE_VALUE); */ | |
1375 | FALLTHRU_EDGE (incr_bb)->flags = EDGE_TRUE_VALUE; | |
1376 | } | |
60cbb674 TS |
1377 | |
1378 | gphi *phi = create_phi_node (iter1, body_bb); | |
1379 | edge preheader_edge = find_edge (entry_bb, body_bb); | |
4ce71579 | 1380 | edge latch_edge = NULL; |
60cbb674 TS |
1381 | add_phi_arg (phi, build_zero_cst (unsigned_type_node), preheader_edge, |
1382 | UNKNOWN_LOCATION); | |
4ce71579 | 1383 | if (incr_bb) |
60cbb674 | 1384 | { |
4ce71579 JJ |
1385 | latch_edge = single_succ_edge (latch_bb); |
1386 | add_phi_arg (phi, iter2, latch_edge, UNKNOWN_LOCATION); | |
1387 | ||
1388 | /* Generate the new return. */ | |
1389 | gsi = gsi_last_bb (new_exit_bb); | |
1390 | if (retval | |
1391 | && TREE_CODE (retval) == VIEW_CONVERT_EXPR | |
1392 | && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL) | |
1393 | retval = TREE_OPERAND (retval, 0); | |
1394 | else if (retval) | |
1395 | { | |
1396 | retval = build1 (VIEW_CONVERT_EXPR, | |
1397 | TREE_TYPE (TREE_TYPE (node->decl)), | |
1398 | retval); | |
1399 | retval = force_gimple_operand_gsi (&gsi, retval, true, NULL, | |
1400 | false, GSI_CONTINUE_LINKING); | |
1401 | } | |
1402 | g = gimple_build_return (retval); | |
1403 | gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); | |
60cbb674 | 1404 | } |
60cbb674 TS |
1405 | |
1406 | /* Handle aligned clauses by replacing default defs of the aligned | |
1407 | uniform args with __builtin_assume_aligned (arg_N(D), alignment) | |
1408 | lhs. Handle linear by adding PHIs. */ | |
1409 | for (unsigned i = 0; i < node->simdclone->nargs; i++) | |
1410 | if (node->simdclone->args[i].arg_type == SIMD_CLONE_ARG_TYPE_UNIFORM | |
1411 | && (TREE_ADDRESSABLE (node->simdclone->args[i].orig_arg) | |
1412 | || !is_gimple_reg_type | |
1413 | (TREE_TYPE (node->simdclone->args[i].orig_arg)))) | |
1414 | { | |
1415 | tree orig_arg = node->simdclone->args[i].orig_arg; | |
1416 | if (is_gimple_reg_type (TREE_TYPE (orig_arg))) | |
1417 | iter1 = make_ssa_name (TREE_TYPE (orig_arg)); | |
1418 | else | |
1419 | { | |
1420 | iter1 = create_tmp_var_raw (TREE_TYPE (orig_arg)); | |
1421 | gimple_add_tmp_var (iter1); | |
1422 | } | |
1423 | gsi = gsi_after_labels (entry_bb); | |
1424 | g = gimple_build_assign (iter1, orig_arg); | |
1425 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1426 | gsi = gsi_after_labels (body_bb); | |
1427 | g = gimple_build_assign (orig_arg, iter1); | |
1428 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1429 | } | |
1430 | else if (node->simdclone->args[i].arg_type == SIMD_CLONE_ARG_TYPE_UNIFORM | |
1431 | && DECL_BY_REFERENCE (node->simdclone->args[i].orig_arg) | |
1432 | && TREE_CODE (TREE_TYPE (node->simdclone->args[i].orig_arg)) | |
1433 | == REFERENCE_TYPE | |
1434 | && TREE_ADDRESSABLE | |
1435 | (TREE_TYPE (TREE_TYPE (node->simdclone->args[i].orig_arg)))) | |
1436 | { | |
1437 | tree orig_arg = node->simdclone->args[i].orig_arg; | |
1438 | tree def = ssa_default_def (cfun, orig_arg); | |
1439 | if (def && !has_zero_uses (def)) | |
1440 | { | |
1441 | iter1 = create_tmp_var_raw (TREE_TYPE (TREE_TYPE (orig_arg))); | |
1442 | gimple_add_tmp_var (iter1); | |
1443 | gsi = gsi_after_labels (entry_bb); | |
1444 | g = gimple_build_assign (iter1, build_simple_mem_ref (def)); | |
1445 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1446 | gsi = gsi_after_labels (body_bb); | |
1447 | g = gimple_build_assign (build_simple_mem_ref (def), iter1); | |
1448 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1449 | } | |
1450 | } | |
1451 | else if (node->simdclone->args[i].alignment | |
1452 | && node->simdclone->args[i].arg_type | |
1453 | == SIMD_CLONE_ARG_TYPE_UNIFORM | |
1454 | && (node->simdclone->args[i].alignment | |
1455 | & (node->simdclone->args[i].alignment - 1)) == 0 | |
1456 | && TREE_CODE (TREE_TYPE (node->simdclone->args[i].orig_arg)) | |
1457 | == POINTER_TYPE) | |
1458 | { | |
1459 | unsigned int alignment = node->simdclone->args[i].alignment; | |
1460 | tree orig_arg = node->simdclone->args[i].orig_arg; | |
1461 | tree def = ssa_default_def (cfun, orig_arg); | |
1462 | if (def && !has_zero_uses (def)) | |
1463 | { | |
1464 | tree fn = builtin_decl_explicit (BUILT_IN_ASSUME_ALIGNED); | |
1465 | gimple_seq seq = NULL; | |
1466 | bool need_cvt = false; | |
1467 | gcall *call | |
1468 | = gimple_build_call (fn, 2, def, size_int (alignment)); | |
1469 | g = call; | |
1470 | if (!useless_type_conversion_p (TREE_TYPE (orig_arg), | |
1471 | ptr_type_node)) | |
1472 | need_cvt = true; | |
1473 | tree t = make_ssa_name (need_cvt ? ptr_type_node : orig_arg); | |
1474 | gimple_call_set_lhs (g, t); | |
1475 | gimple_seq_add_stmt_without_update (&seq, g); | |
1476 | if (need_cvt) | |
1477 | { | |
1478 | t = make_ssa_name (orig_arg); | |
1479 | g = gimple_build_assign (t, NOP_EXPR, gimple_call_lhs (g)); | |
1480 | gimple_seq_add_stmt_without_update (&seq, g); | |
1481 | } | |
1482 | gsi_insert_seq_on_edge_immediate | |
1483 | (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)), seq); | |
1484 | ||
1485 | entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); | |
60cbb674 | 1486 | node->create_edge (cgraph_node::get_create (fn), |
1bad9c18 | 1487 | call, entry_bb->count); |
60cbb674 TS |
1488 | |
1489 | imm_use_iterator iter; | |
1490 | use_operand_p use_p; | |
1491 | gimple *use_stmt; | |
1492 | tree repl = gimple_get_lhs (g); | |
1493 | FOR_EACH_IMM_USE_STMT (use_stmt, iter, def) | |
1494 | if (is_gimple_debug (use_stmt) || use_stmt == call) | |
1495 | continue; | |
1496 | else | |
1497 | FOR_EACH_IMM_USE_ON_STMT (use_p, iter) | |
1498 | SET_USE (use_p, repl); | |
1499 | } | |
1500 | } | |
1501 | else if ((node->simdclone->args[i].arg_type | |
1502 | == SIMD_CLONE_ARG_TYPE_LINEAR_CONSTANT_STEP) | |
1503 | || (node->simdclone->args[i].arg_type | |
1504 | == SIMD_CLONE_ARG_TYPE_LINEAR_REF_CONSTANT_STEP) | |
1505 | || (node->simdclone->args[i].arg_type | |
1506 | == SIMD_CLONE_ARG_TYPE_LINEAR_VARIABLE_STEP) | |
1507 | || (node->simdclone->args[i].arg_type | |
1508 | == SIMD_CLONE_ARG_TYPE_LINEAR_REF_VARIABLE_STEP)) | |
1509 | { | |
1510 | tree orig_arg = node->simdclone->args[i].orig_arg; | |
1511 | gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (orig_arg)) | |
1512 | || POINTER_TYPE_P (TREE_TYPE (orig_arg))); | |
1513 | tree def = NULL_TREE; | |
1514 | if (TREE_ADDRESSABLE (orig_arg)) | |
1515 | { | |
1516 | def = make_ssa_name (TREE_TYPE (orig_arg)); | |
1517 | iter1 = make_ssa_name (TREE_TYPE (orig_arg)); | |
4ce71579 JJ |
1518 | if (incr_bb) |
1519 | iter2 = make_ssa_name (TREE_TYPE (orig_arg)); | |
60cbb674 TS |
1520 | gsi = gsi_after_labels (entry_bb); |
1521 | g = gimple_build_assign (def, orig_arg); | |
1522 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1523 | } | |
1524 | else | |
1525 | { | |
1526 | def = ssa_default_def (cfun, orig_arg); | |
1527 | if (!def || has_zero_uses (def)) | |
1528 | def = NULL_TREE; | |
1529 | else | |
1530 | { | |
1531 | iter1 = make_ssa_name (orig_arg); | |
4ce71579 JJ |
1532 | if (incr_bb) |
1533 | iter2 = make_ssa_name (orig_arg); | |
60cbb674 TS |
1534 | } |
1535 | } | |
1536 | if (def) | |
1537 | { | |
1538 | phi = create_phi_node (iter1, body_bb); | |
1539 | add_phi_arg (phi, def, preheader_edge, UNKNOWN_LOCATION); | |
4ce71579 JJ |
1540 | if (incr_bb) |
1541 | { | |
1542 | add_phi_arg (phi, iter2, latch_edge, UNKNOWN_LOCATION); | |
1543 | enum tree_code code = INTEGRAL_TYPE_P (TREE_TYPE (orig_arg)) | |
1544 | ? PLUS_EXPR : POINTER_PLUS_EXPR; | |
1545 | tree addtype = INTEGRAL_TYPE_P (TREE_TYPE (orig_arg)) | |
1546 | ? TREE_TYPE (orig_arg) : sizetype; | |
1547 | tree addcst = simd_clone_linear_addend (node, i, addtype, | |
1548 | entry_bb); | |
1549 | gsi = gsi_last_bb (incr_bb); | |
1550 | g = gimple_build_assign (iter2, code, iter1, addcst); | |
1551 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1552 | } | |
60cbb674 TS |
1553 | |
1554 | imm_use_iterator iter; | |
1555 | use_operand_p use_p; | |
1556 | gimple *use_stmt; | |
1557 | if (TREE_ADDRESSABLE (orig_arg)) | |
1558 | { | |
1559 | gsi = gsi_after_labels (body_bb); | |
1560 | g = gimple_build_assign (orig_arg, iter1); | |
1561 | gsi_insert_before (&gsi, g, GSI_NEW_STMT); | |
1562 | } | |
1563 | else | |
1564 | FOR_EACH_IMM_USE_STMT (use_stmt, iter, def) | |
1565 | if (use_stmt == phi) | |
1566 | continue; | |
1567 | else | |
1568 | FOR_EACH_IMM_USE_ON_STMT (use_p, iter) | |
1569 | SET_USE (use_p, iter1); | |
1570 | } | |
1571 | } | |
1572 | else if (node->simdclone->args[i].arg_type | |
1573 | == SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP | |
1574 | || (node->simdclone->args[i].arg_type | |
1575 | == SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP)) | |
1576 | { | |
1577 | tree orig_arg = node->simdclone->args[i].orig_arg; | |
1578 | tree def = ssa_default_def (cfun, orig_arg); | |
1579 | gcc_assert (!TREE_ADDRESSABLE (orig_arg) | |
1580 | && TREE_CODE (TREE_TYPE (orig_arg)) == REFERENCE_TYPE); | |
1581 | if (def && !has_zero_uses (def)) | |
1582 | { | |
1583 | tree rtype = TREE_TYPE (TREE_TYPE (orig_arg)); | |
1584 | iter1 = make_ssa_name (orig_arg); | |
4ce71579 JJ |
1585 | if (incr_bb) |
1586 | iter2 = make_ssa_name (orig_arg); | |
60cbb674 TS |
1587 | tree iter3 = make_ssa_name (rtype); |
1588 | tree iter4 = make_ssa_name (rtype); | |
4ce71579 | 1589 | tree iter5 = incr_bb ? make_ssa_name (rtype) : NULL_TREE; |
60cbb674 TS |
1590 | gsi = gsi_after_labels (entry_bb); |
1591 | gimple *load | |
1592 | = gimple_build_assign (iter3, build_simple_mem_ref (def)); | |
1593 | gsi_insert_before (&gsi, load, GSI_NEW_STMT); | |
1594 | ||
1595 | tree array = node->simdclone->args[i].simd_array; | |
1596 | TREE_ADDRESSABLE (array) = 1; | |
1597 | tree ptr = build_fold_addr_expr (array); | |
1598 | phi = create_phi_node (iter1, body_bb); | |
1599 | add_phi_arg (phi, ptr, preheader_edge, UNKNOWN_LOCATION); | |
4ce71579 JJ |
1600 | if (incr_bb) |
1601 | { | |
1602 | add_phi_arg (phi, iter2, latch_edge, UNKNOWN_LOCATION); | |
1603 | g = gimple_build_assign (iter2, POINTER_PLUS_EXPR, iter1, | |
1604 | TYPE_SIZE_UNIT (TREE_TYPE (iter3))); | |
1605 | gsi = gsi_last_bb (incr_bb); | |
1606 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1607 | } | |
60cbb674 TS |
1608 | |
1609 | phi = create_phi_node (iter4, body_bb); | |
1610 | add_phi_arg (phi, iter3, preheader_edge, UNKNOWN_LOCATION); | |
4ce71579 JJ |
1611 | if (incr_bb) |
1612 | { | |
1613 | add_phi_arg (phi, iter5, latch_edge, UNKNOWN_LOCATION); | |
1614 | enum tree_code code = INTEGRAL_TYPE_P (TREE_TYPE (iter3)) | |
1615 | ? PLUS_EXPR : POINTER_PLUS_EXPR; | |
1616 | tree addtype = INTEGRAL_TYPE_P (TREE_TYPE (iter3)) | |
1617 | ? TREE_TYPE (iter3) : sizetype; | |
1618 | tree addcst = simd_clone_linear_addend (node, i, addtype, | |
1619 | entry_bb); | |
1620 | g = gimple_build_assign (iter5, code, iter4, addcst); | |
1621 | gsi = gsi_last_bb (incr_bb); | |
1622 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1623 | } | |
60cbb674 TS |
1624 | |
1625 | g = gimple_build_assign (build_simple_mem_ref (iter1), iter4); | |
1626 | gsi = gsi_after_labels (body_bb); | |
1627 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1628 | ||
1629 | imm_use_iterator iter; | |
1630 | use_operand_p use_p; | |
1631 | gimple *use_stmt; | |
1632 | FOR_EACH_IMM_USE_STMT (use_stmt, iter, def) | |
1633 | if (use_stmt == load) | |
1634 | continue; | |
1635 | else | |
1636 | FOR_EACH_IMM_USE_ON_STMT (use_p, iter) | |
1637 | SET_USE (use_p, iter1); | |
1638 | ||
4ce71579 | 1639 | if (!TYPE_READONLY (rtype) && incr_bb) |
60cbb674 TS |
1640 | { |
1641 | tree v = make_ssa_name (rtype); | |
1642 | tree aref = build4 (ARRAY_REF, rtype, array, | |
1643 | size_zero_node, NULL_TREE, | |
1644 | NULL_TREE); | |
1645 | gsi = gsi_after_labels (new_exit_bb); | |
1646 | g = gimple_build_assign (v, aref); | |
1647 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1648 | g = gimple_build_assign (build_simple_mem_ref (def), v); | |
1649 | gsi_insert_before (&gsi, g, GSI_SAME_STMT); | |
1650 | } | |
1651 | } | |
1652 | } | |
1653 | ||
1654 | calculate_dominance_info (CDI_DOMINATORS); | |
4ce71579 JJ |
1655 | if (loop) |
1656 | add_loop (loop, loop->header->loop_father); | |
60cbb674 TS |
1657 | update_ssa (TODO_update_ssa); |
1658 | ||
1659 | pop_cfun (); | |
1660 | } | |
1661 | ||
1662 | /* If the function in NODE is tagged as an elemental SIMD function, | |
1663 | create the appropriate SIMD clones. */ | |
1664 | ||
848bb6fc | 1665 | void |
60cbb674 TS |
1666 | expand_simd_clones (struct cgraph_node *node) |
1667 | { | |
1668 | tree attr = lookup_attribute ("omp declare simd", | |
1669 | DECL_ATTRIBUTES (node->decl)); | |
1670 | if (attr == NULL_TREE | |
a62bfab5 | 1671 | || node->inlined_to |
60cbb674 TS |
1672 | || lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl))) |
1673 | return; | |
1674 | ||
1675 | /* Ignore | |
1676 | #pragma omp declare simd | |
1677 | extern int foo (); | |
1678 | in C, there we don't know the argument types at all. */ | |
1679 | if (!node->definition | |
1680 | && TYPE_ARG_TYPES (TREE_TYPE (node->decl)) == NULL_TREE) | |
1681 | return; | |
1682 | ||
1683 | /* Call this before creating clone_info, as it might ggc_collect. */ | |
1684 | if (node->definition && node->has_gimple_body_p ()) | |
1685 | node->get_body (); | |
1686 | ||
1687 | do | |
1688 | { | |
1689 | /* Start with parsing the "omp declare simd" attribute(s). */ | |
1690 | bool inbranch_clause_specified; | |
1691 | struct cgraph_simd_clone *clone_info | |
1692 | = simd_clone_clauses_extract (node, TREE_VALUE (attr), | |
1693 | &inbranch_clause_specified); | |
1694 | if (clone_info == NULL) | |
1695 | continue; | |
1696 | ||
abe93733 | 1697 | poly_uint64 orig_simdlen = clone_info->simdlen; |
60cbb674 TS |
1698 | tree base_type = simd_clone_compute_base_data_type (node, clone_info); |
1699 | /* The target can return 0 (no simd clones should be created), | |
1700 | 1 (just one ISA of simd clones should be created) or higher | |
1701 | count of ISA variants. In that case, clone_info is initialized | |
1702 | for the first ISA variant. */ | |
1703 | int count | |
1704 | = targetm.simd_clone.compute_vecsize_and_simdlen (node, clone_info, | |
1705 | base_type, 0); | |
1706 | if (count == 0) | |
1707 | continue; | |
1708 | ||
1709 | /* Loop over all COUNT ISA variants, and if !INBRANCH_CLAUSE_SPECIFIED, | |
1710 | also create one inbranch and one !inbranch clone of it. */ | |
1711 | for (int i = 0; i < count * 2; i++) | |
1712 | { | |
1713 | struct cgraph_simd_clone *clone = clone_info; | |
1714 | if (inbranch_clause_specified && (i & 1) != 0) | |
1715 | continue; | |
1716 | ||
1717 | if (i != 0) | |
1718 | { | |
1719 | clone = simd_clone_struct_alloc (clone_info->nargs | |
1720 | + ((i & 1) != 0)); | |
1721 | simd_clone_struct_copy (clone, clone_info); | |
1722 | /* Undo changes targetm.simd_clone.compute_vecsize_and_simdlen | |
1723 | and simd_clone_adjust_argument_types did to the first | |
1724 | clone's info. */ | |
1725 | clone->nargs -= clone_info->inbranch; | |
1726 | clone->simdlen = orig_simdlen; | |
1727 | /* And call the target hook again to get the right ISA. */ | |
1728 | targetm.simd_clone.compute_vecsize_and_simdlen (node, clone, | |
1729 | base_type, | |
1730 | i / 2); | |
1731 | if ((i & 1) != 0) | |
1732 | clone->inbranch = 1; | |
1733 | } | |
1734 | ||
1735 | /* simd_clone_mangle might fail if such a clone has been created | |
1736 | already. */ | |
1737 | tree id = simd_clone_mangle (node, clone); | |
1738 | if (id == NULL_TREE) | |
b843bcb8 JJ |
1739 | { |
1740 | if (i == 0) | |
1741 | clone->nargs += clone->inbranch; | |
1742 | continue; | |
1743 | } | |
60cbb674 TS |
1744 | |
1745 | /* Only when we are sure we want to create the clone actually | |
1746 | clone the function (or definitions) or create another | |
1747 | extern FUNCTION_DECL (for prototypes without definitions). */ | |
1748 | struct cgraph_node *n = simd_clone_create (node); | |
1749 | if (n == NULL) | |
b843bcb8 JJ |
1750 | { |
1751 | if (i == 0) | |
1752 | clone->nargs += clone->inbranch; | |
1753 | continue; | |
1754 | } | |
60cbb674 TS |
1755 | |
1756 | n->simdclone = clone; | |
1757 | clone->origin = node; | |
1758 | clone->next_clone = NULL; | |
1759 | if (node->simd_clones == NULL) | |
1760 | { | |
1761 | clone->prev_clone = n; | |
1762 | node->simd_clones = n; | |
1763 | } | |
1764 | else | |
1765 | { | |
1766 | clone->prev_clone = node->simd_clones->simdclone->prev_clone; | |
1767 | clone->prev_clone->simdclone->next_clone = n; | |
1768 | node->simd_clones->simdclone->prev_clone = n; | |
1769 | } | |
1770 | symtab->change_decl_assembler_name (n->decl, id); | |
1771 | /* And finally adjust the return type, parameters and for | |
1772 | definitions also function body. */ | |
1773 | if (node->definition) | |
1774 | simd_clone_adjust (n); | |
1775 | else | |
1776 | { | |
e8a70c17 SE |
1777 | TREE_TYPE (n->decl) |
1778 | = build_distinct_type_copy (TREE_TYPE (n->decl)); | |
d9186814 | 1779 | targetm.simd_clone.adjust (n); |
60cbb674 TS |
1780 | simd_clone_adjust_return_type (n); |
1781 | simd_clone_adjust_argument_types (n); | |
1782 | } | |
1783 | } | |
1784 | } | |
1785 | while ((attr = lookup_attribute ("omp declare simd", TREE_CHAIN (attr)))); | |
1786 | } | |
1787 | ||
1788 | /* Entry point for IPA simd clone creation pass. */ | |
1789 | ||
1790 | static unsigned int | |
1791 | ipa_omp_simd_clone (void) | |
1792 | { | |
1793 | struct cgraph_node *node; | |
1794 | FOR_EACH_FUNCTION (node) | |
1795 | expand_simd_clones (node); | |
1796 | return 0; | |
1797 | } | |
1798 | ||
1799 | namespace { | |
1800 | ||
1801 | const pass_data pass_data_omp_simd_clone = | |
1802 | { | |
1803 | SIMPLE_IPA_PASS, /* type */ | |
1804 | "simdclone", /* name */ | |
d03958cf | 1805 | OPTGROUP_OMP, /* optinfo_flags */ |
60cbb674 TS |
1806 | TV_NONE, /* tv_id */ |
1807 | ( PROP_ssa | PROP_cfg ), /* properties_required */ | |
1808 | 0, /* properties_provided */ | |
1809 | 0, /* properties_destroyed */ | |
1810 | 0, /* todo_flags_start */ | |
1811 | 0, /* todo_flags_finish */ | |
1812 | }; | |
1813 | ||
1814 | class pass_omp_simd_clone : public simple_ipa_opt_pass | |
1815 | { | |
1816 | public: | |
1817 | pass_omp_simd_clone(gcc::context *ctxt) | |
1818 | : simple_ipa_opt_pass(pass_data_omp_simd_clone, ctxt) | |
1819 | {} | |
1820 | ||
1821 | /* opt_pass methods: */ | |
1822 | virtual bool gate (function *); | |
1823 | virtual unsigned int execute (function *) { return ipa_omp_simd_clone (); } | |
1824 | }; | |
1825 | ||
1826 | bool | |
1827 | pass_omp_simd_clone::gate (function *) | |
1828 | { | |
1829 | return targetm.simd_clone.compute_vecsize_and_simdlen != NULL; | |
1830 | } | |
1831 | ||
1832 | } // anon namespace | |
1833 | ||
1834 | simple_ipa_opt_pass * | |
1835 | make_pass_omp_simd_clone (gcc::context *ctxt) | |
1836 | { | |
1837 | return new pass_omp_simd_clone (ctxt); | |
1838 | } |