On 11/21/2013 05:40 PM, Iyer, Balaji V wrote:
+/* Returns a TRY_CATCH_EXPR that will encapsulate BODY, EXCEPT_DATA and
+   EXCEPT_FLAG.  */
+
+tree
+create_cilk_try_catch (tree except_flag, tree except_data, tree body)
+{
+  tree catch_list = alloc_stmt_list ();
+  append_to_statement_list (except_flag, &catch_list);
+  append_to_statement_list (except_data, &catch_list);
+  append_to_statement_list (do_begin_catch (), &catch_list);
+  append_to_statement_list (build_throw (NULL_TREE), &catch_list);
+  tree catch_tf_expr = build_stmt (EXPR_LOCATION (body), TRY_FINALLY_EXPR,
+                                  catch_list, do_end_catch (NULL_TREE));
+  catch_list = build2 (CATCH_EXPR, void_type_node, NULL_TREE,
+                      catch_tf_expr);
+  tree try_catch_expr = build_stmt (EXPR_LOCATION (body), TRY_CATCH_EXPR,
+                                   body, catch_list);
+  return try_catch_expr;
+}

I had in mind something less cilk-specific: a function that takes two tree operands, one for the body and one for the throwing path. Basically, make catch_list a parameter and move the first two appends back into the calling function.

The reason is that, when you have something like this:

_Cilk_spawn [=]  { <body> } ();

I need to capture the function call (which in this case is the whole function) 
and throw it into a nested function.  The nested function implementation is 
shared with C. If the function is stored in a variable then I can just send 
that out to the nested function. I have added another constraint to make sure 
the function is a spawning function, this way we can reduce more cases were 
they are stored to a variable. The reason why I added this check in 
finish_call_expr is that it seemed to be most straight-forward for me and only 
place where I could do with least disruption (code-changes).

It looks like you're transforming any

[...] {...} (...);

into

auto lambda = [...]{...};
lambda(...);

which has significantly different semantics, particularly in terms of the lifetime of the lambda object. In some of the Cilk online documentation, I see:

When spawning named lambda functions, be careful that the lifespan of the 
lambda extends at least until the next sync, or else the destructor for the 
lambda will race with the spawned call. For example:
double i = g();
if (some condition) {
   // named lambda with value capture of i
   auto f = [=i]() { double d = sin(i); f(d); };
   cilk_spawn f();
} // Ouch! destructor for f is in parallel with spawned call.

This would seem to apply even more to unnamed lambda functions, since normally they would be destroyed at the end of the full-expression, which must always be before the sync. Does the Intel compiler implicitly extend the lifetime of a lambda called by cilk spawn? In any case, we really don't want to do this for all calls to unnamed lambdas just because we turned on cilk mode.

Jason

Reply via email to