summaryrefslogtreecommitdiff
path: root/gcc/cp/cp-cilkplus.c
blob: 5ccf5d3385dff08bf118a44541811804cb58ec82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/* This file is part of the Intel(R) Cilk(TM) Plus support
   This file contains routines to handle Cilk Plus specific
   routines for the C++ Compiler.
   Copyright (C) 2013-2017 Free Software Foundation, Inc.
   Contributed by Aldy Hernandez <aldyh@redhat.com>.

   This file is part of GCC.

   GCC is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GCC is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GCC; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
#include "tree-iterator.h"
#include "cilk.h"
#include "c-family/c-common.h"

/* Return TRUE if T is a FUNCTION_DECL for a type-conversion operator.  */

static bool
is_conversion_operator_function_decl_p (tree t)
{
  if (TREE_CODE (t) != FUNCTION_DECL)
    return false;

  return DECL_CONV_FN_P (t);
}

/* Recursively traverse EXP to search for a CILK_SPAWN_STMT subtree.
   Return the CILK_SPAWN_STMT subtree if found; otherwise, the last subtree
   searched.  */

static tree
find_spawn (tree exp)
{
  /* Happens with C++ TARGET_EXPR.  */
  if (exp == NULL_TREE)
    return exp;

  if (cilk_ignorable_spawn_rhs_op (exp))
    return find_spawn (TREE_OPERAND (exp, 0));

  switch (TREE_CODE (exp))
    {
    case AGGR_INIT_EXPR:
      {
	/* Check for initialization via a constructor call that represents
	   implicit conversion.  */
	if (AGGR_INIT_VIA_CTOR_P (exp) && aggr_init_expr_nargs (exp) == 2)
	  return find_spawn (AGGR_INIT_EXPR_ARG (exp, 1));

	/* Check for initialization via a call to a type-conversion
	   operator.  */
	tree fn = AGGR_INIT_EXPR_FN (exp);
	if (TREE_CODE (fn) == ADDR_EXPR
	    && is_conversion_operator_function_decl_p (TREE_OPERAND (fn, 0))
	    && aggr_init_expr_nargs (exp) == 1)
	  return find_spawn (AGGR_INIT_EXPR_ARG (exp, 0));
      }
      break;

    case CALL_EXPR:
      {
	/* Check for a call to a type-conversion operator.  */
	tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
	if (is_conversion_operator_function_decl_p (fndecl)
	    && call_expr_nargs (exp) == 1)
	  return find_spawn (CALL_EXPR_ARG (exp, 0));
      }
      break;

    case TARGET_EXPR:
      return find_spawn (TARGET_EXPR_INITIAL (exp));

    case CLEANUP_POINT_EXPR:
    case COMPOUND_EXPR:
    case EXPR_STMT:
      return find_spawn (TREE_OPERAND (exp, 0));

    default:
      break;
    }

    return exp;
}

/* Return true if *EXP0 is a recognized form of spawn.  Recognized forms
   are, after conversion to void, a call expression at outer level or an
   assignment at outer level with the right hand side being a spawned call.
   In addition to this, it also unwraps the CILK_SPAWN_STMT cover from the
   CALL_EXPR that is being spawned.

   Note that `=' in C++ may turn into a CALL_EXPR rather than a
   MODIFY_EXPR.  */

bool
cilk_cp_detect_spawn_and_unwrap (tree *exp0)
{
  tree exp = *exp0;

  if (!TREE_SIDE_EFFECTS (exp))
    return false;

  /* Strip off any conversion to void.  It does not affect whether spawn
     is supported here.  */
  if (TREE_CODE (exp) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (exp)))
    exp = TREE_OPERAND (exp, 0);

  if (TREE_CODE (exp) == MODIFY_EXPR || TREE_CODE (exp) == INIT_EXPR)
    exp = TREE_OPERAND (exp, 1);

  exp = find_spawn (exp);
  if (exp == NULL_TREE)
    return false;

  /* Now we should have a CALL_EXPR with a CILK_SPAWN_STMT wrapper around
     it, or return false.  */
  return cilk_recognize_spawn (exp, exp0);
}

