1 //===--- OpenMPKinds.cpp - Token Kinds Support ----------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// \file 10 /// \brief This file implements the OpenMP enum and support functions. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Basic/OpenMPKinds.h" 15 #include "clang/Basic/IdentifierTable.h" 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/ADT/StringSwitch.h" 18 #include "llvm/Support/ErrorHandling.h" 19 #include <cassert> 20 21 using namespace clang; 22 23 OpenMPDirectiveKind clang::getOpenMPDirectiveKind(StringRef Str) { 24 return llvm::StringSwitch<OpenMPDirectiveKind>(Str) 25 #define OPENMP_DIRECTIVE(Name) .Case(#Name, OMPD_##Name) 26 #define OPENMP_DIRECTIVE_EXT(Name, Str) .Case(Str, OMPD_##Name) 27 #include "clang/Basic/OpenMPKinds.def" 28 .Default(OMPD_unknown); 29 } 30 31 const char *clang::getOpenMPDirectiveName(OpenMPDirectiveKind Kind) { 32 assert(Kind <= OMPD_unknown); 33 switch (Kind) { 34 case OMPD_unknown: 35 return "unknown"; 36 #define OPENMP_DIRECTIVE(Name) \ 37 case OMPD_##Name: \ 38 return #Name; 39 #define OPENMP_DIRECTIVE_EXT(Name, Str) \ 40 case OMPD_##Name: \ 41 return Str; 42 #include "clang/Basic/OpenMPKinds.def" 43 break; 44 } 45 llvm_unreachable("Invalid OpenMP directive kind"); 46 } 47 48 OpenMPClauseKind clang::getOpenMPClauseKind(StringRef Str) { 49 // 'flush' clause cannot be specified explicitly, because this is an implicit 50 // clause for 'flush' directive. If the 'flush' clause is explicitly specified 51 // the Parser should generate a warning about extra tokens at the end of the 52 // directive. 53 if (Str == "flush") 54 return OMPC_unknown; 55 return llvm::StringSwitch<OpenMPClauseKind>(Str) 56 #define OPENMP_CLAUSE(Name, Class) .Case(#Name, OMPC_##Name) 57 #include "clang/Basic/OpenMPKinds.def" 58 .Case("uniform", OMPC_uniform) 59 .Default(OMPC_unknown); 60 } 61 62 const char *clang::getOpenMPClauseName(OpenMPClauseKind Kind) { 63 assert(Kind <= OMPC_unknown); 64 switch (Kind) { 65 case OMPC_unknown: 66 return "unknown"; 67 #define OPENMP_CLAUSE(Name, Class) \ 68 case OMPC_##Name: \ 69 return #Name; 70 #include "clang/Basic/OpenMPKinds.def" 71 case OMPC_uniform: 72 return "uniform"; 73 case OMPC_threadprivate: 74 return "threadprivate or thread local"; 75 } 76 llvm_unreachable("Invalid OpenMP clause kind"); 77 } 78 79 unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, 80 StringRef Str) { 81 switch (Kind) { 82 case OMPC_default: 83 return llvm::StringSwitch<OpenMPDefaultClauseKind>(Str) 84 #define OPENMP_DEFAULT_KIND(Name) .Case(#Name, OMPC_DEFAULT_##Name) 85 #include "clang/Basic/OpenMPKinds.def" 86 .Default(OMPC_DEFAULT_unknown); 87 case OMPC_proc_bind: 88 return llvm::StringSwitch<OpenMPProcBindClauseKind>(Str) 89 #define OPENMP_PROC_BIND_KIND(Name) .Case(#Name, OMPC_PROC_BIND_##Name) 90 #include "clang/Basic/OpenMPKinds.def" 91 .Default(OMPC_PROC_BIND_unknown); 92 case OMPC_schedule: 93 return llvm::StringSwitch<unsigned>(Str) 94 #define OPENMP_SCHEDULE_KIND(Name) \ 95 .Case(#Name, static_cast<unsigned>(OMPC_SCHEDULE_##Name)) 96 #define OPENMP_SCHEDULE_MODIFIER(Name) \ 97 .Case(#Name, static_cast<unsigned>(OMPC_SCHEDULE_MODIFIER_##Name)) 98 #include "clang/Basic/OpenMPKinds.def" 99 .Default(OMPC_SCHEDULE_unknown); 100 case OMPC_depend: 101 return llvm::StringSwitch<OpenMPDependClauseKind>(Str) 102 #define OPENMP_DEPEND_KIND(Name) .Case(#Name, OMPC_DEPEND_##Name) 103 #include "clang/Basic/OpenMPKinds.def" 104 .Default(OMPC_DEPEND_unknown); 105 case OMPC_linear: 106 return llvm::StringSwitch<OpenMPLinearClauseKind>(Str) 107 #define OPENMP_LINEAR_KIND(Name) .Case(#Name, OMPC_LINEAR_##Name) 108 #include "clang/Basic/OpenMPKinds.def" 109 .Default(OMPC_LINEAR_unknown); 110 case OMPC_map: 111 return llvm::StringSwitch<OpenMPMapClauseKind>(Str) 112 #define OPENMP_MAP_KIND(Name) .Case(#Name, OMPC_MAP_##Name) 113 #include "clang/Basic/OpenMPKinds.def" 114 .Default(OMPC_MAP_unknown); 115 case OMPC_dist_schedule: 116 return llvm::StringSwitch<OpenMPDistScheduleClauseKind>(Str) 117 #define OPENMP_DIST_SCHEDULE_KIND(Name) .Case(#Name, OMPC_DIST_SCHEDULE_##Name) 118 #include "clang/Basic/OpenMPKinds.def" 119 .Default(OMPC_DIST_SCHEDULE_unknown); 120 case OMPC_defaultmap: 121 return llvm::StringSwitch<unsigned>(Str) 122 #define OPENMP_DEFAULTMAP_KIND(Name) \ 123 .Case(#Name, static_cast<unsigned>(OMPC_DEFAULTMAP_##Name)) 124 #define OPENMP_DEFAULTMAP_MODIFIER(Name) \ 125 .Case(#Name, static_cast<unsigned>(OMPC_DEFAULTMAP_MODIFIER_##Name)) 126 #include "clang/Basic/OpenMPKinds.def" 127 .Default(OMPC_DEFAULTMAP_unknown); 128 case OMPC_unknown: 129 case OMPC_threadprivate: 130 case OMPC_if: 131 case OMPC_final: 132 case OMPC_num_threads: 133 case OMPC_safelen: 134 case OMPC_simdlen: 135 case OMPC_collapse: 136 case OMPC_private: 137 case OMPC_firstprivate: 138 case OMPC_lastprivate: 139 case OMPC_shared: 140 case OMPC_reduction: 141 case OMPC_aligned: 142 case OMPC_copyin: 143 case OMPC_copyprivate: 144 case OMPC_ordered: 145 case OMPC_nowait: 146 case OMPC_untied: 147 case OMPC_mergeable: 148 case OMPC_flush: 149 case OMPC_read: 150 case OMPC_write: 151 case OMPC_update: 152 case OMPC_capture: 153 case OMPC_seq_cst: 154 case OMPC_device: 155 case OMPC_threads: 156 case OMPC_simd: 157 case OMPC_num_teams: 158 case OMPC_thread_limit: 159 case OMPC_priority: 160 case OMPC_grainsize: 161 case OMPC_nogroup: 162 case OMPC_num_tasks: 163 case OMPC_hint: 164 case OMPC_uniform: 165 case OMPC_to: 166 case OMPC_from: 167 break; 168 } 169 llvm_unreachable("Invalid OpenMP simple clause kind"); 170 } 171 172 const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, 173 unsigned Type) { 174 switch (Kind) { 175 case OMPC_default: 176 switch (Type) { 177 case OMPC_DEFAULT_unknown: 178 return "unknown"; 179 #define OPENMP_DEFAULT_KIND(Name) \ 180 case OMPC_DEFAULT_##Name: \ 181 return #Name; 182 #include "clang/Basic/OpenMPKinds.def" 183 } 184 llvm_unreachable("Invalid OpenMP 'default' clause type"); 185 case OMPC_proc_bind: 186 switch (Type) { 187 case OMPC_PROC_BIND_unknown: 188 return "unknown"; 189 #define OPENMP_PROC_BIND_KIND(Name) \ 190 case OMPC_PROC_BIND_##Name: \ 191 return #Name; 192 #include "clang/Basic/OpenMPKinds.def" 193 } 194 llvm_unreachable("Invalid OpenMP 'proc_bind' clause type"); 195 case OMPC_schedule: 196 switch (Type) { 197 case OMPC_SCHEDULE_unknown: 198 case OMPC_SCHEDULE_MODIFIER_last: 199 return "unknown"; 200 #define OPENMP_SCHEDULE_KIND(Name) \ 201 case OMPC_SCHEDULE_##Name: \ 202 return #Name; 203 #define OPENMP_SCHEDULE_MODIFIER(Name) \ 204 case OMPC_SCHEDULE_MODIFIER_##Name: \ 205 return #Name; 206 #include "clang/Basic/OpenMPKinds.def" 207 } 208 llvm_unreachable("Invalid OpenMP 'schedule' clause type"); 209 case OMPC_depend: 210 switch (Type) { 211 case OMPC_DEPEND_unknown: 212 return "unknown"; 213 #define OPENMP_DEPEND_KIND(Name) \ 214 case OMPC_DEPEND_##Name: \ 215 return #Name; 216 #include "clang/Basic/OpenMPKinds.def" 217 } 218 llvm_unreachable("Invalid OpenMP 'depend' clause type"); 219 case OMPC_linear: 220 switch (Type) { 221 case OMPC_LINEAR_unknown: 222 return "unknown"; 223 #define OPENMP_LINEAR_KIND(Name) \ 224 case OMPC_LINEAR_##Name: \ 225 return #Name; 226 #include "clang/Basic/OpenMPKinds.def" 227 } 228 llvm_unreachable("Invalid OpenMP 'linear' clause type"); 229 case OMPC_map: 230 switch (Type) { 231 case OMPC_MAP_unknown: 232 return "unknown"; 233 #define OPENMP_MAP_KIND(Name) \ 234 case OMPC_MAP_##Name: \ 235 return #Name; 236 #include "clang/Basic/OpenMPKinds.def" 237 default: 238 break; 239 } 240 llvm_unreachable("Invalid OpenMP 'map' clause type"); 241 case OMPC_dist_schedule: 242 switch (Type) { 243 case OMPC_DIST_SCHEDULE_unknown: 244 return "unknown"; 245 #define OPENMP_DIST_SCHEDULE_KIND(Name) \ 246 case OMPC_DIST_SCHEDULE_##Name: \ 247 return #Name; 248 #include "clang/Basic/OpenMPKinds.def" 249 } 250 llvm_unreachable("Invalid OpenMP 'dist_schedule' clause type"); 251 case OMPC_defaultmap: 252 switch (Type) { 253 case OMPC_DEFAULTMAP_unknown: 254 case OMPC_DEFAULTMAP_MODIFIER_last: 255 return "unknown"; 256 #define OPENMP_DEFAULTMAP_KIND(Name) \ 257 case OMPC_DEFAULTMAP_##Name: \ 258 return #Name; 259 #define OPENMP_DEFAULTMAP_MODIFIER(Name) \ 260 case OMPC_DEFAULTMAP_MODIFIER_##Name: \ 261 return #Name; 262 #include "clang/Basic/OpenMPKinds.def" 263 } 264 llvm_unreachable("Invalid OpenMP 'schedule' clause type"); 265 case OMPC_unknown: 266 case OMPC_threadprivate: 267 case OMPC_if: 268 case OMPC_final: 269 case OMPC_num_threads: 270 case OMPC_safelen: 271 case OMPC_simdlen: 272 case OMPC_collapse: 273 case OMPC_private: 274 case OMPC_firstprivate: 275 case OMPC_lastprivate: 276 case OMPC_shared: 277 case OMPC_reduction: 278 case OMPC_aligned: 279 case OMPC_copyin: 280 case OMPC_copyprivate: 281 case OMPC_ordered: 282 case OMPC_nowait: 283 case OMPC_untied: 284 case OMPC_mergeable: 285 case OMPC_flush: 286 case OMPC_read: 287 case OMPC_write: 288 case OMPC_update: 289 case OMPC_capture: 290 case OMPC_seq_cst: 291 case OMPC_device: 292 case OMPC_threads: 293 case OMPC_simd: 294 case OMPC_num_teams: 295 case OMPC_thread_limit: 296 case OMPC_priority: 297 case OMPC_grainsize: 298 case OMPC_nogroup: 299 case OMPC_num_tasks: 300 case OMPC_hint: 301 case OMPC_uniform: 302 case OMPC_to: 303 case OMPC_from: 304 break; 305 } 306 llvm_unreachable("Invalid OpenMP simple clause kind"); 307 } 308 309 bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind, 310 OpenMPClauseKind CKind) { 311 assert(DKind <= OMPD_unknown); 312 assert(CKind <= OMPC_unknown); 313 switch (DKind) { 314 case OMPD_parallel: 315 switch (CKind) { 316 #define OPENMP_PARALLEL_CLAUSE(Name) \ 317 case OMPC_##Name: \ 318 return true; 319 #include "clang/Basic/OpenMPKinds.def" 320 default: 321 break; 322 } 323 break; 324 case OMPD_simd: 325 switch (CKind) { 326 #define OPENMP_SIMD_CLAUSE(Name) \ 327 case OMPC_##Name: \ 328 return true; 329 #include "clang/Basic/OpenMPKinds.def" 330 default: 331 break; 332 } 333 break; 334 case OMPD_for: 335 switch (CKind) { 336 #define OPENMP_FOR_CLAUSE(Name) \ 337 case OMPC_##Name: \ 338 return true; 339 #include "clang/Basic/OpenMPKinds.def" 340 default: 341 break; 342 } 343 break; 344 case OMPD_for_simd: 345 switch (CKind) { 346 #define OPENMP_FOR_SIMD_CLAUSE(Name) \ 347 case OMPC_##Name: \ 348 return true; 349 #include "clang/Basic/OpenMPKinds.def" 350 default: 351 break; 352 } 353 break; 354 case OMPD_sections: 355 switch (CKind) { 356 #define OPENMP_SECTIONS_CLAUSE(Name) \ 357 case OMPC_##Name: \ 358 return true; 359 #include "clang/Basic/OpenMPKinds.def" 360 default: 361 break; 362 } 363 break; 364 case OMPD_single: 365 switch (CKind) { 366 #define OPENMP_SINGLE_CLAUSE(Name) \ 367 case OMPC_##Name: \ 368 return true; 369 #include "clang/Basic/OpenMPKinds.def" 370 default: 371 break; 372 } 373 break; 374 case OMPD_parallel_for: 375 switch (CKind) { 376 #define OPENMP_PARALLEL_FOR_CLAUSE(Name) \ 377 case OMPC_##Name: \ 378 return true; 379 #include "clang/Basic/OpenMPKinds.def" 380 default: 381 break; 382 } 383 break; 384 case OMPD_parallel_for_simd: 385 switch (CKind) { 386 #define OPENMP_PARALLEL_FOR_SIMD_CLAUSE(Name) \ 387 case OMPC_##Name: \ 388 return true; 389 #include "clang/Basic/OpenMPKinds.def" 390 default: 391 break; 392 } 393 break; 394 case OMPD_parallel_sections: 395 switch (CKind) { 396 #define OPENMP_PARALLEL_SECTIONS_CLAUSE(Name) \ 397 case OMPC_##Name: \ 398 return true; 399 #include "clang/Basic/OpenMPKinds.def" 400 default: 401 break; 402 } 403 break; 404 case OMPD_task: 405 switch (CKind) { 406 #define OPENMP_TASK_CLAUSE(Name) \ 407 case OMPC_##Name: \ 408 return true; 409 #include "clang/Basic/OpenMPKinds.def" 410 default: 411 break; 412 } 413 break; 414 case OMPD_flush: 415 return CKind == OMPC_flush; 416 break; 417 case OMPD_atomic: 418 switch (CKind) { 419 #define OPENMP_ATOMIC_CLAUSE(Name) \ 420 case OMPC_##Name: \ 421 return true; 422 #include "clang/Basic/OpenMPKinds.def" 423 default: 424 break; 425 } 426 break; 427 case OMPD_target: 428 switch (CKind) { 429 #define OPENMP_TARGET_CLAUSE(Name) \ 430 case OMPC_##Name: \ 431 return true; 432 #include "clang/Basic/OpenMPKinds.def" 433 default: 434 break; 435 } 436 break; 437 case OMPD_target_data: 438 switch (CKind) { 439 #define OPENMP_TARGET_DATA_CLAUSE(Name) \ 440 case OMPC_##Name: \ 441 return true; 442 #include "clang/Basic/OpenMPKinds.def" 443 default: 444 break; 445 } 446 break; 447 case OMPD_target_enter_data: 448 switch (CKind) { 449 #define OPENMP_TARGET_ENTER_DATA_CLAUSE(Name) \ 450 case OMPC_##Name: \ 451 return true; 452 #include "clang/Basic/OpenMPKinds.def" 453 default: 454 break; 455 } 456 break; 457 case OMPD_target_exit_data: 458 switch (CKind) { 459 #define OPENMP_TARGET_EXIT_DATA_CLAUSE(Name) \ 460 case OMPC_##Name: \ 461 return true; 462 #include "clang/Basic/OpenMPKinds.def" 463 default: 464 break; 465 } 466 break; 467 case OMPD_target_parallel: 468 switch (CKind) { 469 #define OPENMP_TARGET_PARALLEL_CLAUSE(Name) \ 470 case OMPC_##Name: \ 471 return true; 472 #include "clang/Basic/OpenMPKinds.def" 473 default: 474 break; 475 } 476 break; 477 case OMPD_target_parallel_for: 478 switch (CKind) { 479 #define OPENMP_TARGET_PARALLEL_FOR_CLAUSE(Name) \ 480 case OMPC_##Name: \ 481 return true; 482 #include "clang/Basic/OpenMPKinds.def" 483 default: 484 break; 485 } 486 break; 487 case OMPD_target_update: 488 switch (CKind) { 489 #define OPENMP_TARGET_UPDATE_CLAUSE(Name) \ 490 case OMPC_##Name: \ 491 return true; 492 #include "clang/Basic/OpenMPKinds.def" 493 default: 494 break; 495 } 496 break; 497 case OMPD_teams: 498 switch (CKind) { 499 #define OPENMP_TEAMS_CLAUSE(Name) \ 500 case OMPC_##Name: \ 501 return true; 502 #include "clang/Basic/OpenMPKinds.def" 503 default: 504 break; 505 } 506 break; 507 case OMPD_declare_simd: 508 break; 509 case OMPD_cancel: 510 switch (CKind) { 511 #define OPENMP_CANCEL_CLAUSE(Name) \ 512 case OMPC_##Name: \ 513 return true; 514 #include "clang/Basic/OpenMPKinds.def" 515 default: 516 break; 517 } 518 break; 519 case OMPD_ordered: 520 switch (CKind) { 521 #define OPENMP_ORDERED_CLAUSE(Name) \ 522 case OMPC_##Name: \ 523 return true; 524 #include "clang/Basic/OpenMPKinds.def" 525 default: 526 break; 527 } 528 break; 529 case OMPD_taskloop: 530 switch (CKind) { 531 #define OPENMP_TASKLOOP_CLAUSE(Name) \ 532 case OMPC_##Name: \ 533 return true; 534 #include "clang/Basic/OpenMPKinds.def" 535 default: 536 break; 537 } 538 break; 539 case OMPD_taskloop_simd: 540 switch (CKind) { 541 #define OPENMP_TASKLOOP_SIMD_CLAUSE(Name) \ 542 case OMPC_##Name: \ 543 return true; 544 #include "clang/Basic/OpenMPKinds.def" 545 default: 546 break; 547 } 548 break; 549 case OMPD_critical: 550 switch (CKind) { 551 #define OPENMP_CRITICAL_CLAUSE(Name) \ 552 case OMPC_##Name: \ 553 return true; 554 #include "clang/Basic/OpenMPKinds.def" 555 default: 556 break; 557 } 558 break; 559 case OMPD_distribute: 560 switch (CKind) { 561 #define OPENMP_DISTRIBUTE_CLAUSE(Name) \ 562 case OMPC_##Name: \ 563 return true; 564 #include "clang/Basic/OpenMPKinds.def" 565 default: 566 break; 567 } 568 break; 569 case OMPD_declare_target: 570 case OMPD_end_declare_target: 571 case OMPD_unknown: 572 case OMPD_threadprivate: 573 case OMPD_section: 574 case OMPD_master: 575 case OMPD_taskyield: 576 case OMPD_barrier: 577 case OMPD_taskwait: 578 case OMPD_taskgroup: 579 case OMPD_cancellation_point: 580 case OMPD_declare_reduction: 581 break; 582 } 583 return false; 584 } 585 586 bool clang::isOpenMPLoopDirective(OpenMPDirectiveKind DKind) { 587 return DKind == OMPD_simd || DKind == OMPD_for || DKind == OMPD_for_simd || 588 DKind == OMPD_parallel_for || DKind == OMPD_parallel_for_simd || 589 DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd || 590 DKind == OMPD_distribute || 591 DKind == OMPD_target_parallel_for; // TODO add next directives. 592 } 593 594 bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) { 595 return DKind == OMPD_for || DKind == OMPD_for_simd || 596 DKind == OMPD_sections || DKind == OMPD_section || 597 DKind == OMPD_single || DKind == OMPD_parallel_for || 598 DKind == OMPD_parallel_for_simd || DKind == OMPD_parallel_sections || 599 DKind == OMPD_target_parallel_for; // TODO add next directives. 600 } 601 602 bool clang::isOpenMPTaskLoopDirective(OpenMPDirectiveKind DKind) { 603 return DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd; 604 } 605 606 bool clang::isOpenMPParallelDirective(OpenMPDirectiveKind DKind) { 607 return DKind == OMPD_parallel || DKind == OMPD_parallel_for || 608 DKind == OMPD_parallel_for_simd || DKind == OMPD_parallel_sections || 609 DKind == OMPD_target_parallel || DKind == OMPD_target_parallel_for; 610 // TODO add next directives. 611 } 612 613 bool clang::isOpenMPTargetExecutionDirective(OpenMPDirectiveKind DKind) { 614 // TODO add next directives. 615 return DKind == OMPD_target || DKind == OMPD_target_parallel || 616 DKind == OMPD_target_parallel_for; 617 } 618 619 bool clang::isOpenMPTargetDataManagementDirective(OpenMPDirectiveKind DKind) { 620 // TODO add target update directive check. 621 return DKind == OMPD_target_data || DKind == OMPD_target_enter_data || 622 DKind == OMPD_target_exit_data || DKind == OMPD_target_update; 623 } 624 625 bool clang::isOpenMPTeamsDirective(OpenMPDirectiveKind DKind) { 626 return DKind == OMPD_teams; // TODO add next directives. 627 } 628 629 bool clang::isOpenMPSimdDirective(OpenMPDirectiveKind DKind) { 630 return DKind == OMPD_simd || DKind == OMPD_for_simd || 631 DKind == OMPD_parallel_for_simd || 632 DKind == OMPD_taskloop_simd; // TODO add next directives. 633 } 634 635 bool clang::isOpenMPDistributeDirective(OpenMPDirectiveKind Kind) { 636 return Kind == OMPD_distribute; // TODO add next directives. 637 } 638 639 bool clang::isOpenMPPrivate(OpenMPClauseKind Kind) { 640 return Kind == OMPC_private || Kind == OMPC_firstprivate || 641 Kind == OMPC_lastprivate || Kind == OMPC_linear || 642 Kind == OMPC_reduction; // TODO add next clauses like 'reduction'. 643 } 644 645 bool clang::isOpenMPThreadPrivate(OpenMPClauseKind Kind) { 646 return Kind == OMPC_threadprivate || Kind == OMPC_copyin; 647 } 648 649 bool clang::isOpenMPTaskingDirective(OpenMPDirectiveKind Kind) { 650 return Kind == OMPD_task || isOpenMPTaskLoopDirective(Kind); 651 } 652