PIPS
atomic.c
Go to the documentation of this file.
1 /*
2 
3  $Id: atomic.c 23495 2018-10-24 09:19:47Z coelho $
4 
5  Copyright 1989-2016 MINES ParisTech
6 
7  This file is part of PIPS.
8 
9  PIPS 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 of the License, or
12  any later version.
13 
14  PIPS is distributed in the hope that it will be useful, but WITHOUT ANY
15  WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  FITNESS FOR A PARTICULAR PURPOSE.
17 
18  See the GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with PIPS. If not, see <http://www.gnu.org/licenses/>.
22 
23 */
24 #ifdef HAVE_CONFIG_H
25  #include "pips_config.h"
26 #endif
27 /*
28  * replace reductions with atomic op !
29  * debug driven by REDUCTIONS_DEBUG_LEVEL
30  *
31  * Mehdi Amini, April 2011.
32  */
33 
34 #include "local-header.h"
35 #include "prettyprint.h"
36 #include "properties.h"
37 #include "reductions.h"
38 #include "reduction.h"
39 #include "callgraph.h"
40 
41 GENERIC_LOCAL_FUNCTION(statement_reductions, pstatement_reductions)
42 
43 
44 /** Profiles for supported atomic operations
45  * FIXME should be newgen tag
46  */
49 
50 
51 /* Define an atomic operation
52  * FIXME : should be moved under newgen management
53  */
54 typedef struct atomic_operation {
55  enum atomic_op op;
56  enum atomic_type type;
58 
59 
60 /* Stucture that define the list of atomic_operations */
61 typedef struct atomic_profile {
65 
66 static atomic_operation cuda[] = {
67  { ADD, INT },
68  { ADD, LONG_INT },
69  { INC, INT },
70  { INC, LONG_INT },
71  { DEC, INT },
72  { DEC, LONG_INT },
73  { SUB, INT },
74  { SUB, LONG_INT },
75  { OR, INT },
76  { OR, LONG_INT },
77  { AND, INT },
78  { AND, LONG_INT },
79  { XOR, INT },
80  { XOR, LONG_INT },
81 };
82 
83 
85  struct atomic_profile profile = { NULL, 0 };
86  const char* s_profile = get_string_property("ATOMIC_OPERATION_PROFILE");
87  if(same_string_p(s_profile,"cuda")) {
88  profile.profile = cuda;
89  profile.profile_size = sizeof(cuda)/sizeof(atomic_operation);
90  } else {
91  pips_user_error("Unknown profile for atomic ops : '%s'\n",profile);
92  }
93  return profile;
94 }
95 
96 
97 typedef enum {
100  USE_OPENMP
102 /**
103  * Keep track of what is done in replace recursion
104  */
105 struct replace_ctx {
106  bool replace;
107  bool replaced;
111 };
112 
113 /**
114  * Get an atomic function that replace a reduction operator
115  */
117 
118  type t = type_undefined;
119  string type_suffix = "";
120  switch(op.type) {
121  case DOUBLE:
123  type_suffix = "Double";
124  break;
125  case FLOAT:
126  t = MakeRealResult();
127  type_suffix = "Float";
128  break;
129  case INT:
130  t = MakeIntegerResult();
131  type_suffix = "Int";
132  break;
133  case LONG_INT:
134  t = MakeLongIntegerResult();
135  type_suffix = "LongInt";
136  break;
137  default:
138  pips_internal_error("Unsupported type\n");
139  }
140 
141  string name = string_undefined;
142  switch(op.op) {
143  case ADD:
144  name = "Add";
145  break;
146  case SUB:
147  name = "Sub";
148  break;
149  case INC:
150  name = "Inc";
151  break;
152  case DEC:
153  name = "Dec";
154  break;
155  case AND:
156  name = "And";
157  break;
158  case OR:
159  name = "Or";
160  break;
161  case XOR:
162  name = "Xor";
163  break;
164  default:
165  pips_internal_error("Unsupported op tag\n");
166  }
167 
168  string prefix = "atomic"; // FIXME property
169 
170  name = strdup(concatenate(prefix,name,type_suffix,NULL));
171  entity atomic_func = make_empty_function(name, t,make_language_c());
172  free(name);
173  return atomic_func;
174 
175 }
176 
180  switch(reduction_operator_tag(ro)) {
181  case is_reduction_operator_sum: aop.op = ADD; break;
182  case is_reduction_operator_and: aop.op = AND; break;
183  case is_reduction_operator_or: aop.op = OR; break;
194  default: pips_user_warning("No atomic operation for this reduction operator !\n");
195  }
196 
197  //reference rr = reduction_reference(r);
198  // FIXME : get type of reference
199  aop.type = INT;
200 
201  return aop;
202 
203 }
204 
205 
206 
208  atomic_profile *profile) {
209  bool found_p = false;
210  for(int i=0; i<profile->profile_size; i++) {
211  // Loop over supported operation and look is we support requested op
212  if(profile->profile[i].op == op.op
213  && profile->profile[i].type == op.type ) {
214  found_p = true;
215  break;
216  }
217  }
218  return found_p;
219 }
220 
221 /* Replace a reduction in a statement by a pattern matching way */
223  reductions rs = (reductions)load_statement_reductions(s);
224  if(gen_in_list_p(s,*ctx->replaced_statements)) {
225  pips_debug(1,"Already handled statement !\n");
226  ctx->replaced = true;
227  } else {
228  if(rs && reductions_list(rs) && statement_call_p(s)) {
229  ifdebug(4) {
230  pips_debug(4,"Statement has a reduction ");
231  print_statement(s);
232  }
233  if(gen_length(reductions_list(rs)) > 1) {
234  pips_user_warning("Don't know how to handle multiple reductions on a "
235  "statement ! Abort...\n");
236  return false;
237  }
238 
241  if(!supported_atomic_operator_p(op, ctx->profile)) {
242  ifdebug(1) {
243  pips_debug(1,"Unsupported reduction by atomic operation profile : ");
244  print_reduction(r);
245  fprintf(stderr, "\n");
246  }
247  } else {
248  expression atomic_param =
250  if(expression_undefined_p(atomic_param)) {
251  ifdebug(1) {
252  pips_debug(1,"Didn't manage to get complement expression :(\n");
253  }
254  } else {
255  entity atomic_fun = atomic_function_of_operation(op);
256  pips_assert("have a valid function",!entity_undefined_p(atomic_fun));
257  if(ctx->replace) {
259  expression addr_of_ref =
262  list args = CONS(EXPRESSION,
263  addr_of_ref,
266  NULL));
267  statement_instruction(s) = make_call_instruction(atomic_fun, args);
268  ifdebug(4) {
269  pips_debug(4,"Atomized statement : ");
270  print_statement(s);
271  }
272  }
273  ctx->replaced = true;
275  }
276  }
277  }
278  }
279 
280  return true;
281 }
282 
283 
284 static bool process_reductions_in_loop(statement loop_stat,
285  atomic_profile *profile,
286  list *replaced_statements,
287  bool replace) {
288  bool ret = true;
289  if(statement_loop_p(loop_stat) && !loop_parallel_p(statement_loop(loop_stat))) {
290  pips_debug(1,"Loop is parallel only with reduction, fetching reductions...\n");
291  loop l = statement_loop(loop_stat);
292 // reduction_reference(r);
293  reductions rs = (reductions)load_statement_reductions(loop_stat);
294  if(rs && !ENDP(reductions_list(rs)) ) {
295  pips_debug(1,"Loop has a reduction ! Let's replace reductions inside\n");
296  struct replace_ctx ctx = { replace, false, false, profile, replaced_statements };
298  &ctx,
301  gen_null2);
302  if(!ctx.replaced) {
303  pips_debug(1,"No reduction replaced for this loop, make it sequential "
304  "to be safe :-(\n");
306  } else if(ctx.unsupported) {
307  pips_debug(1,"Unsupported reduction found ! Make it sequential "
308  "to be safe :-(\n");
310  } else {
312  ret = false;
313  }
314  }
315  }
316  return ret;
317 }
318 
320  pips_assert("in a loop", statement_loop_p(st));
321  loop l = statement_loop(st);
322  bool ret = false;
323  if (!(ret=omp_pragma_expr_for_reduction (l, st, false)))
324  ret=omp_pragma_expr_for (l, st);
325  if(ret)
327  return ret;
328 }
329 
330 
331 static bool process_reduced_loops(const char *mod_name, process_reductions_mode mode) {
332  atomic_profile current_profile = load_atomic_profile();
333  if(!current_profile.profile && current_profile.profile_size) {
334  return false;
335  }
336 
338  statement module_stat = (statement)db_get_memory_resource(DBR_CODE, mod_name, true);
339  set_current_module_statement(module_stat);
340 
341  // Load detected reductions
342  set_statement_reductions(
343  (pstatement_reductions)db_get_memory_resource(DBR_CUMULATED_REDUCTIONS,
344  mod_name,
345  true));
347  (pstatement_reductions)db_get_memory_resource(DBR_CUMULATED_REDUCTIONS,
348  mod_name,
349  true));
350 
351  // Load targeted loops, those that need reductions to be parallel !
352  reduced_loops loops = (reduced_loops)db_get_memory_resource(DBR_REDUCTION_PARALLEL_LOOPS,
353  mod_name,
354  true);
356  set_ordering_to_statement(module_stat);
357  FOREACH(int, ordering, reduced_loops_ordering(loops)) {
358  statement s = ordering_to_statement(ordering);
359  switch(mode) {
360  case DO_REPLACE:
361  process_reductions_in_loop(s,&current_profile,&replaced_statements, true);
362  break;
363  case DO_NOT_REPLACE:
364  process_reductions_in_loop(s,&current_profile,&replaced_statements, false);
365  break;
366  case USE_OPENMP:
368  break;
369  }
370  }
372 
373  if (!statement_consistent_p(module_stat)) {
374  pips_internal_error("Statement is inconsistent ! No choice but abort...\n");
375  }
376 
377 
378  DB_PUT_MEMORY_RESOURCE(DBR_CODE,
379  mod_name,
380  (char*) module_stat);
381 
382  // We may have outline some code, so recompute the callees:
383  DB_PUT_MEMORY_RESOURCE(DBR_CALLEES, mod_name,
385 
386  reset_statement_reductions();
390  return true;
391 }
392 
393 
394 /**
395  * Replace reduction with atomic operations
396  */
397 bool replace_reduction_with_atomic( string mod_name) {
398  debug_on("REPLACE_REDUCTION_WITH_ATOMIC_DEBUG_LEVEL");
399 
400  bool result = process_reduced_loops(mod_name, DO_REPLACE);
401 
402  debug_off();
403 
404  return result;
405 }
406 
407 /**
408  * Flag loop as parallel when replacement with atomic is possible without doing
409  * the replacement
410  */
412  debug_on("FLAG_PARALLEL_REDUCED_LOOPS_WITH_ATOMIC_DEBUG_LEVEL");
413 
414  bool result = process_reduced_loops(mod_name, DO_NOT_REPLACE);
415 
416  debug_off();
417 
418  return result;
419 
420 }
421 
422 /**
423  * Flag loop as parallel with OpenMP directives taking reductions into
424  * account
425  */
427  debug_on("FLAG_PARALLEL_REDUCED_LOOPS_WITH_ATOMIC_DEBUG_LEVEL");
428 
429  bool result = process_reduced_loops(mod_name, USE_OPENMP);
430 
431  debug_off();
432 
433  return result;
434 
435 }
bool statement_consistent_p(statement p)
Definition: ri.c:2195
language make_language_c(void)
Definition: ri.c:1253
reference copy_reference(reference p)
REFERENCE.
Definition: ri.c:2047
static reference ref
Current stmt (an integer)
Definition: adg_read_paf.c:163
static bool process_reduced_loops(const char *mod_name, process_reductions_mode mode)
Definition: atomic.c:331
static bool process_reductions_in_loop(statement loop_stat, atomic_profile *profile, list *replaced_statements, bool replace)
Definition: atomic.c:284
static atomic_profile load_atomic_profile()
Definition: atomic.c:84
atomic_type
Definition: atomic.c:48
@ DOUBLE
Definition: atomic.c:48
@ UNKNOWN_TYPE
Definition: atomic.c:48
@ LONG_INT
Definition: atomic.c:48
@ FLOAT
Definition: atomic.c:48
@ INT
Definition: atomic.c:48
struct atomic_operation atomic_operation
Define an atomic operation FIXME : should be moved under newgen management.
struct atomic_profile atomic_profile
Stucture that define the list of atomic_operations.
atomic_op
Profiles for supported atomic operations FIXME should be newgen tag.
Definition: atomic.c:47
@ UNKNOWN_OP
Definition: atomic.c:47
@ SUB
Definition: atomic.c:47
@ INC
Definition: atomic.c:47
@ DEC
Definition: atomic.c:47
@ AND
Definition: atomic.c:47
@ OR
Definition: atomic.c:47
@ XOR
Definition: atomic.c:47
@ ADD
Definition: atomic.c:47
static bool replace_reductions_in_statement(statement s, struct replace_ctx *ctx)
Replace a reduction in a statement by a pattern matching way.
Definition: atomic.c:222
static atomic_operation cuda[]
Definition: atomic.c:66
static atomic_operation reduction_to_atomic_operation(reduction r)
Definition: atomic.c:177
process_reductions_mode
Definition: atomic.c:97
@ USE_OPENMP
Definition: atomic.c:100
@ DO_REPLACE
Definition: atomic.c:98
@ DO_NOT_REPLACE
Definition: atomic.c:99
bool flag_parallel_reduced_loops_with_atomic(string mod_name)
Flag loop as parallel when replacement with atomic is possible without doing the replacement.
Definition: atomic.c:411
bool flag_parallel_reduced_loops_with_openmp_directives(const char *mod_name)
Flag loop as parallel with OpenMP directives taking reductions into account.
Definition: atomic.c:426
static bool process_reductions_in_openmp_loop(statement st)
Definition: atomic.c:319
static bool supported_atomic_operator_p(atomic_operation op, atomic_profile *profile)
Definition: atomic.c:207
bool replace_reduction_with_atomic(string mod_name)
Replace reduction with atomic operations.
Definition: atomic.c:397
static entity atomic_function_of_operation(atomic_operation op)
Get an atomic function that replace a reduction operator.
Definition: atomic.c:116
callees compute_callees(const statement stat)
Recompute the callees of a module statement.
Definition: callgraph.c:355
struct _newgen_struct_statement_ * statement
Definition: cloning.h:21
static list loops
#define ret(why, what)
true if not a remapping for old.
Definition: dynamic.c:986
char * get_string_property(const char *)
#define gen_context_recurse(start, ctxt, domain_number, flt, rwt)
Definition: genC.h:285
void free(void *)
void reset_current_module_entity(void)
Reset the current module entity.
Definition: static.c:97
void reset_current_module_statement(void)
Reset the current module statement.
Definition: static.c:221
statement set_current_module_statement(statement)
Set the current module statement.
Definition: static.c:165
statement get_current_module_statement(void)
Get the current module statement.
Definition: static.c:208
entity set_current_module_entity(entity)
static.c
Definition: static.c:66
void gen_null2(__attribute__((unused)) void *u1, __attribute__((unused)) void *u2)
idem with 2 args, to please overpeaky compiler checks
Definition: genClib.c:2758
instruction make_call_instruction(entity e, list l)
Build an instruction that call a function entity with an argument list.
Definition: instruction.c:51
bool loop_parallel_p(loop l)
Test if a loop is parallel.
Definition: loop.c:393
#define ENDP(l)
Test if a list is empty.
Definition: newgen_list.h:66
#define NIL
The empty list (nil in Lisp)
Definition: newgen_list.h:47
size_t gen_length(const list l)
Definition: list.c:150
#define CONS(_t_, _i_, _l_)
List element cell constructor (insert an element at the beginning of a list)
Definition: newgen_list.h:150
#define CAR(pcons)
Get the value of the first element of a list.
Definition: newgen_list.h:92
bool gen_in_list_p(const void *vo, const list lx)
tell whether vo belongs to lx
Definition: list.c:734
#define FOREACH(_fe_CASTER, _fe_item, _fe_list)
Apply/map an instruction block on all the elements of a list.
Definition: newgen_list.h:179
string db_get_memory_resource(const char *rname, const char *oname, bool pure)
Return the pointer to the resource, whatever it is.
Definition: database.c:755
#define DB_PUT_MEMORY_RESOURCE(res_name, own_name, res_val)
conform to old interface.
Definition: pipsdbm-local.h:66
loop statement_loop(statement)
Get the loop of a statement.
Definition: statement.c:1374
bool statement_call_p(statement)
Definition: statement.c:364
bool statement_loop_p(statement)
Definition: statement.c:349
#define debug_on(env)
Definition: misc-local.h:157
#define pips_debug
these macros use the GNU extensions that allow variadic macros, including with an empty list.
Definition: misc-local.h:145
#define pips_user_warning
Definition: misc-local.h:146
#define pips_assert(what, predicate)
common macros, two flavors depending on NDEBUG
Definition: misc-local.h:172
#define pips_internal_error
Definition: misc-local.h:149
#define debug_off()
Definition: misc-local.h:160
#define pips_user_error
Definition: misc-local.h:147
string concatenate(const char *,...)
Return the concatenation of the given strings.
Definition: string.c:183
#define same_string_p(s1, s2)
#define string_undefined
Definition: newgen_types.h:40
hash_table set_ordering_to_statement(statement s)
To be used instead of initialize_ordering_to_statement() to make sure that the hash table ots is in s...
Definition: ordering.c:172
statement ordering_to_statement(int o)
Get the statement associated to a given ordering.
Definition: ordering.c:111
void reset_ordering_to_statement(void)
Reset the mapping from ordering to statement.
Definition: ordering.c:185
void print_statement(statement)
Print a statement on stderr.
Definition: statement.c:98
struct _newgen_struct_reduced_loops_ * reduced_loops
Definition: reduction.h:45
#define reduced_loops_ordering(x)
Definition: reduction.h:167
bool omp_pragma_expr_for(loop l, statement stmt)
generate "pragma omp for" as a list of expressions
Definition: pragma.c:366
bool omp_pragma_expr_for_reduction(loop l, statement stmt, bool strict)
generate pragma for a reduction as a list of expressions
Definition: pragma.c:346
void print_reduction(reduction r)
Definition: prettyprint.c:137
void set_printed_reductions(pstatement_reductions)
void reset_printed_reductions(void)
expression get_complement_expression(statement, reference)
Return the "other part" of the reduction.
Definition: utils.c:743
struct _newgen_struct_reductions_ * reductions
@ is_reduction_operator_bitwise_xor
@ is_reduction_operator_none
@ is_reduction_operator_min
@ is_reduction_operator_bitwise_and
@ is_reduction_operator_neqv
@ is_reduction_operator_max
@ is_reduction_operator_bitwise_or
@ is_reduction_operator_csum
@ is_reduction_operator_eqv
@ is_reduction_operator_prod
@ is_reduction_operator_or
@ is_reduction_operator_and
@ is_reduction_operator_sum
#define reduction_operator_tag(x)
#define reduction_op(x)
#define REDUCTION_CAST(x)
#define reduction_reference(x)
#define reductions_list(x)
static const char * prefix
#define ADDRESS_OF_OPERATOR_NAME
entity local_name_to_top_level_entity(const char *n)
This function try to find a top-level entity from a local name.
Definition: entity.c:1450
entity entity_intrinsic(const char *name)
FI: I do not understand this function name (see next one!).
Definition: entity.c:1292
entity make_empty_function(const char *name, type r, language l)
Definition: entity.c:283
expression reference_to_expression(reference r)
Definition: expression.c:196
expression int_to_expression(_int i)
transform an int into an expression and generate the corresponding entity if necessary; it is not cle...
Definition: expression.c:1188
expression MakeUnaryCall(entity f, expression a)
Creates a call expression to a function with one argument.
Definition: expression.c:342
type MakeLongIntegerResult(void)
MB.
Definition: type.c:281
type MakeIntegerResult(void)
Definition: type.c:276
type MakeRealResult(void)
Definition: type.c:291
type MakeDoubleprecisionResult(void)
Definition: type.c:296
#define execution_tag(x)
Definition: ri.h:1207
#define loop_execution(x)
Definition: ri.h:1648
#define statement_domain
newgen_sizeofexpression_domain_defined
Definition: ri.h:362
#define EXPRESSION(x)
EXPRESSION.
Definition: ri.h:1217
#define entity_undefined_p(x)
Definition: ri.h:2762
#define expression_undefined_p(x)
Definition: ri.h:1224
#define statement_instruction(x)
Definition: ri.h:2458
#define type_undefined
Definition: ri.h:2883
@ is_execution_parallel
Definition: ri.h:1190
@ is_execution_sequential
Definition: ri.h:1189
int fprintf()
test sc_min : ce test s'appelle par : programme fichier1.data fichier2.data ...
char * strdup()
#define ifdebug(n)
Definition: sg.c:47
GENERIC_LOCAL_FUNCTION(directives, step_directives)
Copyright 2007, 2008, 2009 Alain Muller, Frederique Silber-Chaussumier.
Define an atomic operation FIXME : should be moved under newgen management.
Definition: atomic.c:54
enum atomic_op op
Definition: atomic.c:55
enum atomic_type type
Definition: atomic.c:56
Stucture that define the list of atomic_operations.
Definition: atomic.c:61
int profile_size
Definition: atomic.c:63
atomic_operation * profile
Definition: atomic.c:62
The structure used to build lists in NewGen.
Definition: newgen_list.h:41
Keep track of what is done in replace recursion.
Definition: atomic.c:105
atomic_profile * profile
Definition: atomic.c:109
bool replace
Definition: atomic.c:106
list * replaced_statements
Definition: atomic.c:110
bool unsupported
Definition: atomic.c:108
bool replaced
Definition: atomic.c:107