tasks.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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 if (!strncmp (TREE_STRING_POINTER (target), "gordon",
  92. TREE_STRING_LENGTH (target)))
  93. where_int = STARPU_GORDON;
  94. else
  95. where_int = 0;
  96. return where_int;
  97. }
  98. /* Return the task implemented by TASK_IMPL. */
  99. tree
  100. task_implementation_task (const_tree task_impl)
  101. {
  102. tree impl_attr, args, task;
  103. gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
  104. impl_attr = lookup_attribute (task_implementation_attribute_name,
  105. DECL_ATTRIBUTES (task_impl));
  106. gcc_assert (impl_attr != NULL_TREE);
  107. args = TREE_VALUE (impl_attr);
  108. task = TREE_VALUE (TREE_CHAIN (args));
  109. if (task_implementation_p (task))
  110. /* TASK is an implicit CPU task implementation, so return its real
  111. task. */
  112. return task_implementation_task (task);
  113. return task;
  114. }
  115. /* Return the declaration of the `struct starpu_codelet' variable associated with
  116. TASK_DECL. */
  117. tree
  118. task_codelet_declaration (const_tree task_decl)
  119. {
  120. tree cl_attr;
  121. cl_attr = lookup_attribute (task_codelet_attribute_name,
  122. DECL_ATTRIBUTES (task_decl));
  123. gcc_assert (cl_attr != NULL_TREE);
  124. return TREE_VALUE (cl_attr);
  125. }
  126. /* Return the list of implementations of TASK_DECL. */
  127. tree
  128. task_implementation_list (const_tree task_decl)
  129. {
  130. tree attr;
  131. attr = lookup_attribute (task_implementation_list_attribute_name,
  132. DECL_ATTRIBUTES (task_decl));
  133. return TREE_VALUE (attr);
  134. }
  135. /* Return the list of pointer parameter types of TASK_DECL. */
  136. tree
  137. task_pointer_parameter_types (const_tree task_decl)
  138. {
  139. return filter (pointer_type_p, TYPE_ARG_TYPES (TREE_TYPE (task_decl)));
  140. }
  141. /* Return a bitwise-or of the supported targets of TASK_DECL. */
  142. int
  143. task_where (const_tree task_decl)
  144. {
  145. gcc_assert (task_p (task_decl));
  146. int where;
  147. const_tree impl;
  148. for (impl = task_implementation_list (task_decl), where = 0;
  149. impl != NULL_TREE;
  150. impl = TREE_CHAIN (impl))
  151. where |= task_implementation_where (TREE_VALUE (impl));
  152. return where;
  153. }
  154. /* Return the FUNCTION_DECL of the wrapper generated for TASK_IMPL. */
  155. tree
  156. task_implementation_wrapper (const_tree task_impl)
  157. {
  158. tree attr;
  159. gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
  160. attr = lookup_attribute (task_implementation_wrapper_attribute_name,
  161. DECL_ATTRIBUTES (task_impl));
  162. gcc_assert (attr != NULL_TREE);
  163. return TREE_VALUE (attr);
  164. }
  165. tree
  166. codelet_type (void)
  167. {
  168. /* XXX: Hack to allow the type declaration to be accessible at lower
  169. time. */
  170. static tree type_decl = NULL_TREE;
  171. if (type_decl == NULL_TREE)
  172. /* Lookup the `struct starpu_codelet' struct type. This should succeed since
  173. we push <starpu.h> early on. */
  174. type_decl = type_decl_for_struct_tag (codelet_struct_tag);
  175. return TREE_TYPE (type_decl);
  176. }
  177. /* Return the access mode for POINTER, a PARM_DECL of a task. */
  178. enum starpu_access_mode
  179. access_mode (const_tree type)
  180. {
  181. gcc_assert (POINTER_TYPE_P (type));
  182. /* If TYPE points to a const-qualified type, then mark the data as
  183. read-only; if is has the `output' attribute, then mark it as write-only;
  184. otherwise default to read-write. */
  185. return ((TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)
  186. ? STARPU_R
  187. : (output_type_p (type) ? STARPU_W : STARPU_RW));
  188. }
  189. /* Return true if TYPE is `output'-qualified. */
  190. bool
  191. output_type_p (const_tree type)
  192. {
  193. return (lookup_attribute (output_attribute_name,
  194. TYPE_ATTRIBUTES (type)) != NULL_TREE);
  195. }
  196. /* Code generation. */
  197. /* Turn FN into a task, and push its associated codelet declaration. */
  198. void
  199. taskify_function (tree fn)
  200. {
  201. gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
  202. /* Add a `task' attribute and an empty `task_implementation_list'
  203. attribute. */
  204. DECL_ATTRIBUTES (fn) =
  205. tree_cons (get_identifier (task_implementation_list_attribute_name),
  206. NULL_TREE,
  207. tree_cons (get_identifier (task_attribute_name), NULL_TREE,
  208. DECL_ATTRIBUTES (fn)));
  209. /* Push a declaration for the corresponding `struct starpu_codelet' object and
  210. add it as an attribute of FN. */
  211. tree cl = build_codelet_declaration (fn);
  212. DECL_ATTRIBUTES (fn) =
  213. tree_cons (get_identifier (task_codelet_attribute_name), cl,
  214. DECL_ATTRIBUTES (fn));
  215. pushdecl (cl);
  216. }
  217. /* Return a NODE_IDENTIFIER for the variable holding the `struct starpu_codelet'
  218. structure associated with TASK_DECL. */
  219. tree
  220. build_codelet_identifier (tree task_decl)
  221. {
  222. static const char suffix[] = ".codelet";
  223. tree id;
  224. char *cl_name;
  225. const char *task_name;
  226. id = DECL_NAME (task_decl);
  227. task_name = IDENTIFIER_POINTER (id);
  228. cl_name = (char *) alloca (IDENTIFIER_LENGTH (id) + strlen (suffix) + 1);
  229. memcpy (cl_name, task_name, IDENTIFIER_LENGTH (id));
  230. strcpy (&cl_name[IDENTIFIER_LENGTH (id)], suffix);
  231. return get_identifier (cl_name);
  232. }
  233. /* Return a VAR_DECL that declares a `struct starpu_codelet' structure for
  234. TASK_DECL. */
  235. tree
  236. build_codelet_declaration (tree task_decl)
  237. {
  238. tree name, cl_decl;
  239. name = build_codelet_identifier (task_decl);
  240. cl_decl = build_decl (DECL_SOURCE_LOCATION (task_decl),
  241. VAR_DECL, name,
  242. /* c_build_qualified_type (type, TYPE_QUAL_CONST) */
  243. codelet_type ());
  244. DECL_ARTIFICIAL (cl_decl) = true;
  245. TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
  246. TREE_STATIC (cl_decl) = false;
  247. TREE_USED (cl_decl) = true;
  248. DECL_EXTERNAL (cl_decl) = true;
  249. DECL_CONTEXT (cl_decl) = NULL_TREE;
  250. return cl_decl;
  251. }
  252. /* Return a `struct starpu_codelet' initializer for TASK_DECL. */
  253. tree
  254. build_codelet_initializer (tree task_decl)
  255. {
  256. tree fields;
  257. fields = TYPE_FIELDS (codelet_type ());
  258. gcc_assert (TREE_CODE (fields) == FIELD_DECL);
  259. local_define (tree, lookup_field, (const char *name))
  260. {
  261. tree fdecl, fname;
  262. fname = get_identifier (name);
  263. for (fdecl = fields;
  264. fdecl != NULL_TREE;
  265. fdecl = TREE_CHAIN (fdecl))
  266. {
  267. if (DECL_NAME (fdecl) == fname)
  268. return fdecl;
  269. }
  270. /* Field NAME wasn't found. */
  271. gcc_assert (false);
  272. };
  273. local_define (tree, field_initializer, (const char *name, tree value))
  274. {
  275. tree field, init;
  276. field = lookup_field (name);
  277. init = make_node (TREE_LIST);
  278. TREE_PURPOSE (init) = field;
  279. TREE_CHAIN (init) = NULL_TREE;
  280. if (TREE_CODE (TREE_TYPE (value)) != ARRAY_TYPE)
  281. TREE_VALUE (init) = fold_convert (TREE_TYPE (field), value);
  282. else
  283. TREE_VALUE (init) = value;
  284. return init;
  285. };
  286. local_define (tree, codelet_name, ())
  287. {
  288. const char *name = IDENTIFIER_POINTER (DECL_NAME (task_decl));
  289. return build_string_literal (strlen (name) + 1, name);
  290. };
  291. local_define (tree, where_init, (tree impls))
  292. {
  293. tree impl;
  294. int where_int = 0;
  295. for (impl = impls;
  296. impl != NULL_TREE;
  297. impl = TREE_CHAIN (impl))
  298. {
  299. tree impl_decl;
  300. impl_decl = TREE_VALUE (impl);
  301. gcc_assert (TREE_CODE (impl_decl) == FUNCTION_DECL);
  302. if (verbose_output_p)
  303. /* List the implementations of TASK_DECL. */
  304. inform (DECL_SOURCE_LOCATION (impl_decl),
  305. " %qE", DECL_NAME (impl_decl));
  306. where_int |= task_implementation_where (impl_decl);
  307. }
  308. return build_int_cstu (integer_type_node, where_int);
  309. };
  310. local_define (tree, implementation_pointers, (tree impls, int where))
  311. {
  312. size_t len;
  313. tree impl, pointers;
  314. for (impl = impls, pointers = NULL_TREE, len = 0;
  315. impl != NULL_TREE;
  316. impl = TREE_CHAIN (impl))
  317. {
  318. tree impl_decl;
  319. impl_decl = TREE_VALUE (impl);
  320. if (task_implementation_where (impl_decl) == where)
  321. {
  322. /* Return a pointer to the wrapper of IMPL_DECL. */
  323. tree addr = build_addr (task_implementation_wrapper (impl_decl),
  324. NULL_TREE);
  325. pointers = tree_cons (size_int (len), addr, pointers);
  326. len++;
  327. if (len > STARPU_MAXIMPLEMENTATIONS)
  328. error_at (DECL_SOURCE_LOCATION (impl_decl),
  329. "maximum number of per-target task implementations "
  330. "exceeded");
  331. }
  332. }
  333. /* POINTERS must be null-terminated. */
  334. pointers = tree_cons (size_int (len), build_zero_cst (ptr_type_node),
  335. pointers);
  336. len++;
  337. /* Return an array initializer. */
  338. tree index_type = build_index_type (size_int (list_length (pointers)));
  339. return build_constructor_from_list (build_array_type (ptr_type_node,
  340. index_type),
  341. nreverse (pointers));
  342. };
  343. local_define (tree, pointer_arg_count, (void))
  344. {
  345. size_t len;
  346. len = list_length (task_pointer_parameter_types (task_decl));
  347. return build_int_cstu (integer_type_node, len);
  348. };
  349. local_define (tree, access_mode_array, (void))
  350. {
  351. const_tree type;
  352. tree modes;
  353. size_t index;
  354. for (type = task_pointer_parameter_types (task_decl),
  355. modes = NULL_TREE, index = 0;
  356. type != NULL_TREE && index < STARPU_NMAXBUFS;
  357. type = TREE_CHAIN (type), index++)
  358. {
  359. tree value = build_int_cst (integer_type_node,
  360. access_mode (TREE_VALUE (type)));
  361. modes = tree_cons (size_int (index), value, modes);
  362. }
  363. tree index_type = build_index_type (size_int (list_length (modes)));
  364. return build_constructor_from_list (build_array_type (integer_type_node,
  365. index_type),
  366. nreverse (modes));
  367. };
  368. if (verbose_output_p)
  369. inform (DECL_SOURCE_LOCATION (task_decl),
  370. "implementations for task %qE:", DECL_NAME (task_decl));
  371. tree impls, inits;
  372. impls = task_implementation_list (task_decl);
  373. inits =
  374. chain_trees (field_initializer ("name", codelet_name ()),
  375. field_initializer ("where", where_init (impls)),
  376. field_initializer ("nbuffers", pointer_arg_count ()),
  377. field_initializer ("modes", access_mode_array ()),
  378. field_initializer ("cpu_funcs",
  379. implementation_pointers (impls,
  380. STARPU_CPU)),
  381. field_initializer ("opencl_funcs",
  382. implementation_pointers (impls,
  383. STARPU_OPENCL)),
  384. field_initializer ("cuda_funcs",
  385. implementation_pointers (impls,
  386. STARPU_CUDA)),
  387. NULL_TREE);
  388. return build_constructor_from_unsorted_list (codelet_type (), inits);
  389. }
  390. /* Return the VAR_DECL that defines a `struct starpu_codelet' structure for
  391. TASK_DECL. The VAR_DECL is assumed to already exists, so it must not be
  392. pushed again. */
  393. tree
  394. declare_codelet (tree task_decl)
  395. {
  396. /* Retrieve the declaration of the `struct starpu_codelet' object. */
  397. tree cl_decl;
  398. cl_decl = lookup_name (build_codelet_identifier (task_decl));
  399. gcc_assert (cl_decl != NULL_TREE && TREE_CODE (cl_decl) == VAR_DECL);
  400. /* Turn the codelet declaration into a definition. */
  401. TREE_TYPE (cl_decl) = codelet_type ();
  402. TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
  403. return cl_decl;
  404. }
  405. /* Build the body of TASK_DECL, which will call `starpu_insert_task'. */
  406. void
  407. define_task (tree task_decl)
  408. {
  409. /* First of all, give TASK_DECL an argument list. */
  410. DECL_ARGUMENTS (task_decl) = build_function_arguments (task_decl);
  411. VEC(tree, gc) *args = NULL;
  412. location_t loc = DECL_SOURCE_LOCATION (task_decl);
  413. tree p, params = DECL_ARGUMENTS (task_decl);
  414. /* The first argument will be a pointer to the codelet. */
  415. VEC_safe_push (tree, gc, args,
  416. build_addr (task_codelet_declaration (task_decl),
  417. current_function_decl));
  418. for (p = params; p != NULL_TREE; p = TREE_CHAIN (p))
  419. {
  420. gcc_assert (TREE_CODE (p) == PARM_DECL);
  421. tree type = TREE_TYPE (p);
  422. if (POINTER_TYPE_P (type))
  423. {
  424. /* A pointer: the arguments will be:
  425. `STARPU_RW, ptr' or similar. */
  426. VEC_safe_push (tree, gc, args,
  427. build_int_cst (integer_type_node,
  428. access_mode (type)));
  429. VEC_safe_push (tree, gc, args, build_pointer_lookup (p));
  430. }
  431. else
  432. {
  433. /* A scalar: the arguments will be:
  434. `STARPU_VALUE, &scalar, sizeof (scalar)'. */
  435. mark_addressable (p);
  436. VEC_safe_push (tree, gc, args,
  437. build_int_cst (integer_type_node, STARPU_VALUE));
  438. VEC_safe_push (tree, gc, args,
  439. build_addr (p, current_function_decl));
  440. VEC_safe_push (tree, gc, args,
  441. size_in_bytes (type));
  442. }
  443. }
  444. /* Push the terminating zero. */
  445. VEC_safe_push (tree, gc, args,
  446. build_int_cst (integer_type_node, 0));
  447. /* Introduce a local variable to hold the error code. */
  448. tree error_var = build_decl (loc, VAR_DECL,
  449. create_tmp_var_name (".insert_task_error"),
  450. integer_type_node);
  451. DECL_CONTEXT (error_var) = task_decl;
  452. DECL_ARTIFICIAL (error_var) = true;
  453. /* Build this:
  454. err = starpu_insert_task (...);
  455. if (err != 0)
  456. { printf ...; abort (); }
  457. */
  458. static tree insert_task_fn;
  459. LOOKUP_STARPU_FUNCTION (insert_task_fn, "starpu_insert_task");
  460. tree call = build_call_expr_loc_vec (loc, insert_task_fn, args);
  461. tree assignment = build2 (INIT_EXPR, TREE_TYPE (error_var),
  462. error_var, call);
  463. tree name = DECL_NAME (task_decl);
  464. tree cond = build3 (COND_EXPR, void_type_node,
  465. build2 (NE_EXPR, boolean_type_node,
  466. error_var, integer_zero_node),
  467. build_error_statements (loc, error_var,
  468. build_starpu_error_string,
  469. "failed to insert task `%s'",
  470. IDENTIFIER_POINTER (name)),
  471. NULL_TREE);
  472. tree stmts = NULL;
  473. append_to_statement_list (assignment, &stmts);
  474. append_to_statement_list (cond, &stmts);
  475. tree bind = build3 (BIND_EXPR, void_type_node, error_var, stmts,
  476. NULL_TREE);
  477. /* Put it all together. */
  478. DECL_SAVED_TREE (task_decl) = bind;
  479. TREE_STATIC (task_decl) = true;
  480. DECL_EXTERNAL (task_decl) = false;
  481. DECL_ARTIFICIAL (task_decl) = true;
  482. DECL_INITIAL (task_decl) =
  483. build_block (error_var, NULL_TREE, task_decl, NULL_TREE);
  484. DECL_RESULT (task_decl) =
  485. build_decl (loc, RESULT_DECL, NULL_TREE, void_type_node);
  486. DECL_CONTEXT (DECL_RESULT (task_decl)) = task_decl;
  487. }
  488. /* Add FN to the list of implementations of TASK_DECL. */
  489. void
  490. add_task_implementation (tree task_decl, tree fn, const_tree where)
  491. {
  492. location_t loc;
  493. tree attr, impls;
  494. attr = lookup_attribute (task_implementation_list_attribute_name,
  495. DECL_ATTRIBUTES (task_decl));
  496. gcc_assert (attr != NULL_TREE);
  497. gcc_assert (TREE_CODE (where) == STRING_CST);
  498. loc = DECL_SOURCE_LOCATION (fn);
  499. impls = tree_cons (NULL_TREE, fn, TREE_VALUE (attr));
  500. TREE_VALUE (attr) = impls;
  501. TREE_USED (fn) = true;
  502. /* Check the `where' argument to raise a warning if needed. */
  503. if (task_implementation_target_to_int (where) == 0)
  504. warning_at (loc, 0,
  505. "unsupported target %E; task implementation won't be used",
  506. where);
  507. else if (task_implementation_target_to_int (where) == STARPU_OPENCL)
  508. {
  509. local_define (void, validate, (tree t))
  510. {
  511. validate_opencl_argument_type (loc, t);
  512. };
  513. for_each (validate, TYPE_ARG_TYPES (TREE_TYPE (fn)));
  514. }
  515. }