tasks.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /* GCC-StarPU
  2. Copyright (C) 2012 Inria
  3. GCC-StarPU is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. GCC-StarPU is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with GCC-StarPU. If not, see <http://www.gnu.org/licenses/>. */
  13. #include <starpu-gcc/config.h>
  14. /* We must include starpu.h here, otherwise gcc will complain about a poisoned
  15. malloc in xmmintrin.h. */
  16. #include <starpu.h>
  17. #include <gcc-plugin.h>
  18. #include <plugin-version.h>
  19. #include <plugin.h>
  20. #include <cpplib.h>
  21. #include <tree.h>
  22. #include <tree-iterator.h>
  23. #include <gimple.h>
  24. #ifdef HAVE_C_FAMILY_C_COMMON_H
  25. # include <c-family/c-common.h>
  26. #elif HAVE_C_COMMON_H
  27. # include <c-common.h>
  28. #endif
  29. #include <diagnostic.h>
  30. #include <starpu-gcc/tasks.h>
  31. #include <starpu-gcc/utils.h>
  32. #include <starpu-gcc/opencl.h>
  33. /* Task-related functions. */
  34. /* Name of public attributes. */
  35. const char task_attribute_name[] = "task";
  36. const char task_implementation_attribute_name[] = "task_implementation";
  37. const char output_attribute_name[] = "output";
  38. /* Names of attributes used internally. */
  39. static const char task_codelet_attribute_name[] = ".codelet";
  40. const char task_implementation_list_attribute_name[] =
  41. ".task_implementation_list";
  42. const char task_implementation_wrapper_attribute_name[] =
  43. ".task_implementation_wrapper";
  44. /* Names of data structures defined in <starpu.h>. */
  45. static const char codelet_struct_tag[] = "starpu_codelet";
  46. /* Return true if DECL is a task. */
  47. bool
  48. task_p (const_tree decl)
  49. {
  50. return (TREE_CODE (decl) == FUNCTION_DECL &&
  51. lookup_attribute (task_attribute_name,
  52. DECL_ATTRIBUTES (decl)) != NULL_TREE);
  53. }
  54. /* Return true if DECL is a task implementation. */
  55. bool
  56. task_implementation_p (const_tree decl)
  57. {
  58. return (TREE_CODE (decl) == FUNCTION_DECL &&
  59. lookup_attribute (task_implementation_attribute_name,
  60. DECL_ATTRIBUTES (decl)) != NULL_TREE);
  61. }
  62. /* Return a value indicating where TASK_IMPL should execute (`STARPU_CPU',
  63. `STARPU_CUDA', etc.). */
  64. int
  65. task_implementation_where (const_tree task_impl)
  66. {
  67. tree impl_attr, args, where;
  68. gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
  69. impl_attr = lookup_attribute (task_implementation_attribute_name,
  70. DECL_ATTRIBUTES (task_impl));
  71. gcc_assert (impl_attr != NULL_TREE);
  72. args = TREE_VALUE (impl_attr);
  73. where = TREE_VALUE (args);
  74. return task_implementation_target_to_int (where);
  75. }
  76. /* Return the StarPU integer constant corresponding to string TARGET. */
  77. int
  78. task_implementation_target_to_int (const_tree target)
  79. {
  80. gcc_assert (TREE_CODE (target) == STRING_CST);
  81. int where_int;
  82. if (!strncmp (TREE_STRING_POINTER (target), "cpu",
  83. TREE_STRING_LENGTH (target)))
  84. where_int = STARPU_CPU;
  85. else if (!strncmp (TREE_STRING_POINTER (target), "opencl",
  86. TREE_STRING_LENGTH (target)))
  87. where_int = STARPU_OPENCL;
  88. else if (!strncmp (TREE_STRING_POINTER (target), "cuda",
  89. TREE_STRING_LENGTH (target)))
  90. where_int = STARPU_CUDA;
  91. else
  92. where_int = 0;
  93. return where_int;
  94. }
  95. /* Return the task implemented by TASK_IMPL. */
  96. tree
  97. task_implementation_task (const_tree task_impl)
  98. {
  99. tree impl_attr, args, task;
  100. gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
  101. impl_attr = lookup_attribute (task_implementation_attribute_name,
  102. DECL_ATTRIBUTES (task_impl));
  103. gcc_assert (impl_attr != NULL_TREE);
  104. args = TREE_VALUE (impl_attr);
  105. task = TREE_VALUE (TREE_CHAIN (args));
  106. if (task_implementation_p (task))
  107. /* TASK is an implicit CPU task implementation, so return its real
  108. task. */
  109. return task_implementation_task (task);
  110. return task;
  111. }
  112. /* Return the declaration of the `struct starpu_codelet' variable associated with
  113. TASK_DECL. */
  114. tree
  115. task_codelet_declaration (const_tree task_decl)
  116. {
  117. tree cl_attr;
  118. cl_attr = lookup_attribute (task_codelet_attribute_name,
  119. DECL_ATTRIBUTES (task_decl));
  120. gcc_assert (cl_attr != NULL_TREE);
  121. return TREE_VALUE (cl_attr);
  122. }
  123. /* Return the list of implementations of TASK_DECL. */
  124. tree
  125. task_implementation_list (const_tree task_decl)
  126. {
  127. tree attr;
  128. attr = lookup_attribute (task_implementation_list_attribute_name,
  129. DECL_ATTRIBUTES (task_decl));
  130. return TREE_VALUE (attr);
  131. }
  132. /* Return the list of pointer parameter types of TASK_DECL. */
  133. tree
  134. task_pointer_parameter_types (const_tree task_decl)
  135. {
  136. return filter (pointer_type_p, TYPE_ARG_TYPES (TREE_TYPE (task_decl)));
  137. }
  138. /* Return a bitwise-or of the supported targets of TASK_DECL. */
  139. int
  140. task_where (const_tree task_decl)
  141. {
  142. gcc_assert (task_p (task_decl));
  143. int where;
  144. const_tree impl;
  145. for (impl = task_implementation_list (task_decl), where = 0;
  146. impl != NULL_TREE;
  147. impl = TREE_CHAIN (impl))
  148. where |= task_implementation_where (TREE_VALUE (impl));
  149. return where;
  150. }
  151. /* Return the FUNCTION_DECL of the wrapper generated for TASK_IMPL. */
  152. tree
  153. task_implementation_wrapper (const_tree task_impl)
  154. {
  155. tree attr;
  156. gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
  157. attr = lookup_attribute (task_implementation_wrapper_attribute_name,
  158. DECL_ATTRIBUTES (task_impl));
  159. gcc_assert (attr != NULL_TREE);
  160. return TREE_VALUE (attr);
  161. }
  162. tree
  163. codelet_type (void)
  164. {
  165. /* XXX: Hack to allow the type declaration to be accessible at lower
  166. time. */
  167. static tree type_decl = NULL_TREE;
  168. if (type_decl == NULL_TREE)
  169. /* Lookup the `struct starpu_codelet' struct type. This should succeed since
  170. we push <starpu.h> early on. */
  171. type_decl = type_decl_for_struct_tag (codelet_struct_tag);
  172. return TREE_TYPE (type_decl);
  173. }
  174. /* Return the access mode for POINTER, a PARM_DECL of a task. */
  175. enum starpu_data_access_mode
  176. access_mode (const_tree type)
  177. {
  178. gcc_assert (POINTER_TYPE_P (type));
  179. /* If TYPE points to a const-qualified type, then mark the data as
  180. read-only; if is has the `output' attribute, then mark it as write-only;
  181. otherwise default to read-write. */
  182. return ((TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)
  183. ? STARPU_R
  184. : (output_type_p (type) ? STARPU_W : STARPU_RW));
  185. }
  186. /* Return true if TYPE is `output'-qualified. */
  187. bool
  188. output_type_p (const_tree type)
  189. {
  190. return (lookup_attribute (output_attribute_name,
  191. TYPE_ATTRIBUTES (type)) != NULL_TREE);
  192. }
  193. /* Code generation. */
  194. /* Turn FN into a task, and push its associated codelet declaration. */
  195. void
  196. taskify_function (tree fn)
  197. {
  198. gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
  199. /* Add a `task' attribute and an empty `task_implementation_list'
  200. attribute. */
  201. DECL_ATTRIBUTES (fn) =
  202. tree_cons (get_identifier (task_implementation_list_attribute_name),
  203. NULL_TREE,
  204. tree_cons (get_identifier (task_attribute_name), NULL_TREE,
  205. DECL_ATTRIBUTES (fn)));
  206. /* Push a declaration for the corresponding `struct starpu_codelet' object and
  207. add it as an attribute of FN. */
  208. tree cl = build_codelet_declaration (fn);
  209. DECL_ATTRIBUTES (fn) =
  210. tree_cons (get_identifier (task_codelet_attribute_name), cl,
  211. DECL_ATTRIBUTES (fn));
  212. pushdecl (cl);
  213. }
  214. /* Return a NODE_IDENTIFIER for the variable holding the `struct starpu_codelet'
  215. structure associated with TASK_DECL. */
  216. tree
  217. build_codelet_identifier (tree task_decl)
  218. {
  219. static const char suffix[] = ".codelet";
  220. tree id;
  221. char *cl_name;
  222. const char *task_name;
  223. id = DECL_NAME (task_decl);
  224. task_name = IDENTIFIER_POINTER (id);
  225. cl_name = (char *) alloca (IDENTIFIER_LENGTH (id) + strlen (suffix) + 1);
  226. memcpy (cl_name, task_name, IDENTIFIER_LENGTH (id));
  227. strcpy (&cl_name[IDENTIFIER_LENGTH (id)], suffix);
  228. return get_identifier (cl_name);
  229. }
  230. /* Return a VAR_DECL that declares a `struct starpu_codelet' structure for
  231. TASK_DECL. */
  232. tree
  233. build_codelet_declaration (tree task_decl)
  234. {
  235. tree name, cl_decl;
  236. name = build_codelet_identifier (task_decl);
  237. cl_decl = build_decl (DECL_SOURCE_LOCATION (task_decl),
  238. VAR_DECL, name,
  239. /* c_build_qualified_type (type, TYPE_QUAL_CONST) */
  240. codelet_type ());
  241. DECL_ARTIFICIAL (cl_decl) = true;
  242. TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
  243. TREE_STATIC (cl_decl) = false;
  244. TREE_USED (cl_decl) = true;
  245. DECL_EXTERNAL (cl_decl) = true;
  246. DECL_CONTEXT (cl_decl) = NULL_TREE;
  247. return cl_decl;
  248. }
  249. /* Return a `struct starpu_codelet' initializer for TASK_DECL. */
  250. tree
  251. build_codelet_initializer (tree task_decl)
  252. {
  253. tree fields;
  254. fields = TYPE_FIELDS (codelet_type ());
  255. gcc_assert (TREE_CODE (fields) == FIELD_DECL);
  256. local_define (tree, lookup_field, (const char *name))
  257. {
  258. tree fdecl, fname;
  259. fname = get_identifier (name);
  260. for (fdecl = fields;
  261. fdecl != NULL_TREE;
  262. fdecl = TREE_CHAIN (fdecl))
  263. {
  264. if (DECL_NAME (fdecl) == fname)
  265. return fdecl;
  266. }
  267. /* Field NAME wasn't found. */
  268. gcc_assert (false);
  269. };
  270. local_define (tree, field_initializer, (const char *name, tree value))
  271. {
  272. tree field, init;
  273. field = lookup_field (name);
  274. init = make_node (TREE_LIST);
  275. TREE_PURPOSE (init) = field;
  276. TREE_CHAIN (init) = NULL_TREE;
  277. if (TREE_CODE (TREE_TYPE (value)) != ARRAY_TYPE)
  278. TREE_VALUE (init) = fold_convert (TREE_TYPE (field), value);
  279. else
  280. TREE_VALUE (init) = value;
  281. return init;
  282. };
  283. local_define (tree, codelet_name, ())
  284. {
  285. const char *name = IDENTIFIER_POINTER (DECL_NAME (task_decl));
  286. return build_string_literal (strlen (name) + 1, name);
  287. };
  288. local_define (tree, where_init, (tree impls))
  289. {
  290. tree impl;
  291. int where_int = 0;
  292. for (impl = impls;
  293. impl != NULL_TREE;
  294. impl = TREE_CHAIN (impl))
  295. {
  296. tree impl_decl;
  297. impl_decl = TREE_VALUE (impl);
  298. gcc_assert (TREE_CODE (impl_decl) == FUNCTION_DECL);
  299. if (verbose_output_p)
  300. /* List the implementations of TASK_DECL. */
  301. inform (DECL_SOURCE_LOCATION (impl_decl),
  302. " %qE", DECL_NAME (impl_decl));
  303. where_int |= task_implementation_where (impl_decl);
  304. }
  305. return build_int_cstu (integer_type_node, where_int);
  306. };
  307. local_define (tree, implementation_pointers, (tree impls, int where))
  308. {
  309. size_t len;
  310. tree impl, pointers;
  311. for (impl = impls, pointers = NULL_TREE, len = 0;
  312. impl != NULL_TREE;
  313. impl = TREE_CHAIN (impl))
  314. {
  315. tree impl_decl;
  316. impl_decl = TREE_VALUE (impl);
  317. if (task_implementation_where (impl_decl) == where)
  318. {
  319. /* Return a pointer to the wrapper of IMPL_DECL. */
  320. tree addr = build_addr (task_implementation_wrapper (impl_decl),
  321. NULL_TREE);
  322. pointers = tree_cons (size_int (len), addr, pointers);
  323. len++;
  324. if (len > STARPU_MAXIMPLEMENTATIONS)
  325. error_at (DECL_SOURCE_LOCATION (impl_decl),
  326. "maximum number of per-target task implementations "
  327. "exceeded");
  328. }
  329. }
  330. /* POINTERS must be null-terminated. */
  331. pointers = tree_cons (size_int (len), build_zero_cst (ptr_type_node),
  332. pointers);
  333. len++;
  334. /* Return an array initializer. */
  335. tree index_type = build_index_type (size_int (list_length (pointers)));
  336. return build_constructor_from_list (build_array_type (ptr_type_node,
  337. index_type),
  338. nreverse (pointers));
  339. };
  340. local_define (tree, pointer_arg_count, (void))
  341. {
  342. size_t len;
  343. len = list_length (task_pointer_parameter_types (task_decl));
  344. return build_int_cstu (integer_type_node, len);
  345. };
  346. local_define (tree, access_mode_array, (void))
  347. {
  348. const_tree type;
  349. tree modes;
  350. size_t index;
  351. for (type = task_pointer_parameter_types (task_decl),
  352. modes = NULL_TREE, index = 0;
  353. type != NULL_TREE && index < STARPU_NMAXBUFS;
  354. type = TREE_CHAIN (type), index++)
  355. {
  356. tree value = build_int_cst (integer_type_node,
  357. access_mode (TREE_VALUE (type)));
  358. modes = tree_cons (size_int (index), value, modes);
  359. }
  360. tree index_type = build_index_type (size_int (list_length (modes)));
  361. return build_constructor_from_list (build_array_type (integer_type_node,
  362. index_type),
  363. nreverse (modes));
  364. };
  365. if (verbose_output_p)
  366. inform (DECL_SOURCE_LOCATION (task_decl),
  367. "implementations for task %qE:", DECL_NAME (task_decl));
  368. tree impls, inits;
  369. impls = task_implementation_list (task_decl);
  370. inits =
  371. chain_trees (field_initializer ("name", codelet_name ()),
  372. field_initializer ("where", where_init (impls)),
  373. field_initializer ("nbuffers", pointer_arg_count ()),
  374. field_initializer ("modes", access_mode_array ()),
  375. field_initializer ("cpu_funcs",
  376. implementation_pointers (impls,
  377. STARPU_CPU)),
  378. field_initializer ("opencl_funcs",
  379. implementation_pointers (impls,
  380. STARPU_OPENCL)),
  381. field_initializer ("cuda_funcs",
  382. implementation_pointers (impls,
  383. STARPU_CUDA)),
  384. NULL_TREE);
  385. return build_constructor_from_unsorted_list (codelet_type (), inits);
  386. }
  387. /* Return the VAR_DECL that defines a `struct starpu_codelet' structure for
  388. TASK_DECL. The VAR_DECL is assumed to already exists, so it must not be
  389. pushed again. */
  390. tree
  391. declare_codelet (tree task_decl)
  392. {
  393. /* Retrieve the declaration of the `struct starpu_codelet' object. */
  394. tree cl_decl;
  395. cl_decl = lookup_name (build_codelet_identifier (task_decl));
  396. gcc_assert (cl_decl != NULL_TREE && TREE_CODE (cl_decl) == VAR_DECL);
  397. /* Turn the codelet declaration into a definition. */
  398. TREE_TYPE (cl_decl) = codelet_type ();
  399. TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
  400. return cl_decl;
  401. }
  402. /* Build the body of TASK_DECL, which will call `starpu_task_insert'. */
  403. void
  404. define_task (tree task_decl)
  405. {
  406. /* First of all, give TASK_DECL an argument list. */
  407. DECL_ARGUMENTS (task_decl) = build_function_arguments (task_decl);
  408. VEC(tree, gc) *args = NULL;
  409. location_t loc = DECL_SOURCE_LOCATION (task_decl);
  410. tree p, params = DECL_ARGUMENTS (task_decl);
  411. /* The first argument will be a pointer to the codelet. */
  412. VEC_safe_push (tree, gc, args,
  413. build_addr (task_codelet_declaration (task_decl),
  414. current_function_decl));
  415. for (p = params; p != NULL_TREE; p = TREE_CHAIN (p))
  416. {
  417. gcc_assert (TREE_CODE (p) == PARM_DECL);
  418. tree type = TREE_TYPE (p);
  419. if (POINTER_TYPE_P (type))
  420. {
  421. /* A pointer: the arguments will be:
  422. `STARPU_RW, ptr' or similar. */
  423. VEC_safe_push (tree, gc, args,
  424. build_int_cst (integer_type_node,
  425. access_mode (type)));
  426. VEC_safe_push (tree, gc, args, build_pointer_lookup (p));
  427. }
  428. else
  429. {
  430. /* A scalar: the arguments will be:
  431. `STARPU_VALUE, &scalar, sizeof (scalar)'. */
  432. mark_addressable (p);
  433. VEC_safe_push (tree, gc, args,
  434. build_int_cst (integer_type_node, STARPU_VALUE));
  435. VEC_safe_push (tree, gc, args,
  436. build_addr (p, current_function_decl));
  437. VEC_safe_push (tree, gc, args,
  438. size_in_bytes (type));
  439. }
  440. }
  441. /* Push the terminating zero. */
  442. VEC_safe_push (tree, gc, args,
  443. build_int_cst (integer_type_node, 0));
  444. /* Introduce a local variable to hold the error code. */
  445. tree error_var = build_decl (loc, VAR_DECL,
  446. create_tmp_var_name (".task_insert_error"),
  447. integer_type_node);
  448. DECL_CONTEXT (error_var) = task_decl;
  449. DECL_ARTIFICIAL (error_var) = true;
  450. /* Build this:
  451. err = starpu_task_insert (...);
  452. if (err != 0)
  453. { printf ...; abort (); }
  454. */
  455. static tree task_insert_fn;
  456. LOOKUP_STARPU_FUNCTION (task_insert_fn, "starpu_task_insert");
  457. tree call = build_call_expr_loc_vec (loc, task_insert_fn, args);
  458. tree assignment = build2 (INIT_EXPR, TREE_TYPE (error_var),
  459. error_var, call);
  460. tree name = DECL_NAME (task_decl);
  461. tree cond = build3 (COND_EXPR, void_type_node,
  462. build2 (NE_EXPR, boolean_type_node,
  463. error_var, integer_zero_node),
  464. build_error_statements (loc, error_var,
  465. build_starpu_error_string,
  466. "failed to insert task `%s'",
  467. IDENTIFIER_POINTER (name)),
  468. NULL_TREE);
  469. tree stmts = NULL;
  470. append_to_statement_list (assignment, &stmts);
  471. append_to_statement_list (cond, &stmts);
  472. tree bind = build3 (BIND_EXPR, void_type_node, error_var, stmts,
  473. NULL_TREE);
  474. /* Put it all together. */
  475. DECL_SAVED_TREE (task_decl) = bind;
  476. TREE_STATIC (task_decl) = true;
  477. DECL_EXTERNAL (task_decl) = false;
  478. DECL_ARTIFICIAL (task_decl) = true;
  479. DECL_INITIAL (task_decl) =
  480. build_block (error_var, NULL_TREE, task_decl, NULL_TREE);
  481. DECL_RESULT (task_decl) =
  482. build_decl (loc, RESULT_DECL, NULL_TREE, void_type_node);
  483. DECL_CONTEXT (DECL_RESULT (task_decl)) = task_decl;
  484. }
  485. /* Add FN to the list of implementations of TASK_DECL. */
  486. void
  487. add_task_implementation (tree task_decl, tree fn, const_tree where)
  488. {
  489. location_t loc;
  490. tree attr, impls;
  491. attr = lookup_attribute (task_implementation_list_attribute_name,
  492. DECL_ATTRIBUTES (task_decl));
  493. gcc_assert (attr != NULL_TREE);
  494. gcc_assert (TREE_CODE (where) == STRING_CST);
  495. loc = DECL_SOURCE_LOCATION (fn);
  496. impls = tree_cons (NULL_TREE, fn, TREE_VALUE (attr));
  497. TREE_VALUE (attr) = impls;
  498. TREE_USED (fn) = true;
  499. /* Check the `where' argument to raise a warning if needed. */
  500. if (task_implementation_target_to_int (where) == 0)
  501. warning_at (loc, 0,
  502. "unsupported target %E; task implementation won't be used",
  503. where);
  504. else if (task_implementation_target_to_int (where) == STARPU_OPENCL)
  505. {
  506. local_define (void, validate, (tree t))
  507. {
  508. validate_opencl_argument_type (loc, t);
  509. };
  510. for_each (validate, TYPE_ARG_TYPES (TREE_TYPE (fn)));
  511. }
  512. }