/* Callback for cp_walk_tree to validate the body of a pragma simd loop
   or _cilk_for loop.

   This function is passed in as a function pointer to walk_tree.  *TP is
   the current tree pointer, *WALK_SUBTREES is set to 0 by this function if
   recursing into TP's subtrees is unnecessary. *DATA is a bool variable that
   is set to false if an error has occurred.  */

static tree
cpp_validate_cilk_plus_loop_aux (tree *tp, int *walk_subtrees, void *data)
{
  bool *valid = (bool *) data;

  if (!tp || !*tp)
    return NULL_TREE;

  location_t loc = EXPR_LOCATION (*tp);
  if (TREE_CODE (*tp) == THROW_EXPR)
    {
      error_at (loc, "throw expressions are not allowed inside loops "
		"marked with pragma simd");
      *walk_subtrees = 0;
      *valid = false;
    }
  else if (TREE_CODE (*tp) == TRY_BLOCK)
    {
      error_at (loc, "try statements are not allowed inside loops marked "
		"with #pragma simd");
      *valid = false;
      *walk_subtrees = 0;
    }
  return NULL_TREE;
}  


/* Walks through all the subtrees of BODY using walk_tree to make sure
   invalid statements/expressions are not found inside BODY.  Returns
   false if any invalid statements are found.  */

bool
cpp_validate_cilk_plus_loop (tree body)
{
  bool valid = true;
  cp_walk_tree (&body, cpp_validate_cilk_plus_loop_aux,
		(void *) &valid, NULL);
  return valid;
}

/* Sets the EXCEPTION bit (0x10) in the FRAME.flags field.  */

static tree
set_cilk_except_flag (tree frame)
{
  tree flags = cilk_dot (frame, CILK_TI_FRAME_FLAGS, 0);

  flags = build2 (MODIFY_EXPR, void_type_node, flags,
		  build2 (BIT_IOR_EXPR, TREE_TYPE (flags), flags,
			  build_int_cst (TREE_TYPE (flags),
					 CILK_FRAME_EXCEPTING)));
  return flags;
}

/* Sets the frame.EXCEPT_DATA field to the head of the exception pointer.  */

static tree
set_cilk_except_data (tree frame)
{
  tree except_data = cilk_dot (frame, CILK_TI_FRAME_EXCEPTION, 0);
  tree uresume_fn = builtin_decl_implicit (BUILT_IN_EH_POINTER);
  tree ret_expr;
  uresume_fn  = build_call_expr (uresume_fn, 1,
				 build_int_cst (integer_type_node, 0));
  ret_expr = build2 (MODIFY_EXPR, void_type_node, except_data, uresume_fn);
  return ret_expr;
}

/* Installs BODY into function FNDECL with appropriate exception handling
   code.  WD holds information of wrapper function used to pass into the
   outlining function, cilk_outline.  */

void
cilk_install_body_with_frame_cleanup (tree fndecl, tree orig_body, void *wd)
{
  tree frame = make_cilk_frame (fndecl);
  tree dtor = create_cilk_function_exit (frame, false, false);
  add_local_decl (cfun, frame);

  cfun->language = ggc_cleared_alloc<language_function> ();
  
  location_t loc = EXPR_LOCATION (orig_body);
  tree list = alloc_stmt_list ();
  DECL_SAVED_TREE (fndecl) = list;
  tree body = alloc_stmt_list ();
  cilk_outline (fndecl, &orig_body, (struct wrapper_data *) wd);
  append_to_statement_list (orig_body, &body);
  if (flag_exceptions)
    {
      tree except_flag = set_cilk_except_flag (frame);
      tree except_data = set_cilk_except_data (frame);
      tree catch_list = alloc_stmt_list ();
      append_to_statement_list (except_flag, &catch_list);
      append_to_statement_list (except_data, &catch_list);
      body = create_try_catch_expr (body, catch_list);
    }
  append_to_statement_list (build_stmt (loc, TRY_FINALLY_EXPR, body, dtor),
			    &list);
}