111
|
1 /* This file is part of the Intel(R) Cilk(TM) Plus support
|
|
2 This file contains the CilkPlus Intrinsics
|
|
3 Copyright (C) 2013-2017 Free Software Foundation, Inc.
|
|
4 Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com>,
|
|
5 Intel Corporation
|
|
6
|
|
7 This file is part of GCC.
|
|
8
|
|
9 GCC is free software; you can redistribute it and/or modify it
|
|
10 under the terms of the GNU General Public License as published by
|
|
11 the Free Software Foundation; either version 3, or (at your option)
|
|
12 any later version.
|
|
13
|
|
14 GCC is distributed in the hope that it will be useful, but
|
|
15 WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
17 General Public License for more details.
|
|
18
|
|
19 You should have received a copy of the GNU General Public License
|
|
20 along with GCC; see the file COPYING3. If not see
|
|
21 <http://www.gnu.org/licenses/>. */
|
|
22
|
|
23 #include "config.h"
|
|
24 #include "system.h"
|
|
25 #include "coretypes.h"
|
|
26 #include "tm.h"
|
|
27 #include "function.h"
|
|
28 #include "c-family/c-common.h"
|
|
29 #include "gimple-expr.h"
|
|
30 #include "stringpool.h"
|
|
31 #include "cgraph.h"
|
|
32 #include "diagnostic.h"
|
|
33 #include "gimplify.h"
|
|
34 #include "tree-iterator.h"
|
|
35 #include "tree-inline.h"
|
|
36 #include "toplev.h"
|
|
37 #include "calls.h"
|
|
38 #include "cilk.h"
|
|
39
|
|
40 enum add_variable_type {
|
|
41 /* Reference to previously-defined variable. */
|
|
42 ADD_READ,
|
|
43 /* Definition of a new variable in inner-scope. */
|
|
44 ADD_BIND,
|
|
45 /* Write to possibly previously-defined variable. */
|
|
46 ADD_WRITE
|
|
47 };
|
|
48
|
|
49 enum cilk_block_type {
|
|
50 /* Indicates a _Cilk_spawn block. 30 was an arbitary number picked for
|
|
51 ease of debugging. */
|
|
52 CILK_BLOCK_SPAWN = 30,
|
|
53 /* Indicates _Cilk_for statement block. */
|
|
54 CILK_BLOCK_FOR
|
|
55 };
|
|
56
|
|
57 struct wrapper_data
|
|
58 {
|
|
59 /* Kind of function to be created. */
|
|
60 enum cilk_block_type type;
|
|
61 /* Signature of helper function. */
|
|
62 tree fntype;
|
|
63 /* Containing function. */
|
|
64 tree context;
|
|
65 /* Disposition of all variables in the inner statement. */
|
|
66 hash_map<tree, tree> *decl_map;
|
|
67 /* True if this function needs a static chain. */
|
|
68 bool nested;
|
|
69 /* Arguments to be passed to wrapper function, currently a list. */
|
|
70 tree arglist;
|
|
71 /* Argument types, a list. */
|
|
72 tree argtypes;
|
|
73 /* Incoming parameters. */
|
|
74 tree parms;
|
|
75 /* Outer BLOCK object. */
|
|
76 tree block;
|
|
77 };
|
|
78
|
|
79 static tree contains_cilk_spawn_stmt_walker (tree *tp, int *, void *);
|
|
80 static void extract_free_variables (tree, struct wrapper_data *,
|
|
81 enum add_variable_type);
|
|
82 static HOST_WIDE_INT cilk_wrapper_count;
|
|
83
|
|
84 /* Marks the CALL_EXPR or FUNCTION_DECL, FCALL, as a spawned function call
|
|
85 and the current function as a spawner. Emit error if the function call
|
|
86 is outside a function or if a non function-call is spawned. */
|
|
87
|
|
88 inline bool
|
|
89 cilk_set_spawn_marker (location_t loc, tree fcall)
|
|
90 {
|
|
91 if (!current_function_decl)
|
|
92 {
|
|
93 error_at (loc, "%<_Cilk_spawn%> may only be used inside a function");
|
|
94 return false;
|
|
95 }
|
|
96 else if (fcall == error_mark_node)
|
|
97 /* Error reporting here is not necessary here since if FCALL is an
|
|
98 error_mark_node, the function marking it as error would have reported
|
|
99 it. */
|
|
100 return false;
|
|
101 else if (TREE_CODE (fcall) != CALL_EXPR
|
|
102 /* In C++, TARGET_EXPR is generated when we have an overloaded
|
|
103 '=' operator. */
|
|
104 && TREE_CODE (fcall) != TARGET_EXPR)
|
|
105 {
|
|
106 error_at (loc, "only function calls can be spawned");
|
|
107 return false;
|
|
108 }
|
|
109 else
|
|
110 {
|
|
111 cfun->calls_cilk_spawn = true;
|
|
112 if (TREE_CODE (fcall) == CALL_EXPR)
|
|
113 EXPR_CILK_SPAWN (fcall) = 1;
|
|
114 else /* TREE_CODE (fcall) == TARGET_EXPR */
|
|
115 EXPR_CILK_SPAWN (TREE_OPERAND (fcall, 1)) = 1;
|
|
116 return true;
|
|
117 }
|
|
118 }
|
|
119
|
|
120 /* This function will output the exit conditions for a spawn call. */
|
|
121
|
|
122 tree
|
|
123 create_cilk_function_exit (tree frame, bool detaches, bool needs_sync)
|
|
124 {
|
|
125 tree epi = alloc_stmt_list ();
|
|
126
|
|
127 if (needs_sync)
|
|
128 append_to_statement_list (build_cilk_sync (), &epi);
|
|
129 tree func_ptr = build1 (ADDR_EXPR, cilk_frame_ptr_type_decl, frame);
|
|
130 tree pop_frame = build_call_expr (cilk_pop_fndecl, 1, func_ptr);
|
|
131 tree worker = cilk_dot (frame, CILK_TI_FRAME_WORKER, 0);
|
|
132 tree current = cilk_arrow (worker, CILK_TI_WORKER_CUR, 0);
|
|
133 tree parent = cilk_dot (frame, CILK_TI_FRAME_PARENT, 0);
|
|
134 tree set_current = build2 (MODIFY_EXPR, void_type_node, current, parent);
|
|
135 append_to_statement_list (set_current, &epi);
|
|
136 append_to_statement_list (pop_frame, &epi);
|
|
137 tree call = build_call_expr (cilk_leave_fndecl, 1, func_ptr);
|
|
138 if (!detaches)
|
|
139 {
|
|
140 tree flags = cilk_dot (frame, CILK_TI_FRAME_FLAGS, false);
|
|
141 tree flags_cmp_expr = fold_build2 (NE_EXPR, TREE_TYPE (flags), flags,
|
|
142 build_int_cst (TREE_TYPE (flags),
|
|
143 CILK_FRAME_VERSION));
|
|
144 call = fold_build3 (COND_EXPR, void_type_node, flags_cmp_expr,
|
|
145 call, build_empty_stmt (EXPR_LOCATION (flags)));
|
|
146 }
|
|
147 append_to_statement_list (call, &epi);
|
|
148 return epi;
|
|
149 }
|
|
150
|
|
151 /* Trying to get the correct cfun for the FUNCTION_DECL indicated by OUTER. */
|
|
152
|
|
153 static void
|
|
154 pop_cfun_to (tree outer)
|
|
155 {
|
|
156 pop_cfun ();
|
|
157 current_function_decl = outer;
|
|
158 gcc_assert (cfun == DECL_STRUCT_FUNCTION (current_function_decl));
|
|
159 gcc_assert (cfun->decl == current_function_decl);
|
|
160 }
|
|
161
|
|
162 /* This function does whatever is necessary to make the compiler emit a newly
|
|
163 generated function, FNDECL. */
|
|
164
|
|
165 static void
|
|
166 call_graph_add_fn (tree fndecl)
|
|
167 {
|
|
168 const tree outer = current_function_decl;
|
|
169 struct function *f = DECL_STRUCT_FUNCTION (fndecl);
|
|
170 gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
|
|
171
|
|
172 f->is_cilk_function = 1;
|
|
173 f->curr_properties = cfun->curr_properties;
|
|
174 gcc_assert (cfun == DECL_STRUCT_FUNCTION (outer));
|
|
175 gcc_assert (cfun->decl == outer);
|
|
176
|
|
177 push_cfun (f);
|
|
178 cgraph_node::create (fndecl);
|
|
179 pop_cfun_to (outer);
|
|
180 }
|
|
181
|
|
182 /* Return true if this is a tree which is allowed to contain a spawn as
|
|
183 operand 0.
|
|
184 A spawn call may be wrapped in a series of unary operations such
|
|
185 as conversions. These conversions need not be "useless"
|
|
186 to be disregarded because they are retained in the spawned
|
|
187 statement. They are bypassed only to look for a spawn
|
|
188 within.
|
|
189 A comparison to constant is simple enough to allow, and
|
|
190 is used to convert to bool. */
|
|
191
|
|
192 bool
|
|
193 cilk_ignorable_spawn_rhs_op (tree exp)
|
|
194 {
|
|
195 enum tree_code code = TREE_CODE (exp);
|
|
196 switch (TREE_CODE_CLASS (code))
|
|
197 {
|
|
198 case tcc_expression:
|
|
199 return code == ADDR_EXPR;
|
|
200 case tcc_comparison:
|
|
201 /* We need the spawn as operand 0 for now. That's where it
|
|
202 appears in the only case we really care about, conversion
|
|
203 to bool. */
|
|
204 return (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST);
|
|
205 case tcc_unary:
|
|
206 case tcc_reference:
|
|
207 return true;
|
|
208 default:
|
|
209 return false;
|
|
210 }
|
|
211 }
|
|
212
|
|
213 /* Helper function for walk_tree. If *TP is a CILK_SPAWN_STMT, then unwrap
|
|
214 this "wrapper." The function returns NULL_TREE regardless. */
|
|
215
|
|
216 static tree
|
|
217 unwrap_cilk_spawn_stmt (tree *tp, int *walk_subtrees, void *)
|
|
218 {
|
|
219 if (TREE_CODE (*tp) == CILK_SPAWN_STMT)
|
|
220 {
|
|
221 *tp = CILK_SPAWN_FN (*tp);
|
|
222 *walk_subtrees = 0;
|
|
223 }
|
|
224 return NULL_TREE;
|
|
225 }
|
|
226
|
|
227 /* Returns true when EXP is a CALL_EXPR with _Cilk_spawn in front. Unwraps
|
|
228 CILK_SPAWN_STMT wrapper from the CALL_EXPR in *EXP0 statement. */
|
|
229
|
|
230 bool
|
|
231 cilk_recognize_spawn (tree exp, tree *exp0)
|
|
232 {
|
|
233 bool spawn_found = false;
|
|
234 if (TREE_CODE (exp) == CILK_SPAWN_STMT)
|
|
235 {
|
|
236 /* Remove the CALL_EXPR from CILK_SPAWN_STMT wrapper. */
|
|
237 exp = CILK_SPAWN_FN (exp);
|
|
238 walk_tree (exp0, unwrap_cilk_spawn_stmt, NULL, NULL);
|
|
239 spawn_found = true;
|
|
240 }
|
|
241 /* _Cilk_spawn can't be wrapped in expression such as PLUS_EXPR. */
|
|
242 else if (contains_cilk_spawn_stmt (exp))
|
|
243 {
|
|
244 location_t loc = EXPR_LOCATION (exp);
|
|
245 if (loc == UNKNOWN_LOCATION)
|
|
246 {
|
|
247 tree stmt = walk_tree (&exp,
|
|
248 contains_cilk_spawn_stmt_walker,
|
|
249 NULL,
|
|
250 NULL);
|
|
251 gcc_assert (stmt != NULL_TREE);
|
|
252 loc = EXPR_LOCATION (stmt);
|
|
253 }
|
|
254 error_at (loc, "invalid use of %<_Cilk_spawn%>");
|
|
255 }
|
|
256 return spawn_found;
|
|
257 }
|
|
258
|
|
259 /* Returns true if *EXP0 is a recognized form of spawn. Recognized forms are,
|
|
260 after conversion to void, a call expression at outer level or an assignment
|
|
261 at outer level with the right hand side being a spawned call.
|
|
262 In addition to this, it also unwraps the CILK_SPAWN_STMT cover from the
|
|
263 CALL_EXPR that is being spawned.
|
|
264 Note that `=' in C++ may turn into a CALL_EXPR rather than a MODIFY_EXPR. */
|
|
265
|
|
266 bool
|
|
267 cilk_detect_spawn_and_unwrap (tree *exp0)
|
|
268 {
|
|
269 tree exp = *exp0;
|
|
270
|
|
271 if (!TREE_SIDE_EFFECTS (exp))
|
|
272 return false;
|
|
273
|
|
274 /* Strip off any conversion to void. It does not affect whether spawn
|
|
275 is supported here. */
|
|
276 if (TREE_CODE (exp) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (exp)))
|
|
277 exp = TREE_OPERAND (exp, 0);
|
|
278
|
|
279 if (TREE_CODE (exp) == MODIFY_EXPR || TREE_CODE (exp) == INIT_EXPR)
|
|
280 exp = TREE_OPERAND (exp, 1);
|
|
281
|
|
282 while (cilk_ignorable_spawn_rhs_op (exp))
|
|
283 exp = TREE_OPERAND (exp, 0);
|
|
284
|
|
285 if (TREE_CODE (exp) == TARGET_EXPR)
|
|
286 if (TARGET_EXPR_INITIAL (exp)
|
|
287 && TREE_CODE (TARGET_EXPR_INITIAL (exp)) != AGGR_INIT_EXPR)
|
|
288 exp = TARGET_EXPR_INITIAL (exp);
|
|
289
|
|
290 /* Happens with C++ TARGET_EXPR. */
|
|
291 if (exp == NULL_TREE)
|
|
292 return false;
|
|
293
|
|
294 while (TREE_CODE (exp) == CLEANUP_POINT_EXPR || TREE_CODE (exp) == EXPR_STMT)
|
|
295 exp = TREE_OPERAND (exp, 0);
|
|
296
|
|
297 /* Now we should have a CALL_EXPR with a CILK_SPAWN_STMT wrapper around
|
|
298 it, or return false. */
|
|
299 if (cilk_recognize_spawn (exp, exp0))
|
|
300 return true;
|
|
301 return false;
|
|
302 }
|
|
303
|
|
304 /* This function will build and return a FUNCTION_DECL using information
|
|
305 from *WD. */
|
|
306
|
|
307 static tree
|
|
308 create_cilk_helper_decl (struct wrapper_data *wd)
|
|
309 {
|
|
310 char name[20];
|
|
311 if (wd->type == CILK_BLOCK_FOR)
|
|
312 sprintf (name, "_cilk_for_" HOST_WIDE_INT_PRINT_DEC, cilk_wrapper_count++);
|
|
313 else if (wd->type == CILK_BLOCK_SPAWN)
|
|
314 sprintf (name, "_cilk_spn_" HOST_WIDE_INT_PRINT_DEC, cilk_wrapper_count++);
|
|
315 else
|
|
316 gcc_unreachable ();
|
|
317
|
|
318 clean_symbol_name (name);
|
|
319
|
|
320 tree fndecl = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
|
|
321 FUNCTION_DECL, get_identifier (name), wd->fntype);
|
|
322
|
|
323 TREE_PUBLIC (fndecl) = 0;
|
|
324 TREE_STATIC (fndecl) = 1;
|
|
325 TREE_USED (fndecl) = 1;
|
|
326 DECL_ARTIFICIAL (fndecl) = 0;
|
|
327 DECL_IGNORED_P (fndecl) = 0;
|
|
328 DECL_EXTERNAL (fndecl) = 0;
|
|
329
|
|
330 DECL_CONTEXT (fndecl) = wd->context;
|
|
331 tree block = make_node (BLOCK);
|
|
332 DECL_INITIAL (fndecl) = block;
|
|
333 TREE_USED (block) = 1;
|
|
334 BLOCK_SUPERCONTEXT (block) = fndecl;
|
|
335 gcc_assert (!DECL_SAVED_TREE (fndecl));
|
|
336
|
|
337 /* Inlining would defeat the purpose of this wrapper.
|
|
338 Either it secretly switches stack frames or it allocates
|
|
339 a stable stack frame to hold function arguments even if
|
|
340 the parent stack frame is stolen. */
|
|
341 DECL_UNINLINABLE (fndecl) = 1;
|
|
342
|
|
343 tree result_decl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
|
|
344 void_type_node);
|
|
345 DECL_ARTIFICIAL (result_decl) = 0;
|
|
346 DECL_IGNORED_P (result_decl) = 1;
|
|
347 DECL_CONTEXT (result_decl) = fndecl;
|
|
348 DECL_RESULT (fndecl) = result_decl;
|
|
349
|
|
350 return fndecl;
|
|
351 }
|
|
352
|
|
353 struct cilk_decls
|
|
354 {
|
|
355 tree key;
|
|
356 tree *val;
|
|
357 };
|
|
358
|
|
359 /* A function used by traversal to fill vector of decls for further work. */
|
|
360
|
|
361 bool
|
|
362 fill_decls_vec (tree const &key0, tree *val0, auto_vec<struct cilk_decls> *v)
|
|
363 {
|
|
364 tree t1 = key0;
|
|
365 struct cilk_decls dp;
|
|
366
|
|
367 if (DECL_P (t1))
|
|
368 {
|
|
369 dp.key = t1;
|
|
370 dp.val = val0;
|
|
371 v->safe_push (dp);
|
|
372 }
|
|
373 return true;
|
|
374 }
|
|
375
|
|
376 /* Function that actually creates necessary parm lists. */
|
|
377
|
|
378 static void
|
|
379 create_parm_list (struct wrapper_data *wd, tree *val0, tree arg)
|
|
380 {
|
|
381 tree val = *val0;
|
|
382 tree parm;
|
|
383
|
|
384 if (val == error_mark_node || val == arg)
|
|
385 return;
|
|
386
|
|
387 if (TREE_CODE (val) == PAREN_EXPR)
|
|
388 {
|
|
389 /* We should not reach here with a register receiver.
|
|
390 We may see a register variable modified in the
|
|
391 argument list. Because register variables are
|
|
392 worker-local we don't need to work hard to support
|
|
393 them in code that spawns. */
|
|
394 if (VAR_P (arg) && DECL_HARD_REGISTER (arg))
|
|
395 {
|
|
396 error_at (EXPR_LOCATION (arg),
|
|
397 "explicit register variable %qD may not be modified in "
|
|
398 "spawn", arg);
|
|
399 arg = null_pointer_node;
|
|
400 }
|
|
401 else
|
|
402 arg = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (arg)), arg);
|
|
403
|
|
404 val = TREE_OPERAND (val, 0);
|
|
405 *val0 = val;
|
|
406 gcc_assert (INDIRECT_REF_P (val));
|
|
407 parm = TREE_OPERAND (val, 0);
|
|
408 STRIP_NOPS (parm);
|
|
409 }
|
|
410 else
|
|
411 parm = val;
|
|
412 TREE_CHAIN (parm) = wd->parms;
|
|
413 wd->parms = parm;
|
|
414 wd->argtypes = tree_cons (NULL_TREE, TREE_TYPE (parm), wd->argtypes);
|
|
415 wd->arglist = tree_cons (NULL_TREE, arg, wd->arglist);
|
|
416 }
|
|
417
|
|
418 /* Sorting decls in a vector. */
|
|
419
|
|
420 static int
|
|
421 compare_decls (const void *a, const void *b)
|
|
422 {
|
|
423 const struct cilk_decls *t1 = (const struct cilk_decls *) a;
|
|
424 const struct cilk_decls *t2 = (const struct cilk_decls *) b;
|
|
425
|
|
426 if (DECL_UID (t1->key) > DECL_UID (t2->key))
|
|
427 return 1;
|
|
428 else if (DECL_UID (t1->key) < DECL_UID (t2->key))
|
|
429 return -1;
|
|
430 else
|
|
431 return 0;
|
|
432 }
|
|
433
|
|
434 /* This function is used to build a wrapper of a certain type. */
|
|
435
|
|
436 static void
|
|
437 build_wrapper_type (struct wrapper_data *wd)
|
|
438 {
|
|
439 unsigned int j;
|
|
440 struct cilk_decls * c;
|
|
441 auto_vec<struct cilk_decls> vd;
|
|
442 wd->arglist = NULL_TREE;
|
|
443 wd->parms = NULL_TREE;
|
|
444 wd->argtypes = void_list_node;
|
|
445
|
|
446 gcc_assert (wd->type != CILK_BLOCK_FOR);
|
|
447 wd->decl_map->traverse<auto_vec<struct cilk_decls> *, fill_decls_vec> (&vd);
|
|
448 vd.qsort (compare_decls);
|
|
449
|
|
450 FOR_EACH_VEC_ELT (vd, j, c)
|
|
451 create_parm_list (wd, c->val, c->key);
|
|
452
|
|
453 /* Now build a function.
|
|
454 Its return type is void (all side effects are via explicit parameters).
|
|
455 Its parameters are WRAPPER_PARMS with type WRAPPER_TYPES.
|
|
456 Actual arguments in the caller are WRAPPER_ARGS. */
|
|
457 wd->fntype = build_function_type (void_type_node, wd->argtypes);
|
|
458 }
|
|
459
|
|
460 /* This function checks all the CALL_EXPRs in *TP found by cilk_outline. */
|
|
461
|
|
462 static tree
|
|
463 check_outlined_calls (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
|
|
464 void *data)
|
|
465 {
|
|
466 bool *throws = (bool *) data;
|
|
467 tree t = *tp;
|
|
468 int flags;
|
|
469
|
|
470 if (TREE_CODE (t) != CALL_EXPR)
|
|
471 return 0;
|
|
472 flags = call_expr_flags (t);
|
|
473
|
|
474 if (!(flags & ECF_NOTHROW) && flag_exceptions)
|
|
475 *throws = true;
|
|
476 if (flags & ECF_RETURNS_TWICE)
|
|
477 error_at (EXPR_LOCATION (t),
|
|
478 "cannot spawn call to function that returns twice");
|
|
479 return 0;
|
|
480 }
|
|
481
|
|
482 /* Each DECL in the source code (spawned statement) is passed to this function
|
|
483 once. Each instance of the DECL is replaced with the result of this
|
|
484 function.
|
|
485
|
|
486 The parameters of the wrapper should have been entered into the map already.
|
|
487 This function only deals with variables with scope limited to the
|
|
488 spawned expression. */
|
|
489
|
|
490 static tree
|
|
491 copy_decl_for_cilk (tree decl, copy_body_data *id)
|
|
492 {
|
|
493 switch (TREE_CODE (decl))
|
|
494 {
|
|
495 case VAR_DECL:
|
|
496 return copy_decl_no_change (decl, id);
|
|
497
|
|
498 case LABEL_DECL:
|
|
499 error_at (EXPR_LOCATION (decl), "invalid use of label %q+D in "
|
|
500 "%<_Cilk_spawn%>",
|
|
501 decl);
|
|
502 return error_mark_node;
|
|
503
|
|
504 case RESULT_DECL:
|
|
505 case PARM_DECL:
|
|
506 /* RESULT_DECL and PARM_DECL has already been entered into the map. */
|
|
507 default:
|
|
508 gcc_unreachable ();
|
|
509 return error_mark_node;
|
|
510 }
|
|
511 }
|
|
512
|
|
513 /* Alter a tree STMT from OUTER_FN to form the body of INNER_FN. */
|
|
514
|
|
515 void
|
|
516 cilk_outline (tree inner_fn, tree *stmt_p, void *w)
|
|
517 {
|
|
518 struct wrapper_data *wd = (struct wrapper_data *) w;
|
|
519 const tree outer_fn = wd->context;
|
|
520 const bool nested = (wd->type == CILK_BLOCK_FOR);
|
|
521 copy_body_data id;
|
|
522 bool throws;
|
|
523 auto_vec<struct cilk_decls> vd;
|
|
524 unsigned int j;
|
|
525 struct cilk_decls * c;
|
|
526
|
|
527 DECL_STATIC_CHAIN (outer_fn) = 1;
|
|
528
|
|
529 memset (&id, 0, sizeof (id));
|
|
530 /* Copy from the function containing the spawn... */
|
|
531 id.src_fn = outer_fn;
|
|
532
|
|
533 /* ...to the wrapper. */
|
|
534 id.dst_fn = inner_fn;
|
|
535 id.src_cfun = DECL_STRUCT_FUNCTION (outer_fn);
|
|
536
|
|
537 /* There shall be no RETURN in spawn helper. */
|
|
538 id.retvar = 0;
|
|
539 id.decl_map = wd->decl_map;
|
|
540 id.copy_decl = nested ? copy_decl_no_change : copy_decl_for_cilk;
|
|
541 id.block = DECL_INITIAL (inner_fn);
|
|
542 id.transform_lang_insert_block = NULL;
|
|
543
|
|
544 id.transform_new_cfg = true;
|
|
545 id.transform_call_graph_edges = CB_CGE_MOVE;
|
|
546 id.remap_var_for_cilk = true;
|
|
547 id.regimplify = true; /* unused? */
|
|
548
|
|
549 insert_decl_map (&id, wd->block, DECL_INITIAL (inner_fn));
|
|
550
|
|
551 wd->decl_map->traverse<auto_vec<struct cilk_decls> *, fill_decls_vec> (&vd);
|
|
552 vd.qsort (compare_decls);
|
|
553 /* We don't want the private variables any more. */
|
|
554 FOR_EACH_VEC_ELT (vd, j, c)
|
|
555 if (*(c->val) == error_mark_node)
|
|
556 *(c->val) = nested ? copy_decl_no_change (c->key, &id)
|
|
557 : copy_decl_for_cilk (c->key, &id);
|
|
558
|
|
559 walk_tree (stmt_p, copy_tree_body_r, (void *) &id, NULL);
|
|
560
|
|
561 /* See if this function can throw or calls something that should
|
|
562 not be spawned. The exception part is only necessary if
|
|
563 flag_exceptions && !flag_non_call_exceptions. */
|
|
564 throws = false ;
|
|
565 (void) walk_tree_without_duplicates (stmt_p, check_outlined_calls, &throws);
|
|
566 }
|
|
567
|
|
568 /* Generate the body of a wrapper function that assigns the
|
|
569 result of the expression RHS into RECEIVER. RECEIVER must
|
|
570 be NULL if this is not a spawn -- the wrapper will return
|
|
571 a value. If this is a spawn, the wrapper will return void. */
|
|
572
|
|
573 static tree
|
|
574 create_cilk_wrapper_body (tree stmt, struct wrapper_data *wd)
|
|
575 {
|
|
576 const tree outer = current_function_decl;
|
|
577 tree fndecl;
|
|
578 tree p;
|
|
579
|
|
580 /* Build the type of the wrapper and its argument list from the
|
|
581 variables that it requires. */
|
|
582 build_wrapper_type (wd);
|
|
583
|
|
584 /* Emit a function that takes WRAPPER_PARMS incoming and applies ARGS
|
|
585 (modified) to the wrapped function. Return the wrapper and modified ARGS
|
|
586 to the caller to generate a function call. */
|
|
587 fndecl = create_cilk_helper_decl (wd);
|
|
588 push_struct_function (fndecl);
|
|
589 if (wd->nested && (wd->type == CILK_BLOCK_FOR))
|
|
590 {
|
|
591 gcc_assert (TREE_VALUE (wd->arglist) == NULL_TREE);
|
|
592 TREE_VALUE (wd->arglist) = build2 (FDESC_EXPR, ptr_type_node,
|
|
593 fndecl, integer_one_node);
|
|
594 }
|
|
595 DECL_ARGUMENTS (fndecl) = wd->parms;
|
|
596
|
|
597 for (p = wd->parms; p; p = TREE_CHAIN (p))
|
|
598 DECL_CONTEXT (p) = fndecl;
|
|
599
|
|
600 /* The statement containing the spawn expression might create temporaries with
|
|
601 destructors defined; if so we need to add a CLEANUP_POINT_EXPR to ensure
|
|
602 the expression is properly gimplified. */
|
|
603 stmt = fold_build_cleanup_point_expr (void_type_node, stmt);
|
|
604
|
|
605 gcc_assert (!DECL_SAVED_TREE (fndecl));
|
|
606 cilk_install_body_with_frame_cleanup (fndecl, stmt, (void *) wd);
|
|
607 gcc_assert (DECL_SAVED_TREE (fndecl));
|
|
608
|
|
609 pop_cfun_to (outer);
|
|
610
|
|
611 /* Recognize the new function. */
|
|
612 call_graph_add_fn (fndecl);
|
|
613 return fndecl;
|
|
614 }
|
|
615
|
|
616 /* Initializes the wrapper data structure. */
|
|
617
|
|
618 static void
|
|
619 init_wd (struct wrapper_data *wd, enum cilk_block_type type)
|
|
620 {
|
|
621 wd->type = type;
|
|
622 wd->fntype = NULL_TREE;
|
|
623 wd->context = current_function_decl;
|
|
624 wd->decl_map = new hash_map<tree, tree>;
|
|
625 /* _Cilk_for bodies are always nested. Others start off as
|
|
626 normal functions. */
|
|
627 wd->nested = (type == CILK_BLOCK_FOR);
|
|
628 wd->arglist = NULL_TREE;
|
|
629 wd->argtypes = NULL_TREE;
|
|
630 wd->block = NULL_TREE;
|
|
631 }
|
|
632
|
|
633 /* Clears the wrapper data structure. */
|
|
634
|
|
635 static void
|
|
636 free_wd (struct wrapper_data *wd)
|
|
637 {
|
|
638 delete wd->decl_map;
|
|
639 wd->nested = false;
|
|
640 wd->arglist = NULL_TREE;
|
|
641 wd->argtypes = NULL_TREE;
|
|
642 wd->parms = NULL_TREE;
|
|
643 }
|
|
644
|
|
645
|
|
646 /* Given a variable in an expression to be extracted into
|
|
647 a helper function, declare the helper function parameter
|
|
648 to receive it.
|
|
649
|
|
650 On entry the value of the (key, value) pair may be
|
|
651
|
|
652 (*, error_mark_node) -- Variable is private to helper function,
|
|
653 do nothing.
|
|
654
|
|
655 (var, var) -- Reference to outer scope (function or global scope).
|
|
656
|
|
657 (var, integer 0) -- Capture by value, save newly-declared PARM_DECL
|
|
658 for value in value slot.
|
|
659
|
|
660 (var, integer 1) -- Capture by reference, declare pointer to type
|
|
661 as new PARM_DECL and store (spawn_stmt (indirect_ref (parm)).
|
|
662
|
|
663 (var, ???) -- Pure output argument, handled similarly to above.
|
|
664 */
|
|
665
|
|
666 bool
|
|
667 declare_one_free_variable (tree var0, tree *map0)
|
|
668 {
|
|
669 const_tree var = var0;
|
|
670 tree map = *map0;
|
|
671 tree var_type = TREE_TYPE (var), arg_type;
|
|
672 bool by_reference;
|
|
673 tree parm;
|
|
674
|
|
675 gcc_assert (DECL_P (var));
|
|
676
|
|
677 /* Ignore truly local variables. */
|
|
678 if (map == error_mark_node)
|
|
679 return true;
|
|
680 /* Ignore references to the parent function. */
|
|
681 if (map == var)
|
|
682 return true;
|
|
683
|
|
684 gcc_assert (TREE_CODE (map) == INTEGER_CST);
|
|
685
|
|
686 /* A value is passed by reference if:
|
|
687
|
|
688 1. It is addressable, so that a copy may not be made.
|
|
689 2. It is modified in the spawned statement.
|
|
690 In the future this function may want to arrange
|
|
691 a warning if the spawned statement is a loop body
|
|
692 because an output argument would indicate a race.
|
|
693 Note: Earlier passes must have marked the variable addressable.
|
|
694 3. It is expensive to copy. */
|
|
695 by_reference =
|
|
696 (TREE_ADDRESSABLE (var_type)
|
|
697 /* Arrays must be passed by reference. This is required for C
|
|
698 semantics -- arrays are not first class objects. Other
|
|
699 aggregate types can and should be passed by reference if
|
|
700 they are not passed to the spawned function. We aren't yet
|
|
701 distinguishing safe uses in argument calculation from unsafe
|
|
702 uses as outgoing function arguments, so we make a copy to
|
|
703 stabilize the value. */
|
|
704 || TREE_CODE (var_type) == ARRAY_TYPE
|
|
705 || (tree) map == integer_one_node);
|
|
706
|
|
707 if (by_reference)
|
|
708 var_type = build_qualified_type (build_pointer_type (var_type),
|
|
709 TYPE_QUAL_RESTRICT);
|
|
710 gcc_assert (!TREE_ADDRESSABLE (var_type));
|
|
711
|
|
712 /* Maybe promote to int. */
|
|
713 if (INTEGRAL_TYPE_P (var_type) && COMPLETE_TYPE_P (var_type)
|
|
714 && tree_int_cst_lt (TYPE_SIZE (var_type), TYPE_SIZE (integer_type_node)))
|
|
715 arg_type = integer_type_node;
|
|
716 else
|
|
717 arg_type = var_type;
|
|
718
|
|
719 parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, var_type);
|
|
720 DECL_ARG_TYPE (parm) = arg_type;
|
|
721 DECL_ARTIFICIAL (parm) = 0;
|
|
722 TREE_READONLY (parm) = 1;
|
|
723
|
|
724 if (by_reference)
|
|
725 {
|
|
726 parm = build1 (INDIRECT_REF, TREE_TYPE (var_type), parm);
|
|
727 parm = build1 (PAREN_EXPR, void_type_node, parm);
|
|
728 }
|
|
729 *map0 = parm;
|
|
730 return true;
|
|
731 }
|
|
732
|
|
733 /* Returns a wrapper function for a _Cilk_spawn. */
|
|
734
|
|
735 static tree
|
|
736 create_cilk_wrapper (tree exp, tree *args_out)
|
|
737 {
|
|
738 struct wrapper_data wd;
|
|
739 tree fndecl;
|
|
740 unsigned int j;
|
|
741 struct cilk_decls * c;
|
|
742 auto_vec<struct cilk_decls> vd;
|
|
743
|
|
744 init_wd (&wd, CILK_BLOCK_SPAWN);
|
|
745
|
|
746 if (TREE_CODE (exp) == CONVERT_EXPR)
|
|
747 exp = TREE_OPERAND (exp, 0);
|
|
748
|
|
749 /* Special handling for top level INIT_EXPR. Usually INIT_EXPR means the
|
|
750 variable is defined in the spawned expression and can be private to the
|
|
751 spawn helper. A top level INIT_EXPR defines a variable to be initialized
|
|
752 by spawn and the variable must remain in the outer function. */
|
|
753 if (TREE_CODE (exp) == INIT_EXPR)
|
|
754 {
|
|
755 extract_free_variables (TREE_OPERAND (exp, 0), &wd, ADD_WRITE);
|
|
756 extract_free_variables (TREE_OPERAND (exp, 1), &wd, ADD_READ);
|
|
757 /* TREE_TYPE should be void. Be defensive. */
|
|
758 if (TREE_TYPE (exp) != void_type_node)
|
|
759 extract_free_variables (TREE_TYPE (exp), &wd, ADD_READ);
|
|
760 }
|
|
761 else
|
|
762 extract_free_variables (exp, &wd, ADD_READ);
|
|
763 wd.decl_map->traverse<auto_vec<struct cilk_decls> *, fill_decls_vec> (&vd);
|
|
764 vd.qsort (compare_decls);
|
|
765 FOR_EACH_VEC_ELT (vd, j, c)
|
|
766 declare_one_free_variable (c->key, c->val);
|
|
767
|
|
768 wd.block = TREE_BLOCK (exp);
|
|
769 if (!wd.block)
|
|
770 wd.block = DECL_INITIAL (current_function_decl);
|
|
771
|
|
772 /* Now fvars maps the old variable to incoming variable. Update
|
|
773 the expression and arguments to refer to the new names. */
|
|
774 fndecl = create_cilk_wrapper_body (exp, &wd);
|
|
775 *args_out = wd.arglist;
|
|
776
|
|
777 free_wd (&wd);
|
|
778
|
|
779 return fndecl;
|
|
780 }
|
|
781
|
|
782 /* Transform *SPAWN_P, a spawned CALL_EXPR, to gimple. *SPAWN_P can be a
|
|
783 CALL_EXPR, INIT_EXPR or MODIFY_EXPR. Returns GS_OK if everything is fine,
|
|
784 and GS_UNHANDLED, otherwise. */
|
|
785
|
|
786 int
|
|
787 gimplify_cilk_spawn (tree *spawn_p)
|
|
788 {
|
|
789 tree expr = *spawn_p;
|
|
790 tree function, call1, call2, new_args;
|
|
791 tree ii_args = NULL_TREE;
|
|
792 int total_args = 0, ii = 0;
|
|
793 tree *arg_array;
|
|
794 tree setjmp_cond_expr = NULL_TREE;
|
|
795 tree setjmp_expr, spawn_expr, setjmp_value = NULL_TREE;
|
|
796
|
|
797 cfun->calls_cilk_spawn = 1;
|
|
798 cfun->is_cilk_function = 1;
|
|
799
|
|
800 /* Remove CLEANUP_POINT_EXPR and EXPR_STMT from *spawn_p. */
|
|
801 while (TREE_CODE (expr) == CLEANUP_POINT_EXPR
|
|
802 || TREE_CODE (expr) == EXPR_STMT)
|
|
803 expr = TREE_OPERAND (expr, 0);
|
|
804
|
|
805 new_args = NULL;
|
|
806 function = create_cilk_wrapper (expr, &new_args);
|
|
807
|
|
808 /* This should give the number of parameters. */
|
|
809 total_args = list_length (new_args);
|
|
810 if (total_args)
|
|
811 arg_array = XNEWVEC (tree, total_args);
|
|
812 else
|
|
813 arg_array = NULL;
|
|
814
|
|
815 ii_args = new_args;
|
|
816 for (ii = 0; ii < total_args; ii++)
|
|
817 {
|
|
818 arg_array[ii] = TREE_VALUE (ii_args);
|
|
819 ii_args = TREE_CHAIN (ii_args);
|
|
820 }
|
|
821
|
|
822 TREE_USED (function) = 1;
|
|
823 rest_of_decl_compilation (function, 0, 0);
|
|
824
|
|
825 call1 = cilk_call_setjmp (cfun->cilk_frame_decl);
|
|
826
|
|
827 if (arg_array == NULL || *arg_array == NULL_TREE)
|
|
828 call2 = build_call_expr (function, 0);
|
|
829 else
|
|
830 call2 = build_call_expr_loc_array (EXPR_LOCATION (*spawn_p), function,
|
|
831 total_args, arg_array);
|
|
832 *spawn_p = alloc_stmt_list ();
|
|
833 tree f_ptr_type = build_pointer_type (TREE_TYPE (cfun->cilk_frame_decl));
|
|
834 tree frame_ptr = build1 (ADDR_EXPR, f_ptr_type, cfun->cilk_frame_decl);
|
|
835 tree save_fp = build_call_expr (cilk_save_fp_fndecl, 1, frame_ptr);
|
|
836 append_to_statement_list (save_fp, spawn_p);
|
|
837 setjmp_value = create_tmp_var (TREE_TYPE (call1));
|
|
838 setjmp_expr = fold_build2 (MODIFY_EXPR, void_type_node, setjmp_value, call1);
|
|
839
|
|
840 append_to_statement_list_force (setjmp_expr, spawn_p);
|
|
841
|
|
842 setjmp_cond_expr = fold_build2 (EQ_EXPR, TREE_TYPE (call1), setjmp_value,
|
|
843 build_int_cst (TREE_TYPE (call1), 0));
|
|
844 spawn_expr = fold_build3 (COND_EXPR, void_type_node, setjmp_cond_expr,
|
|
845 call2, build_empty_stmt (EXPR_LOCATION (call1)));
|
|
846 append_to_statement_list (spawn_expr, spawn_p);
|
|
847
|
|
848 free (arg_array);
|
|
849 return GS_OK;
|
|
850 }
|
|
851
|
|
852 /* Make the frames necessary for a spawn call. */
|
|
853
|
|
854 tree
|
|
855 make_cilk_frame (tree fn)
|
|
856 {
|
|
857 struct function *f = DECL_STRUCT_FUNCTION (fn);
|
|
858 tree decl;
|
|
859
|
|
860 if (f->cilk_frame_decl)
|
|
861 return f->cilk_frame_decl;
|
|
862
|
|
863 decl = build_decl (EXPR_LOCATION (fn), VAR_DECL, NULL_TREE,
|
|
864 cilk_frame_type_decl);
|
|
865 DECL_CONTEXT (decl) = fn;
|
|
866 DECL_SEEN_IN_BIND_EXPR_P (decl) = 1;
|
|
867 f->cilk_frame_decl = decl;
|
|
868 return decl;
|
|
869 }
|
|
870
|
|
871 /* Add a new variable, VAR to a variable list in WD->DECL_MAP. HOW indicates
|
|
872 whether the variable is previously defined, currently defined, or a variable
|
|
873 that is being written to. */
|
|
874
|
|
875 static void
|
|
876 add_variable (struct wrapper_data *wd, tree var, enum add_variable_type how)
|
|
877 {
|
|
878 tree *valp = wd->decl_map->get (var);
|
|
879 if (valp)
|
|
880 {
|
|
881 tree val = (tree) *valp;
|
|
882 /* If the variable is local, do nothing. */
|
|
883 if (val == error_mark_node)
|
|
884 return;
|
|
885 /* If the variable was entered with itself as value,
|
|
886 meaning it belongs to an outer scope, do not alter
|
|
887 the value. */
|
|
888 if (val == var)
|
|
889 return;
|
|
890 /* A statement expression may cause a variable to be
|
|
891 bound twice, once in BIND_EXPR and again in a
|
|
892 DECL_EXPR. That case caused a return in the
|
|
893 test above. Any other duplicate definition is
|
|
894 an error. */
|
|
895 gcc_assert (how != ADD_BIND);
|
|
896 if (how != ADD_WRITE)
|
|
897 return;
|
|
898 /* This variable might have been entered as read but is now written. */
|
|
899 *valp = var;
|
|
900 wd->nested = true;
|
|
901 return;
|
|
902 }
|
|
903 else
|
|
904 {
|
|
905 tree val = NULL_TREE;
|
|
906
|
|
907 /* Nested function rewriting silently discards hard register
|
|
908 assignments for function scope variables, and they wouldn't
|
|
909 work anyway. Warn here. This misses one case: if the
|
|
910 register variable is used as the loop bound or increment it
|
|
911 has already been added to the map. */
|
|
912 if ((how != ADD_BIND) && VAR_P (var)
|
|
913 && !DECL_EXTERNAL (var) && DECL_HARD_REGISTER (var))
|
|
914 warning (0, "register assignment ignored for %qD used in Cilk block",
|
|
915 var);
|
|
916
|
|
917 switch (how)
|
|
918 {
|
|
919 /* ADD_BIND means always make a fresh new variable. */
|
|
920 case ADD_BIND:
|
|
921 val = error_mark_node;
|
|
922 break;
|
|
923 /* ADD_READ means
|
|
924 1. For cilk_for, refer to the outer scope definition as-is
|
|
925 2. For a spawned block, take a scalar in an rgument
|
|
926 and otherwise refer to the outer scope definition as-is.
|
|
927 3. For a spawned call, take a scalar in an argument. */
|
|
928 case ADD_READ:
|
|
929 switch (wd->type)
|
|
930 {
|
|
931 case CILK_BLOCK_FOR:
|
|
932 val = var;
|
|
933 break;
|
|
934 case CILK_BLOCK_SPAWN:
|
|
935 if (TREE_ADDRESSABLE (var))
|
|
936 {
|
|
937 val = var;
|
|
938 wd->nested = true;
|
|
939 break;
|
|
940 }
|
|
941 val = integer_zero_node;
|
|
942 break;
|
|
943 }
|
|
944 break;
|
|
945 case ADD_WRITE:
|
|
946 switch (wd->type)
|
|
947 {
|
|
948 case CILK_BLOCK_FOR:
|
|
949 val = var;
|
|
950 wd->nested = true;
|
|
951 break;
|
|
952 case CILK_BLOCK_SPAWN:
|
|
953 if (TREE_ADDRESSABLE (var))
|
|
954 val = integer_one_node;
|
|
955 else
|
|
956 {
|
|
957 val = var;
|
|
958 wd->nested = true;
|
|
959 }
|
|
960 break;
|
|
961 }
|
|
962 }
|
|
963 wd->decl_map->put (var, val);
|
|
964 }
|
|
965 }
|
|
966
|
|
967 /* Find the variables referenced in an expression T. This does not avoid
|
|
968 duplicates because a variable may be read in one context and written in
|
|
969 another. HOW describes the context in which the reference is seen. If
|
|
970 NESTED is true a nested function is being generated and variables in the
|
|
971 original context should not be remapped. */
|
|
972
|
|
973 static void
|
|
974 extract_free_variables (tree t, struct wrapper_data *wd,
|
|
975 enum add_variable_type how)
|
|
976 {
|
|
977 if (t == NULL_TREE)
|
|
978 return;
|
|
979
|
|
980 enum tree_code code = TREE_CODE (t);
|
|
981 bool is_expr = IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code));
|
|
982
|
|
983 if (is_expr)
|
|
984 extract_free_variables (TREE_TYPE (t), wd, ADD_READ);
|
|
985
|
|
986 switch (code)
|
|
987 {
|
|
988 case ERROR_MARK:
|
|
989 case IDENTIFIER_NODE:
|
|
990 case VOID_CST:
|
|
991 case INTEGER_CST:
|
|
992 case REAL_CST:
|
|
993 case FIXED_CST:
|
|
994 case STRING_CST:
|
|
995 case BLOCK:
|
|
996 case PLACEHOLDER_EXPR:
|
|
997 case FIELD_DECL:
|
|
998 case VOID_TYPE:
|
|
999 case REAL_TYPE:
|
|
1000 /* These do not contain variable references. */
|
|
1001 return;
|
|
1002
|
|
1003 case SSA_NAME:
|
|
1004 /* Currently we don't see SSA_NAME. */
|
|
1005 extract_free_variables (SSA_NAME_VAR (t), wd, how);
|
|
1006 return;
|
|
1007
|
|
1008 case LABEL_DECL:
|
|
1009 /* This might be a reference to a label outside the Cilk block,
|
|
1010 which is an error, or a reference to a label in the Cilk block
|
|
1011 that we haven't seen yet. We can't tell. Ignore it. An
|
|
1012 invalid use will cause an error later in copy_decl_for_cilk. */
|
|
1013 return;
|
|
1014
|
|
1015 case RESULT_DECL:
|
|
1016 if (wd->type != CILK_BLOCK_SPAWN)
|
|
1017 TREE_ADDRESSABLE (t) = 1;
|
|
1018 /* FALLTHRU */
|
|
1019 case VAR_DECL:
|
|
1020 case PARM_DECL:
|
|
1021 if (!is_global_var (t))
|
|
1022 add_variable (wd, t, how);
|
|
1023 return;
|
|
1024
|
|
1025 case NON_LVALUE_EXPR:
|
|
1026 case CONVERT_EXPR:
|
|
1027 case NOP_EXPR:
|
|
1028 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ);
|
|
1029 return;
|
|
1030
|
|
1031 case VEC_INIT_EXPR:
|
|
1032 case INIT_EXPR:
|
|
1033 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_BIND);
|
|
1034 extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ);
|
|
1035 return;
|
|
1036
|
|
1037 case MODIFY_EXPR:
|
|
1038 case PREDECREMENT_EXPR:
|
|
1039 case PREINCREMENT_EXPR:
|
|
1040 case POSTDECREMENT_EXPR:
|
|
1041 case POSTINCREMENT_EXPR:
|
|
1042 /* These write their result. */
|
|
1043 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_WRITE);
|
|
1044 extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ);
|
|
1045 return;
|
|
1046
|
|
1047 case ADDR_EXPR:
|
|
1048 /* This might modify its argument, and the value needs to be
|
|
1049 passed by reference in any case to preserve identity and
|
|
1050 type if is a promoting type. In the case of a nested loop
|
|
1051 just notice that we touch the variable. It will already
|
|
1052 be addressable, and marking it modified will cause a spurious
|
|
1053 warning about writing the control variable. */
|
|
1054 if (wd->type != CILK_BLOCK_SPAWN)
|
|
1055 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ);
|
|
1056 else
|
|
1057 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_WRITE);
|
|
1058 return;
|
|
1059
|
|
1060 case ARRAY_REF:
|
|
1061 /* Treating ARRAY_REF and BIT_FIELD_REF identically may
|
|
1062 mark the array as written but the end result is correct
|
|
1063 because the array is passed by pointer anyway. */
|
|
1064 case BIT_FIELD_REF:
|
|
1065 /* Propagate the access type to the object part of which
|
|
1066 is being accessed here. As for ADDR_EXPR, don't do this
|
|
1067 in a nested loop, unless the access is to a fixed index. */
|
|
1068 if (wd->type != CILK_BLOCK_FOR || TREE_CONSTANT (TREE_OPERAND (t, 1)))
|
|
1069 extract_free_variables (TREE_OPERAND (t, 0), wd, how);
|
|
1070 else
|
|
1071 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ);
|
|
1072 extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ);
|
|
1073 extract_free_variables (TREE_OPERAND (t, 2), wd, ADD_READ);
|
|
1074 return;
|
|
1075
|
|
1076 case TREE_LIST:
|
|
1077 extract_free_variables (TREE_PURPOSE (t), wd, ADD_READ);
|
|
1078 extract_free_variables (TREE_VALUE (t), wd, ADD_READ);
|
|
1079 extract_free_variables (TREE_CHAIN (t), wd, ADD_READ);
|
|
1080 return;
|
|
1081
|
|
1082 case TREE_VEC:
|
|
1083 {
|
|
1084 int len = TREE_VEC_LENGTH (t);
|
|
1085 int i;
|
|
1086 for (i = 0; i < len; i++)
|
|
1087 extract_free_variables (TREE_VEC_ELT (t, i), wd, ADD_READ);
|
|
1088 return;
|
|
1089 }
|
|
1090
|
|
1091 case VECTOR_CST:
|
|
1092 {
|
|
1093 unsigned ii = 0;
|
|
1094 for (ii = 0; ii < VECTOR_CST_NELTS (t); ii++)
|
|
1095 extract_free_variables (VECTOR_CST_ELT (t, ii), wd, ADD_READ);
|
|
1096 break;
|
|
1097 }
|
|
1098
|
|
1099 case COMPLEX_CST:
|
|
1100 extract_free_variables (TREE_REALPART (t), wd, ADD_READ);
|
|
1101 extract_free_variables (TREE_IMAGPART (t), wd, ADD_READ);
|
|
1102 return;
|
|
1103
|
|
1104 case BIND_EXPR:
|
|
1105 {
|
|
1106 tree decl;
|
|
1107 for (decl = BIND_EXPR_VARS (t); decl; decl = TREE_CHAIN (decl))
|
|
1108 {
|
|
1109 add_variable (wd, decl, ADD_BIND);
|
|
1110 /* A self-referential initialization is no problem because
|
|
1111 we already entered the variable into the map as local. */
|
|
1112 extract_free_variables (DECL_INITIAL (decl), wd, ADD_READ);
|
|
1113 extract_free_variables (DECL_SIZE (decl), wd, ADD_READ);
|
|
1114 extract_free_variables (DECL_SIZE_UNIT (decl), wd, ADD_READ);
|
|
1115 }
|
|
1116 extract_free_variables (BIND_EXPR_BODY (t), wd, ADD_READ);
|
|
1117 return;
|
|
1118 }
|
|
1119
|
|
1120 case STATEMENT_LIST:
|
|
1121 {
|
|
1122 tree_stmt_iterator i;
|
|
1123 for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
|
|
1124 extract_free_variables (*tsi_stmt_ptr (i), wd, ADD_READ);
|
|
1125 return;
|
|
1126 }
|
|
1127
|
|
1128 case TARGET_EXPR:
|
|
1129 {
|
|
1130 extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_BIND);
|
|
1131 extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ);
|
|
1132 extract_free_variables (TREE_OPERAND (t, 2), wd, ADD_READ);
|
|
1133 if (TREE_OPERAND (t, 3) != TREE_OPERAND (t, 1))
|
|
1134 extract_free_variables (TREE_OPERAND (t, 3), wd, ADD_READ);
|
|
1135 return;
|
|
1136 }
|
|
1137
|
|
1138 case RETURN_EXPR:
|
|
1139 if (TREE_NO_WARNING (t))
|
|
1140 {
|
|
1141 gcc_assert (errorcount);
|
|
1142 return;
|
|
1143 }
|
|
1144 return;
|
|
1145
|
|
1146 case DECL_EXPR:
|
|
1147 if (TREE_CODE (DECL_EXPR_DECL (t)) != TYPE_DECL)
|
|
1148 extract_free_variables (DECL_EXPR_DECL (t), wd, ADD_BIND);
|
|
1149 return;
|
|
1150
|
|
1151 case INTEGER_TYPE:
|
|
1152 case ENUMERAL_TYPE:
|
|
1153 case BOOLEAN_TYPE:
|
|
1154 extract_free_variables (TYPE_MIN_VALUE (t), wd, ADD_READ);
|
|
1155 extract_free_variables (TYPE_MAX_VALUE (t), wd, ADD_READ);
|
|
1156 return;
|
|
1157
|
|
1158 case POINTER_TYPE:
|
|
1159 extract_free_variables (TREE_TYPE (t), wd, ADD_READ);
|
|
1160 break;
|
|
1161
|
|
1162 case ARRAY_TYPE:
|
|
1163 extract_free_variables (TREE_TYPE (t), wd, ADD_READ);
|
|
1164 extract_free_variables (TYPE_DOMAIN (t), wd, ADD_READ);
|
|
1165 return;
|
|
1166
|
|
1167 case RECORD_TYPE:
|
|
1168 extract_free_variables (TYPE_FIELDS (t), wd, ADD_READ);
|
|
1169 return;
|
|
1170
|
|
1171 case METHOD_TYPE:
|
|
1172 extract_free_variables (TYPE_ARG_TYPES (t), wd, ADD_READ);
|
|
1173 extract_free_variables (TYPE_METHOD_BASETYPE (t), wd, ADD_READ);
|
|
1174 return;
|
|
1175
|
|
1176 case AGGR_INIT_EXPR:
|
|
1177 {
|
|
1178 int len = 0;
|
|
1179 int ii = 0;
|
|
1180 extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ);
|
|
1181 if (TREE_CODE (TREE_OPERAND (t, 0)) == INTEGER_CST)
|
|
1182 {
|
|
1183 len = TREE_INT_CST_LOW (TREE_OPERAND (t, 0));
|
|
1184
|
|
1185 for (ii = 3; ii < len; ii++)
|
|
1186 extract_free_variables (TREE_OPERAND (t, ii), wd, ADD_READ);
|
|
1187 extract_free_variables (TREE_TYPE (t), wd, ADD_READ);
|
|
1188 }
|
|
1189 break;
|
|
1190 }
|
|
1191
|
|
1192 case CALL_EXPR:
|
|
1193 {
|
|
1194 int len = 0;
|
|
1195 int ii = 0;
|
|
1196 if (TREE_CODE (TREE_OPERAND (t, 0)) == INTEGER_CST)
|
|
1197 {
|
|
1198 len = TREE_INT_CST_LOW (TREE_OPERAND (t, 0));
|
|
1199
|
|
1200 for (ii = 0; ii < len; ii++)
|
|
1201 extract_free_variables (TREE_OPERAND (t, ii), wd, ADD_READ);
|
|
1202 extract_free_variables (TREE_TYPE (t), wd, ADD_READ);
|
|
1203 }
|
|
1204 break;
|
|
1205 }
|
|
1206
|
|
1207 case CONSTRUCTOR:
|
|
1208 {
|
|
1209 unsigned HOST_WIDE_INT idx = 0;
|
|
1210 constructor_elt *ce;
|
|
1211 for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (t), idx, &ce); idx++)
|
|
1212 extract_free_variables (ce->value, wd, ADD_READ);
|
|
1213 break;
|
|
1214 }
|
|
1215
|
|
1216 default:
|
|
1217 if (is_expr)
|
|
1218 {
|
|
1219 int i, len;
|
|
1220
|
|
1221 /* Walk over all the sub-trees of this operand. */
|
|
1222 len = TREE_CODE_LENGTH (code);
|
|
1223
|
|
1224 /* Go through the subtrees. We need to do this in forward order so
|
|
1225 that the scope of a FOR_EXPR is handled properly. */
|
|
1226 for (i = 0; i < len; ++i)
|
|
1227 extract_free_variables (TREE_OPERAND (t, i), wd, ADD_READ);
|
|
1228 }
|
|
1229 }
|
|
1230 }
|
|
1231
|
|
1232 /* Add appropriate frames needed for a Cilk spawned function call, FNDECL.
|
|
1233 Returns the __cilkrts_stack_frame * variable. */
|
|
1234
|
|
1235 tree
|
|
1236 insert_cilk_frame (tree fndecl)
|
|
1237 {
|
|
1238 tree addr, body, enter, out, orig_body;
|
|
1239 location_t loc = EXPR_LOCATION (fndecl);
|
|
1240
|
|
1241 if (!cfun || cfun->decl != fndecl)
|
|
1242 push_cfun (DECL_STRUCT_FUNCTION (fndecl));
|
|
1243
|
|
1244 tree decl = cfun->cilk_frame_decl;
|
|
1245 if (!decl)
|
|
1246 {
|
|
1247 tree *saved_tree = &DECL_SAVED_TREE (fndecl);
|
|
1248 decl = make_cilk_frame (fndecl);
|
|
1249 add_local_decl (cfun, decl);
|
|
1250
|
|
1251 addr = build1 (ADDR_EXPR, cilk_frame_ptr_type_decl, decl);
|
|
1252 enter = build_call_expr (cilk_enter_fndecl, 1, addr);
|
|
1253 out = create_cilk_function_exit (cfun->cilk_frame_decl, false, true);
|
|
1254
|
|
1255 /* The new body will be:
|
|
1256 __cilkrts_enter_frame_1 (&sf);
|
|
1257 try {
|
|
1258 orig_body;
|
|
1259 }
|
|
1260 finally {
|
|
1261 __cilkrts_pop_frame (&sf);
|
|
1262 __cilkrts_leave_frame (&sf);
|
|
1263 } */
|
|
1264
|
|
1265 body = alloc_stmt_list ();
|
|
1266 orig_body = *saved_tree;
|
|
1267
|
|
1268 if (TREE_CODE (orig_body) == BIND_EXPR)
|
|
1269 orig_body = BIND_EXPR_BODY (orig_body);
|
|
1270
|
|
1271 append_to_statement_list (enter, &body);
|
|
1272 append_to_statement_list (build_stmt (loc, TRY_FINALLY_EXPR, orig_body,
|
|
1273 out), &body);
|
|
1274 if (TREE_CODE (*saved_tree) == BIND_EXPR)
|
|
1275 BIND_EXPR_BODY (*saved_tree) = body;
|
|
1276 else
|
|
1277 *saved_tree = body;
|
|
1278 }
|
|
1279 return decl;
|
|
1280 }
|
|
1281
|
|
1282 /* Wraps CALL, a CALL_EXPR, into a CILK_SPAWN_STMT tree and returns it. */
|
|
1283
|
|
1284 tree
|
|
1285 build_cilk_spawn (location_t loc, tree call)
|
|
1286 {
|
|
1287 if (!cilk_set_spawn_marker (loc, call))
|
|
1288 return error_mark_node;
|
|
1289 tree spawn_stmt = build1 (CILK_SPAWN_STMT, TREE_TYPE (call), call);
|
|
1290 TREE_SIDE_EFFECTS (spawn_stmt) = 1;
|
|
1291 return spawn_stmt;
|
|
1292 }
|
|
1293
|
|
1294 /* Returns a tree of type CILK_SYNC_STMT. */
|
|
1295
|
|
1296 tree
|
|
1297 build_cilk_sync (void)
|
|
1298 {
|
|
1299 tree sync = build0 (CILK_SYNC_STMT, void_type_node);
|
|
1300 TREE_SIDE_EFFECTS (sync) = 1;
|
|
1301 return sync;
|
|
1302 }
|
|
1303
|
|
1304 /* Helper for contains_cilk_spawn_stmt, callback for walk_tree. Return
|
|
1305 non-null tree if TP contains CILK_SPAWN_STMT. */
|
|
1306
|
|
1307 static tree
|
|
1308 contains_cilk_spawn_stmt_walker (tree *tp, int *, void *)
|
|
1309 {
|
|
1310 if (TREE_CODE (*tp) == CILK_SPAWN_STMT)
|
|
1311 return *tp;
|
|
1312 else
|
|
1313 return NULL_TREE;
|
|
1314 }
|
|
1315
|
|
1316 /* Returns true if EXPR or any of its subtrees contain CILK_SPAWN_STMT
|
|
1317 node. */
|
|
1318
|
|
1319 bool
|
|
1320 contains_cilk_spawn_stmt (tree expr)
|
|
1321 {
|
|
1322 return walk_tree (&expr, contains_cilk_spawn_stmt_walker, NULL, NULL)
|
|
1323 != NULL_TREE;
|
|
1324 }
|
|
1325
|
|
1326 /* Return a error location for EXPR if LOC is not set. */
|
|
1327
|
|
1328 static location_t
|
|
1329 get_error_location (tree expr, location_t loc)
|
|
1330 {
|
|
1331 if (loc == UNKNOWN_LOCATION)
|
|
1332 {
|
|
1333 if (TREE_CODE (expr) == MODIFY_EXPR)
|
|
1334 expr = TREE_OPERAND (expr, 0);
|
|
1335 loc = EXPR_LOCATION (expr);
|
|
1336 }
|
|
1337 return loc;
|
|
1338 }
|
|
1339
|
|
1340 /* Check that no array notation or spawn statement is in EXPR.
|
|
1341 If not true generate an error at LOC for ARRAY_GMSGID or
|
|
1342 SPAWN_MSGID. */
|
|
1343
|
|
1344 bool
|
|
1345 check_no_cilk (tree expr, const char *array_msgid, const char *spawn_msgid,
|
|
1346 location_t loc)
|
|
1347 {
|
|
1348 if (!flag_cilkplus)
|
|
1349 return false;
|
|
1350 if (contains_array_notation_expr (expr))
|
|
1351 {
|
|
1352 loc = get_error_location (expr, loc);
|
|
1353 error_at (loc, array_msgid);
|
|
1354 return true;
|
|
1355 }
|
|
1356 if (walk_tree (&expr, contains_cilk_spawn_stmt_walker, NULL, NULL))
|
|
1357 {
|
|
1358 loc = get_error_location (expr, loc);
|
|
1359 error_at (loc, spawn_msgid);
|
|
1360 return true;
|
|
1361 }
|
|
1362 return false;
|
|
1363 }
|