1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
|
//===- polly/ScheduleTreeTransform.cpp --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Make changes to isl's schedule tree data structure.
//
//===----------------------------------------------------------------------===//
#include "polly/ScheduleTreeTransform.h"
#include "polly/Support/GICHelper.h"
#include "polly/Support/ISLTools.h"
#include "polly/Support/ScopHelper.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Transforms/Utils/UnrollLoop.h"
#define DEBUG_TYPE "polly-opt-isl"
using namespace polly;
using namespace llvm;
namespace {
/// Copy the band member attributes (coincidence, loop type, isolate ast loop
/// type) from one band to another.
static isl::schedule_node_band
applyBandMemberAttributes(isl::schedule_node_band Target, int TargetIdx,
const isl::schedule_node_band &Source,
int SourceIdx) {
bool Coincident = Source.member_get_coincident(SourceIdx).release();
Target = Target.member_set_coincident(TargetIdx, Coincident);
isl_ast_loop_type LoopType =
isl_schedule_node_band_member_get_ast_loop_type(Source.get(), SourceIdx);
Target = isl::manage(isl_schedule_node_band_member_set_ast_loop_type(
Target.release(), TargetIdx, LoopType))
.as<isl::schedule_node_band>();
isl_ast_loop_type IsolateType =
isl_schedule_node_band_member_get_isolate_ast_loop_type(Source.get(),
SourceIdx);
Target = isl::manage(isl_schedule_node_band_member_set_isolate_ast_loop_type(
Target.release(), TargetIdx, IsolateType))
.as<isl::schedule_node_band>();
return Target;
}
/// Create a new band by copying members from another @p Band. @p IncludeCb
/// decides which band indices are copied to the result.
template <typename CbTy>
static isl::schedule rebuildBand(isl::schedule_node_band OldBand,
isl::schedule Body, CbTy IncludeCb) {
int NumBandDims = unsignedFromIslSize(OldBand.n_member());
bool ExcludeAny = false;
bool IncludeAny = false;
for (auto OldIdx : seq<int>(0, NumBandDims)) {
if (IncludeCb(OldIdx))
IncludeAny = true;
else
ExcludeAny = true;
}
// Instead of creating a zero-member band, don't create a band at all.
if (!IncludeAny)
return Body;
isl::multi_union_pw_aff PartialSched = OldBand.get_partial_schedule();
isl::multi_union_pw_aff NewPartialSched;
if (ExcludeAny) {
// Select the included partial scatter functions.
isl::union_pw_aff_list List = PartialSched.list();
int NewIdx = 0;
for (auto OldIdx : seq<int>(0, NumBandDims)) {
if (IncludeCb(OldIdx))
NewIdx += 1;
else
List = List.drop(NewIdx, 1);
}
isl::space ParamSpace = PartialSched.get_space().params();
isl::space NewScatterSpace = ParamSpace.add_unnamed_tuple(NewIdx);
NewPartialSched = isl::multi_union_pw_aff(NewScatterSpace, List);
} else {
// Just reuse original scatter function of copying all of them.
NewPartialSched = PartialSched;
}
// Create the new band node.
isl::schedule_node_band NewBand =
Body.insert_partial_schedule(NewPartialSched)
.get_root()
.child(0)
.as<isl::schedule_node_band>();
// If OldBand was permutable, so is the new one, even if some dimensions are
// missing.
bool IsPermutable = OldBand.permutable().release();
NewBand = NewBand.set_permutable(IsPermutable);
// Reapply member attributes.
int NewIdx = 0;
for (auto OldIdx : seq<int>(0, NumBandDims)) {
if (!IncludeCb(OldIdx))
continue;
NewBand =
applyBandMemberAttributes(std::move(NewBand), NewIdx, OldBand, OldIdx);
NewIdx += 1;
}
return NewBand.get_schedule();
}
/// Rewrite a schedule tree by reconstructing it bottom-up.
///
/// By default, the original schedule tree is reconstructed. To build a
/// different tree, redefine visitor methods in a derived class (CRTP).
///
/// Note that AST build options are not applied; Setting the isolate[] option
/// makes the schedule tree 'anchored' and cannot be modified afterwards. Hence,
/// AST build options must be set after the tree has been constructed.
template <typename Derived, typename... Args>
struct ScheduleTreeRewriter
: RecursiveScheduleTreeVisitor<Derived, isl::schedule, Args...> {
Derived &getDerived() { return *static_cast<Derived *>(this); }
const Derived &getDerived() const {
return *static_cast<const Derived *>(this);
}
isl::schedule visitDomain(isl::schedule_node_domain Node, Args... args) {
// Every schedule_tree already has a domain node, no need to add one.
return getDerived().visit(Node.first_child(), std::forward<Args>(args)...);
}
isl::schedule visitBand(isl::schedule_node_band Band, Args... args) {
isl::schedule NewChild =
getDerived().visit(Band.child(0), std::forward<Args>(args)...);
return rebuildBand(Band, NewChild, [](int) { return true; });
}
isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
Args... args) {
int NumChildren = isl_schedule_node_n_children(Sequence.get());
isl::schedule Result =
getDerived().visit(Sequence.child(0), std::forward<Args>(args)...);
for (int i = 1; i < NumChildren; i += 1)
Result = Result.sequence(
getDerived().visit(Sequence.child(i), std::forward<Args>(args)...));
return Result;
}
isl::schedule visitSet(isl::schedule_node_set Set, Args... args) {
int NumChildren = isl_schedule_node_n_children(Set.get());
isl::schedule Result =
getDerived().visit(Set.child(0), std::forward<Args>(args)...);
for (int i = 1; i < NumChildren; i += 1)
Result = isl::manage(
isl_schedule_set(Result.release(),
getDerived()
.visit(Set.child(i), std::forward<Args>(args)...)
.release()));
return Result;
}
isl::schedule visitLeaf(isl::schedule_node_leaf Leaf, Args... args) {
return isl::schedule::from_domain(Leaf.get_domain());
}
isl::schedule visitMark(const isl::schedule_node &Mark, Args... args) {
isl::id TheMark = Mark.as<isl::schedule_node_mark>().get_id();
isl::schedule_node NewChild =
getDerived()
.visit(Mark.first_child(), std::forward<Args>(args)...)
.get_root()
.first_child();
return NewChild.insert_mark(TheMark).get_schedule();
}
isl::schedule visitExtension(isl::schedule_node_extension Extension,
Args... args) {
isl::union_map TheExtension =
Extension.as<isl::schedule_node_extension>().get_extension();
isl::schedule_node NewChild = getDerived()
.visit(Extension.child(0), args...)
.get_root()
.first_child();
isl::schedule_node NewExtension =
isl::schedule_node::from_extension(TheExtension);
return NewChild.graft_before(NewExtension).get_schedule();
}
isl::schedule visitFilter(isl::schedule_node_filter Filter, Args... args) {
isl::union_set FilterDomain =
Filter.as<isl::schedule_node_filter>().get_filter();
isl::schedule NewSchedule =
getDerived().visit(Filter.child(0), std::forward<Args>(args)...);
return NewSchedule.intersect_domain(FilterDomain);
}
isl::schedule visitNode(isl::schedule_node Node, Args... args) {
llvm_unreachable("Not implemented");
}
};
/// Rewrite the schedule tree without any changes. Useful to copy a subtree into
/// a new schedule, discarding everything but.
struct IdentityRewriter : ScheduleTreeRewriter<IdentityRewriter> {};
/// Rewrite a schedule tree to an equivalent one without extension nodes.
///
/// Each visit method takes two additional arguments:
///
/// * The new domain the node, which is the inherited domain plus any domains
/// added by extension nodes.
///
/// * A map of extension domains of all children is returned; it is required by
/// band nodes to schedule the additional domains at the same position as the
/// extension node would.
///
struct ExtensionNodeRewriter final
: ScheduleTreeRewriter<ExtensionNodeRewriter, const isl::union_set &,
isl::union_map &> {
using BaseTy = ScheduleTreeRewriter<ExtensionNodeRewriter,
const isl::union_set &, isl::union_map &>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
isl::schedule visitSchedule(isl::schedule Schedule) {
isl::union_map Extensions;
isl::schedule Result =
visit(Schedule.get_root(), Schedule.get_domain(), Extensions);
assert(!Extensions.is_null() && Extensions.is_empty());
return Result;
}
isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
const isl::union_set &Domain,
isl::union_map &Extensions) {
int NumChildren = isl_schedule_node_n_children(Sequence.get());
isl::schedule NewNode = visit(Sequence.first_child(), Domain, Extensions);
for (int i = 1; i < NumChildren; i += 1) {
isl::schedule_node OldChild = Sequence.child(i);
isl::union_map NewChildExtensions;
isl::schedule NewChildNode = visit(OldChild, Domain, NewChildExtensions);
NewNode = NewNode.sequence(NewChildNode);
Extensions = Extensions.unite(NewChildExtensions);
}
return NewNode;
}
isl::schedule visitSet(isl::schedule_node_set Set,
const isl::union_set &Domain,
isl::union_map &Extensions) {
int NumChildren = isl_schedule_node_n_children(Set.get());
isl::schedule NewNode = visit(Set.first_child(), Domain, Extensions);
for (int i = 1; i < NumChildren; i += 1) {
isl::schedule_node OldChild = Set.child(i);
isl::union_map NewChildExtensions;
isl::schedule NewChildNode = visit(OldChild, Domain, NewChildExtensions);
NewNode = isl::manage(
isl_schedule_set(NewNode.release(), NewChildNode.release()));
Extensions = Extensions.unite(NewChildExtensions);
}
return NewNode;
}
isl::schedule visitLeaf(isl::schedule_node_leaf Leaf,
const isl::union_set &Domain,
isl::union_map &Extensions) {
Extensions = isl::union_map::empty(Leaf.ctx());
return isl::schedule::from_domain(Domain);
}
isl::schedule visitBand(isl::schedule_node_band OldNode,
const isl::union_set &Domain,
isl::union_map &OuterExtensions) {
isl::schedule_node OldChild = OldNode.first_child();
isl::multi_union_pw_aff PartialSched =
isl::manage(isl_schedule_node_band_get_partial_schedule(OldNode.get()));
isl::union_map NewChildExtensions;
isl::schedule NewChild = visit(OldChild, Domain, NewChildExtensions);
// Add the extensions to the partial schedule.
OuterExtensions = isl::union_map::empty(NewChildExtensions.ctx());
isl::union_map NewPartialSchedMap = isl::union_map::from(PartialSched);
unsigned BandDims = isl_schedule_node_band_n_member(OldNode.get());
for (isl::map Ext : NewChildExtensions.get_map_list()) {
unsigned ExtDims = unsignedFromIslSize(Ext.domain_tuple_dim());
assert(ExtDims >= BandDims);
unsigned OuterDims = ExtDims - BandDims;
isl::map BandSched =
Ext.project_out(isl::dim::in, 0, OuterDims).reverse();
NewPartialSchedMap = NewPartialSchedMap.unite(BandSched);
// There might be more outer bands that have to schedule the extensions.
if (OuterDims > 0) {
isl::map OuterSched =
Ext.project_out(isl::dim::in, OuterDims, BandDims);
OuterExtensions = OuterExtensions.unite(OuterSched);
}
}
isl::multi_union_pw_aff NewPartialSchedAsAsMultiUnionPwAff =
isl::multi_union_pw_aff::from_union_map(NewPartialSchedMap);
isl::schedule_node NewNode =
NewChild.insert_partial_schedule(NewPartialSchedAsAsMultiUnionPwAff)
.get_root()
.child(0);
// Reapply permutability and coincidence attributes.
NewNode = isl::manage(isl_schedule_node_band_set_permutable(
NewNode.release(),
isl_schedule_node_band_get_permutable(OldNode.get())));
for (unsigned i = 0; i < BandDims; i += 1)
NewNode = applyBandMemberAttributes(NewNode.as<isl::schedule_node_band>(),
i, OldNode, i);
return NewNode.get_schedule();
}
isl::schedule visitFilter(isl::schedule_node_filter Filter,
const isl::union_set &Domain,
isl::union_map &Extensions) {
isl::union_set FilterDomain =
Filter.as<isl::schedule_node_filter>().get_filter();
isl::union_set NewDomain = Domain.intersect(FilterDomain);
// A filter is added implicitly if necessary when joining schedule trees.
return visit(Filter.first_child(), NewDomain, Extensions);
}
isl::schedule visitExtension(isl::schedule_node_extension Extension,
const isl::union_set &Domain,
isl::union_map &Extensions) {
isl::union_map ExtDomain =
Extension.as<isl::schedule_node_extension>().get_extension();
isl::union_set NewDomain = Domain.unite(ExtDomain.range());
isl::union_map ChildExtensions;
isl::schedule NewChild =
visit(Extension.first_child(), NewDomain, ChildExtensions);
Extensions = ChildExtensions.unite(ExtDomain);
return NewChild;
}
};
/// Collect all AST build options in any schedule tree band.
///
/// ScheduleTreeRewriter cannot apply the schedule tree options. This class
/// collects these options to apply them later.
struct CollectASTBuildOptions final
: RecursiveScheduleTreeVisitor<CollectASTBuildOptions> {
using BaseTy = RecursiveScheduleTreeVisitor<CollectASTBuildOptions>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
llvm::SmallVector<isl::union_set, 8> ASTBuildOptions;
void visitBand(isl::schedule_node_band Band) {
ASTBuildOptions.push_back(
isl::manage(isl_schedule_node_band_get_ast_build_options(Band.get())));
return getBase().visitBand(Band);
}
};
/// Apply AST build options to the bands in a schedule tree.
///
/// This rewrites a schedule tree with the AST build options applied. We assume
/// that the band nodes are visited in the same order as they were when the
/// build options were collected, typically by CollectASTBuildOptions.
struct ApplyASTBuildOptions final : ScheduleNodeRewriter<ApplyASTBuildOptions> {
using BaseTy = ScheduleNodeRewriter<ApplyASTBuildOptions>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
size_t Pos;
llvm::ArrayRef<isl::union_set> ASTBuildOptions;
ApplyASTBuildOptions(llvm::ArrayRef<isl::union_set> ASTBuildOptions)
: ASTBuildOptions(ASTBuildOptions) {}
isl::schedule visitSchedule(isl::schedule Schedule) {
Pos = 0;
isl::schedule Result = visit(Schedule).get_schedule();
assert(Pos == ASTBuildOptions.size() &&
"AST build options must match to band nodes");
return Result;
}
isl::schedule_node visitBand(isl::schedule_node_band Band) {
isl::schedule_node_band Result =
Band.set_ast_build_options(ASTBuildOptions[Pos]);
Pos += 1;
return getBase().visitBand(Result);
}
};
/// Return whether the schedule contains an extension node.
static bool containsExtensionNode(isl::schedule Schedule) {
assert(!Schedule.is_null());
auto Callback = [](__isl_keep isl_schedule_node *Node,
void *User) -> isl_bool {
if (isl_schedule_node_get_type(Node) == isl_schedule_node_extension) {
// Stop walking the schedule tree.
return isl_bool_error;
}
// Continue searching the subtree.
return isl_bool_true;
};
isl_stat RetVal = isl_schedule_foreach_schedule_node_top_down(
Schedule.get(), Callback, nullptr);
// We assume that the traversal itself does not fail, i.e. the only reason to
// return isl_stat_error is that an extension node was found.
return RetVal == isl_stat_error;
}
/// Find a named MDNode property in a LoopID.
static MDNode *findOptionalNodeOperand(MDNode *LoopMD, StringRef Name) {
return dyn_cast_or_null<MDNode>(
findMetadataOperand(LoopMD, Name).value_or(nullptr));
}
/// Is this node of type mark?
static bool isMark(const isl::schedule_node &Node) {
return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_mark;
}
/// Is this node of type band?
static bool isBand(const isl::schedule_node &Node) {
return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band;
}
#ifndef NDEBUG
/// Is this node a band of a single dimension (i.e. could represent a loop)?
static bool isBandWithSingleLoop(const isl::schedule_node &Node) {
return isBand(Node) && isl_schedule_node_band_n_member(Node.get()) == 1;
}
#endif
static bool isLeaf(const isl::schedule_node &Node) {
return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_leaf;
}
/// Create an isl::id representing the output loop after a transformation.
static isl::id createGeneratedLoopAttr(isl::ctx Ctx, MDNode *FollowupLoopMD) {
// Don't need to id the followup.
// TODO: Append llvm.loop.disable_heustistics metadata unless overridden by
// user followup-MD
if (!FollowupLoopMD)
return {};
BandAttr *Attr = new BandAttr();
Attr->Metadata = FollowupLoopMD;
return getIslLoopAttr(Ctx, Attr);
}
/// A loop consists of a band and an optional marker that wraps it. Return the
/// outermost of the two.
/// That is, either the mark or, if there is not mark, the loop itself. Can
/// start with either the mark or the band.
static isl::schedule_node moveToBandMark(isl::schedule_node BandOrMark) {
if (isBandMark(BandOrMark)) {
assert(isBandWithSingleLoop(BandOrMark.child(0)));
return BandOrMark;
}
assert(isBandWithSingleLoop(BandOrMark));
isl::schedule_node Mark = BandOrMark.parent();
if (isBandMark(Mark))
return Mark;
// Band has no loop marker.
return BandOrMark;
}
static isl::schedule_node removeMark(isl::schedule_node MarkOrBand,
BandAttr *&Attr) {
MarkOrBand = moveToBandMark(MarkOrBand);
isl::schedule_node Band;
if (isMark(MarkOrBand)) {
Attr = getLoopAttr(MarkOrBand.as<isl::schedule_node_mark>().get_id());
Band = isl::manage(isl_schedule_node_delete(MarkOrBand.release()));
} else {
Attr = nullptr;
Band = MarkOrBand;
}
assert(isBandWithSingleLoop(Band));
return Band;
}
/// Remove the mark that wraps a loop. Return the band representing the loop.
static isl::schedule_node removeMark(isl::schedule_node MarkOrBand) {
BandAttr *Attr;
return removeMark(MarkOrBand, Attr);
}
static isl::schedule_node insertMark(isl::schedule_node Band, isl::id Mark) {
assert(isBand(Band));
assert(moveToBandMark(Band).is_equal(Band) &&
"Don't add a two marks for a band");
return Band.insert_mark(Mark).child(0);
}
/// Return the (one-dimensional) set of numbers that are divisible by @p Factor
/// with remainder @p Offset.
///
/// isDivisibleBySet(Ctx, 4, 0) = { [i] : floord(i,4) = 0 }
/// isDivisibleBySet(Ctx, 4, 1) = { [i] : floord(i,4) = 1 }
///
static isl::basic_set isDivisibleBySet(isl::ctx &Ctx, long Factor,
long Offset) {
isl::val ValFactor{Ctx, Factor};
isl::val ValOffset{Ctx, Offset};
isl::space Unispace{Ctx, 0, 1};
isl::local_space LUnispace{Unispace};
isl::aff AffFactor{LUnispace, ValFactor};
isl::aff AffOffset{LUnispace, ValOffset};
isl::aff Id = isl::aff::var_on_domain(LUnispace, isl::dim::out, 0);
isl::aff DivMul = Id.mod(ValFactor);
isl::basic_map Divisible = isl::basic_map::from_aff(DivMul);
isl::basic_map Modulo = Divisible.fix_val(isl::dim::out, 0, ValOffset);
return Modulo.domain();
}
/// Make the last dimension of Set to take values from 0 to VectorWidth - 1.
///
/// @param Set A set, which should be modified.
/// @param VectorWidth A parameter, which determines the constraint.
static isl::set addExtentConstraints(isl::set Set, int VectorWidth) {
unsigned Dims = unsignedFromIslSize(Set.tuple_dim());
assert(Dims >= 1);
isl::space Space = Set.get_space();
isl::local_space LocalSpace = isl::local_space(Space);
isl::constraint ExtConstr = isl::constraint::alloc_inequality(LocalSpace);
ExtConstr = ExtConstr.set_constant_si(0);
ExtConstr = ExtConstr.set_coefficient_si(isl::dim::set, Dims - 1, 1);
Set = Set.add_constraint(ExtConstr);
ExtConstr = isl::constraint::alloc_inequality(LocalSpace);
ExtConstr = ExtConstr.set_constant_si(VectorWidth - 1);
ExtConstr = ExtConstr.set_coefficient_si(isl::dim::set, Dims - 1, -1);
return Set.add_constraint(ExtConstr);
}
/// Collapse perfectly nested bands into a single band.
class BandCollapseRewriter final
: public ScheduleTreeRewriter<BandCollapseRewriter> {
private:
using BaseTy = ScheduleTreeRewriter<BandCollapseRewriter>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
public:
isl::schedule visitBand(isl::schedule_node_band RootBand) {
isl::schedule_node_band Band = RootBand;
isl::ctx Ctx = Band.ctx();
// Do not merge permutable band to avoid loosing the permutability property.
// Cannot collapse even two permutable loops, they might be permutable
// individually, but not necassarily across.
if (unsignedFromIslSize(Band.n_member()) > 1u && Band.permutable())
return getBase().visitBand(Band);
// Find collapsable bands.
SmallVector<isl::schedule_node_band> Nest;
int NumTotalLoops = 0;
isl::schedule_node Body;
while (true) {
Nest.push_back(Band);
NumTotalLoops += unsignedFromIslSize(Band.n_member());
Body = Band.first_child();
if (!Body.isa<isl::schedule_node_band>())
break;
Band = Body.as<isl::schedule_node_band>();
// Do not include next band if it is permutable to not lose its
// permutability property.
if (unsignedFromIslSize(Band.n_member()) > 1u && Band.permutable())
break;
}
// Nothing to collapse, preserve permutability.
if (Nest.size() <= 1)
return getBase().visitBand(Band);
LLVM_DEBUG({
dbgs() << "Found loops to collapse between\n";
dumpIslObj(RootBand, dbgs());
dbgs() << "and\n";
dumpIslObj(Body, dbgs());
dbgs() << "\n";
});
isl::schedule NewBody = visit(Body);
// Collect partial schedules from all members.
isl::union_pw_aff_list PartScheds{Ctx, NumTotalLoops};
for (isl::schedule_node_band Band : Nest) {
int NumLoops = unsignedFromIslSize(Band.n_member());
isl::multi_union_pw_aff BandScheds = Band.get_partial_schedule();
for (auto j : seq<int>(0, NumLoops))
PartScheds = PartScheds.add(BandScheds.at(j));
}
isl::space ScatterSpace = isl::space(Ctx, 0, NumTotalLoops);
isl::multi_union_pw_aff PartSchedsMulti{ScatterSpace, PartScheds};
isl::schedule_node_band CollapsedBand =
NewBody.insert_partial_schedule(PartSchedsMulti)
.get_root()
.first_child()
.as<isl::schedule_node_band>();
// Copy over loop attributes form original bands.
int LoopIdx = 0;
for (isl::schedule_node_band Band : Nest) {
int NumLoops = unsignedFromIslSize(Band.n_member());
for (int i : seq<int>(0, NumLoops)) {
CollapsedBand = applyBandMemberAttributes(std::move(CollapsedBand),
LoopIdx, Band, i);
LoopIdx += 1;
}
}
assert(LoopIdx == NumTotalLoops &&
"Expect the same number of loops to add up again");
return CollapsedBand.get_schedule();
}
};
static isl::schedule collapseBands(isl::schedule Sched) {
LLVM_DEBUG(dbgs() << "Collapse bands in schedule\n");
BandCollapseRewriter Rewriter;
return Rewriter.visit(Sched);
}
/// Collect sequentially executed bands (or anything else), even if nested in a
/// mark or other nodes whose child is executed just once. If we can
/// successfully fuse the bands, we allow them to be removed.
static void collectPotentiallyFusableBands(
isl::schedule_node Node,
SmallVectorImpl<std::pair<isl::schedule_node, isl::schedule_node>>
&ScheduleBands,
const isl::schedule_node &DirectChild) {
switch (isl_schedule_node_get_type(Node.get())) {
case isl_schedule_node_sequence:
case isl_schedule_node_set:
case isl_schedule_node_mark:
case isl_schedule_node_domain:
case isl_schedule_node_filter:
if (Node.has_children()) {
isl::schedule_node C = Node.first_child();
while (true) {
collectPotentiallyFusableBands(C, ScheduleBands, DirectChild);
if (!C.has_next_sibling())
break;
C = C.next_sibling();
}
}
break;
default:
// Something that does not execute suquentially (e.g. a band)
ScheduleBands.push_back({Node, DirectChild});
break;
}
}
/// Remove dependencies that are resolved by @p PartSched. That is, remove
/// everything that we already know is executed in-order.
static isl::union_map remainingDepsFromPartialSchedule(isl::union_map PartSched,
isl::union_map Deps) {
unsigned NumDims = getNumScatterDims(PartSched);
auto ParamSpace = PartSched.get_space().params();
// { Scatter[] }
isl::space ScatterSpace =
ParamSpace.set_from_params().add_dims(isl::dim::set, NumDims);
// { Scatter[] -> Domain[] }
isl::union_map PartSchedRev = PartSched.reverse();
// { Scatter[] -> Scatter[] }
isl::map MaybeBefore = isl::map::lex_le(ScatterSpace);
// { Domain[] -> Domain[] }
isl::union_map DomMaybeBefore =
MaybeBefore.apply_domain(PartSchedRev).apply_range(PartSchedRev);
// { Domain[] -> Domain[] }
isl::union_map ChildRemainingDeps = Deps.intersect(DomMaybeBefore);
return ChildRemainingDeps;
}
/// Remove dependencies that are resolved by executing them in the order
/// specified by @p Domains;
static isl::union_map remainigDepsFromSequence(ArrayRef<isl::union_set> Domains,
isl::union_map Deps) {
isl::ctx Ctx = Deps.ctx();
isl::space ParamSpace = Deps.get_space().params();
// Create a partial schedule mapping to constants that reflect the execution
// order.
isl::union_map PartialSchedules = isl::union_map::empty(Ctx);
for (auto P : enumerate(Domains)) {
isl::val ExecTime = isl::val(Ctx, P.index());
isl::union_pw_aff DomSched{P.value(), ExecTime};
PartialSchedules = PartialSchedules.unite(DomSched.as_union_map());
}
return remainingDepsFromPartialSchedule(PartialSchedules, Deps);
}
/// Determine whether the outermost loop of to bands can be fused while
/// respecting validity dependencies.
static bool canFuseOutermost(const isl::schedule_node_band &LHS,
const isl::schedule_node_band &RHS,
const isl::union_map &Deps) {
// { LHSDomain[] -> Scatter[] }
isl::union_map LHSPartSched =
LHS.get_partial_schedule().get_at(0).as_union_map();
// { Domain[] -> Scatter[] }
isl::union_map RHSPartSched =
RHS.get_partial_schedule().get_at(0).as_union_map();
// Dependencies that are already resolved because LHS executes before RHS, but
// will not be anymore after fusion. { DefDomain[] -> UseDomain[] }
isl::union_map OrderedBySequence =
Deps.intersect_domain(LHSPartSched.domain())
.intersect_range(RHSPartSched.domain());
isl::space ParamSpace = OrderedBySequence.get_space().params();
isl::space NewScatterSpace = ParamSpace.add_unnamed_tuple(1);
// { Scatter[] -> Scatter[] }
isl::map After = isl::map::lex_gt(NewScatterSpace);
// After fusion, instances with smaller (or equal, which means they will be
// executed in the same iteration, but the LHS instance is still sequenced
// before RHS) scatter value will still be executed before. This are the
// orderings where this is not necessarily the case.
// { LHSDomain[] -> RHSDomain[] }
isl::union_map MightBeAfterDoms = After.apply_domain(LHSPartSched.reverse())
.apply_range(RHSPartSched.reverse());
// Dependencies that are not resolved by the new execution order.
isl::union_map WithBefore = OrderedBySequence.intersect(MightBeAfterDoms);
return WithBefore.is_empty();
}
/// Fuse @p LHS and @p RHS if possible while preserving validity dependenvies.
static isl::schedule tryGreedyFuse(isl::schedule_node_band LHS,
isl::schedule_node_band RHS,
const isl::union_map &Deps) {
if (!canFuseOutermost(LHS, RHS, Deps))
return {};
LLVM_DEBUG({
dbgs() << "Found loops for greedy fusion:\n";
dumpIslObj(LHS, dbgs());
dbgs() << "and\n";
dumpIslObj(RHS, dbgs());
dbgs() << "\n";
});
// The partial schedule of the bands outermost loop that we need to combine
// for the fusion.
isl::union_pw_aff LHSPartOuterSched = LHS.get_partial_schedule().get_at(0);
isl::union_pw_aff RHSPartOuterSched = RHS.get_partial_schedule().get_at(0);
// Isolate band bodies as roots of their own schedule trees.
IdentityRewriter Rewriter;
isl::schedule LHSBody = Rewriter.visit(LHS.first_child());
isl::schedule RHSBody = Rewriter.visit(RHS.first_child());
// Reconstruct the non-outermost (not going to be fused) loops from both
// bands.
// TODO: Maybe it is possibly to transfer the 'permutability' property from
// LHS+RHS. At minimum we need merge multiple band members at once, otherwise
// permutability has no meaning.
isl::schedule LHSNewBody =
rebuildBand(LHS, LHSBody, [](int i) { return i > 0; });
isl::schedule RHSNewBody =
rebuildBand(RHS, RHSBody, [](int i) { return i > 0; });
// The loop body of the fused loop.
isl::schedule NewCommonBody = LHSNewBody.sequence(RHSNewBody);
// Combine the partial schedules of both loops to a new one. Instances with
// the same scatter value are put together.
isl::union_map NewCommonPartialSched =
LHSPartOuterSched.as_union_map().unite(RHSPartOuterSched.as_union_map());
isl::schedule NewCommonSchedule = NewCommonBody.insert_partial_schedule(
NewCommonPartialSched.as_multi_union_pw_aff());
return NewCommonSchedule;
}
static isl::schedule tryGreedyFuse(isl::schedule_node LHS,
isl::schedule_node RHS,
const isl::union_map &Deps) {
// TODO: Non-bands could be interpreted as a band with just as single
// iteration. However, this is only useful if both ends of a fused loop were
// originally loops themselves.
if (!LHS.isa<isl::schedule_node_band>())
return {};
if (!RHS.isa<isl::schedule_node_band>())
return {};
return tryGreedyFuse(LHS.as<isl::schedule_node_band>(),
RHS.as<isl::schedule_node_band>(), Deps);
}
/// Fuse all fusable loop top-down in a schedule tree.
///
/// The isl::union_map parameters is the set of validity dependencies that have
/// not been resolved/carried by a parent schedule node.
class GreedyFusionRewriter final
: public ScheduleTreeRewriter<GreedyFusionRewriter, isl::union_map> {
private:
using BaseTy = ScheduleTreeRewriter<GreedyFusionRewriter, isl::union_map>;
BaseTy &getBase() { return *this; }
const BaseTy &getBase() const { return *this; }
public:
/// Is set to true if anything has been fused.
bool AnyChange = false;
isl::schedule visitBand(isl::schedule_node_band Band, isl::union_map Deps) {
// { Domain[] -> Scatter[] }
isl::union_map PartSched =
isl::union_map::from(Band.get_partial_schedule());
assert(getNumScatterDims(PartSched) ==
unsignedFromIslSize(Band.n_member()));
isl::space ParamSpace = PartSched.get_space().params();
// { Scatter[] -> Domain[] }
isl::union_map PartSchedRev = PartSched.reverse();
// Possible within the same iteration. Dependencies with smaller scatter
// value are carried by this loop and therefore have been resolved by the
// in-order execution if the loop iteration. A dependency with small scatter
// value would be a dependency violation that we assume did not happen. {
// Domain[] -> Domain[] }
isl::union_map Unsequenced = PartSchedRev.apply_domain(PartSchedRev);
// Actual dependencies within the same iteration.
// { DefDomain[] -> UseDomain[] }
isl::union_map RemDeps = Deps.intersect(Unsequenced);
return getBase().visitBand(Band, RemDeps);
}
isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
isl::union_map Deps) {
int NumChildren = isl_schedule_node_n_children(Sequence.get());
// List of fusion candidates. The first element is the fusion candidate, the
// second is candidate's ancestor that is the sequence's direct child. It is
// preferable to use the direct child if not if its non-direct children is
// fused to preserve its structure such as mark nodes.
SmallVector<std::pair<isl::schedule_node, isl::schedule_node>> Bands;
for (auto i : seq<int>(0, NumChildren)) {
isl::schedule_node Child = Sequence.child(i);
collectPotentiallyFusableBands(Child, Bands, Child);
}
// Direct children that had at least one of its decendants fused.
SmallDenseSet<isl_schedule_node *, 4> ChangedDirectChildren;
// Fuse neigboring bands until reaching the end of candidates.
int i = 0;
while (i + 1 < (int)Bands.size()) {
isl::schedule Fused =
tryGreedyFuse(Bands[i].first, Bands[i + 1].first, Deps);
if (Fused.is_null()) {
// Cannot merge this node with the next; look at next pair.
i += 1;
continue;
}
// Mark the direct children as (partially) fused.
if (!Bands[i].second.is_null())
ChangedDirectChildren.insert(Bands[i].second.get());
if (!Bands[i + 1].second.is_null())
ChangedDirectChildren.insert(Bands[i + 1].second.get());
// Collapse the neigbros to a single new candidate that could be fused
// with the next candidate.
Bands[i] = {Fused.get_root(), {}};
Bands.erase(Bands.begin() + i + 1);
AnyChange = true;
}
// By construction equal if done with collectPotentiallyFusableBands's
// output.
SmallVector<isl::union_set> SubDomains;
SubDomains.reserve(NumChildren);
for (int i = 0; i < NumChildren; i += 1)
SubDomains.push_back(Sequence.child(i).domain());
auto SubRemainingDeps = remainigDepsFromSequence(SubDomains, Deps);
// We may iterate over direct children multiple times, be sure to add each
// at most once.
SmallDenseSet<isl_schedule_node *, 4> AlreadyAdded;
isl::schedule Result;
for (auto &P : Bands) {
isl::schedule_node MaybeFused = P.first;
isl::schedule_node DirectChild = P.second;
// If not modified, use the direct child.
if (!DirectChild.is_null() &&
!ChangedDirectChildren.count(DirectChild.get())) {
if (AlreadyAdded.count(DirectChild.get()))
continue;
AlreadyAdded.insert(DirectChild.get());
MaybeFused = DirectChild;
} else {
assert(AnyChange &&
"Need changed flag for be consistent with actual change");
}
// Top-down recursion: If the outermost loop has been fused, their nested
// bands might be fusable now as well.
isl::schedule InnerFused = visit(MaybeFused, SubRemainingDeps);
// Reconstruct the sequence, with some of the children fused.
if (Result.is_null())
Result = InnerFused;
else
Result = Result.sequence(InnerFused);
}
return Result;
}
};
} // namespace
bool polly::isBandMark(const isl::schedule_node &Node) {
return isMark(Node) &&
isLoopAttr(Node.as<isl::schedule_node_mark>().get_id());
}
BandAttr *polly::getBandAttr(isl::schedule_node MarkOrBand) {
MarkOrBand = moveToBandMark(MarkOrBand);
if (!isMark(MarkOrBand))
return nullptr;
return getLoopAttr(MarkOrBand.as<isl::schedule_node_mark>().get_id());
}
isl::schedule polly::hoistExtensionNodes(isl::schedule Sched) {
// If there is no extension node in the first place, return the original
// schedule tree.
if (!containsExtensionNode(Sched))
return Sched;
// Build options can anchor schedule nodes, such that the schedule tree cannot
// be modified anymore. Therefore, apply build options after the tree has been
// created.
CollectASTBuildOptions Collector;
Collector.visit(Sched);
// Rewrite the schedule tree without extension nodes.
ExtensionNodeRewriter Rewriter;
isl::schedule NewSched = Rewriter.visitSchedule(Sched);
// Reapply the AST build options. The rewriter must not change the iteration
// order of bands. Any other node type is ignored.
ApplyASTBuildOptions Applicator(Collector.ASTBuildOptions);
NewSched = Applicator.visitSchedule(NewSched);
return NewSched;
}
isl::schedule polly::applyFullUnroll(isl::schedule_node BandToUnroll) {
isl::ctx Ctx = BandToUnroll.ctx();
// Remove the loop's mark, the loop will disappear anyway.
BandToUnroll = removeMark(BandToUnroll);
assert(isBandWithSingleLoop(BandToUnroll));
isl::multi_union_pw_aff PartialSched = isl::manage(
isl_schedule_node_band_get_partial_schedule(BandToUnroll.get()));
assert(unsignedFromIslSize(PartialSched.dim(isl::dim::out)) == 1u &&
"Can only unroll a single dimension");
isl::union_pw_aff PartialSchedUAff = PartialSched.at(0);
isl::union_set Domain = BandToUnroll.get_domain();
PartialSchedUAff = PartialSchedUAff.intersect_domain(Domain);
isl::union_map PartialSchedUMap =
isl::union_map::from(isl::union_pw_multi_aff(PartialSchedUAff));
// Enumerator only the scatter elements.
isl::union_set ScatterList = PartialSchedUMap.range();
// Enumerate all loop iterations.
// TODO: Diagnose if not enumerable or depends on a parameter.
SmallVector<isl::point, 16> Elts;
ScatterList.foreach_point([&Elts](isl::point P) -> isl::stat {
Elts.push_back(P);
return isl::stat::ok();
});
// Don't assume that foreach_point returns in execution order.
llvm::sort(Elts, [](isl::point P1, isl::point P2) -> bool {
isl::val C1 = P1.get_coordinate_val(isl::dim::set, 0);
isl::val C2 = P2.get_coordinate_val(isl::dim::set, 0);
return C1.lt(C2);
});
// Convert the points to a sequence of filters.
isl::union_set_list List = isl::union_set_list(Ctx, Elts.size());
for (isl::point P : Elts) {
// Determine the domains that map this scatter element.
isl::union_set DomainFilter = PartialSchedUMap.intersect_range(P).domain();
List = List.add(DomainFilter);
}
// Replace original band with unrolled sequence.
isl::schedule_node Body =
isl::manage(isl_schedule_node_delete(BandToUnroll.release()));
Body = Body.insert_sequence(List);
return Body.get_schedule();
}
isl::schedule polly::applyPartialUnroll(isl::schedule_node BandToUnroll,
int Factor) {
assert(Factor > 0 && "Positive unroll factor required");
isl::ctx Ctx = BandToUnroll.ctx();
// Remove the mark, save the attribute for later use.
BandAttr *Attr;
BandToUnroll = removeMark(BandToUnroll, Attr);
assert(isBandWithSingleLoop(BandToUnroll));
isl::multi_union_pw_aff PartialSched = isl::manage(
isl_schedule_node_band_get_partial_schedule(BandToUnroll.get()));
// { Stmt[] -> [x] }
isl::union_pw_aff PartialSchedUAff = PartialSched.at(0);
// Here we assume the schedule stride is one and starts with 0, which is not
// necessarily the case.
isl::union_pw_aff StridedPartialSchedUAff =
isl::union_pw_aff::empty(PartialSchedUAff.get_space());
isl::val ValFactor{Ctx, Factor};
PartialSchedUAff.foreach_pw_aff([&StridedPartialSchedUAff,
&ValFactor](isl::pw_aff PwAff) -> isl::stat {
isl::space Space = PwAff.get_space();
isl::set Universe = isl::set::universe(Space.domain());
isl::pw_aff AffFactor{Universe, ValFactor};
isl::pw_aff DivSchedAff = PwAff.div(AffFactor).floor().mul(AffFactor);
StridedPartialSchedUAff = StridedPartialSchedUAff.union_add(DivSchedAff);
return isl::stat::ok();
});
isl::union_set_list List = isl::union_set_list(Ctx, Factor);
for (auto i : seq<int>(0, Factor)) {
// { Stmt[] -> [x] }
isl::union_map UMap =
isl::union_map::from(isl::union_pw_multi_aff(PartialSchedUAff));
// { [x] }
isl::basic_set Divisible = isDivisibleBySet(Ctx, Factor, i);
// { Stmt[] }
isl::union_set UnrolledDomain = UMap.intersect_range(Divisible).domain();
List = List.add(UnrolledDomain);
}
isl::schedule_node Body =
isl::manage(isl_schedule_node_delete(BandToUnroll.copy()));
Body = Body.insert_sequence(List);
isl::schedule_node NewLoop =
Body.insert_partial_schedule(StridedPartialSchedUAff);
MDNode *FollowupMD = nullptr;
if (Attr && Attr->Metadata)
FollowupMD =
findOptionalNodeOperand(Attr->Metadata, LLVMLoopUnrollFollowupUnrolled);
isl::id NewBandId = createGeneratedLoopAttr(Ctx, FollowupMD);
if (!NewBandId.is_null())
NewLoop = insertMark(NewLoop, NewBandId);
return NewLoop.get_schedule();
}
isl::set polly::getPartialTilePrefixes(isl::set ScheduleRange,
int VectorWidth) {
unsigned Dims = unsignedFromIslSize(ScheduleRange.tuple_dim());
assert(Dims >= 1);
isl::set LoopPrefixes =
ScheduleRange.drop_constraints_involving_dims(isl::dim::set, Dims - 1, 1);
auto ExtentPrefixes = addExtentConstraints(LoopPrefixes, VectorWidth);
isl::set BadPrefixes = ExtentPrefixes.subtract(ScheduleRange);
BadPrefixes = BadPrefixes.project_out(isl::dim::set, Dims - 1, 1);
LoopPrefixes = LoopPrefixes.project_out(isl::dim::set, Dims - 1, 1);
return LoopPrefixes.subtract(BadPrefixes);
}
isl::union_set polly::getIsolateOptions(isl::set IsolateDomain,
unsigned OutDimsNum) {
unsigned Dims = unsignedFromIslSize(IsolateDomain.tuple_dim());
assert(OutDimsNum <= Dims &&
"The isl::set IsolateDomain is used to describe the range of schedule "
"dimensions values, which should be isolated. Consequently, the "
"number of its dimensions should be greater than or equal to the "
"number of the schedule dimensions.");
isl::map IsolateRelation = isl::map::from_domain(IsolateDomain);
IsolateRelation = IsolateRelation.move_dims(isl::dim::out, 0, isl::dim::in,
Dims - OutDimsNum, OutDimsNum);
isl::set IsolateOption = IsolateRelation.wrap();
isl::id Id = isl::id::alloc(IsolateOption.ctx(), "isolate", nullptr);
IsolateOption = IsolateOption.set_tuple_id(Id);
return isl::union_set(IsolateOption);
}
isl::union_set polly::getDimOptions(isl::ctx Ctx, const char *Option) {
isl::space Space(Ctx, 0, 1);
auto DimOption = isl::set::universe(Space);
auto Id = isl::id::alloc(Ctx, Option, nullptr);
DimOption = DimOption.set_tuple_id(Id);
return isl::union_set(DimOption);
}
isl::schedule_node polly::tileNode(isl::schedule_node Node,
const char *Identifier,
ArrayRef<int> TileSizes,
int DefaultTileSize) {
auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
auto Dims = Space.dim(isl::dim::set);
auto Sizes = isl::multi_val::zero(Space);
std::string IdentifierString(Identifier);
for (unsigned i : rangeIslSize(0, Dims)) {
unsigned tileSize = i < TileSizes.size() ? TileSizes[i] : DefaultTileSize;
Sizes = Sizes.set_val(i, isl::val(Node.ctx(), tileSize));
}
auto TileLoopMarkerStr = IdentifierString + " - Tiles";
auto TileLoopMarker = isl::id::alloc(Node.ctx(), TileLoopMarkerStr, nullptr);
Node = Node.insert_mark(TileLoopMarker);
Node = Node.child(0);
Node =
isl::manage(isl_schedule_node_band_tile(Node.release(), Sizes.release()));
Node = Node.child(0);
auto PointLoopMarkerStr = IdentifierString + " - Points";
auto PointLoopMarker =
isl::id::alloc(Node.ctx(), PointLoopMarkerStr, nullptr);
Node = Node.insert_mark(PointLoopMarker);
return Node.child(0);
}
isl::schedule_node polly::applyRegisterTiling(isl::schedule_node Node,
ArrayRef<int> TileSizes,
int DefaultTileSize) {
Node = tileNode(Node, "Register tiling", TileSizes, DefaultTileSize);
auto Ctx = Node.ctx();
return Node.as<isl::schedule_node_band>().set_ast_build_options(
isl::union_set(Ctx, "{unroll[x]}"));
}
/// Find statements and sub-loops in (possibly nested) sequences.
static void
collectFissionableStmts(isl::schedule_node Node,
SmallVectorImpl<isl::schedule_node> &ScheduleStmts) {
if (isBand(Node) || isLeaf(Node)) {
ScheduleStmts.push_back(Node);
return;
}
if (Node.has_children()) {
isl::schedule_node C = Node.first_child();
while (true) {
collectFissionableStmts(C, ScheduleStmts);
if (!C.has_next_sibling())
break;
C = C.next_sibling();
}
}
}
isl::schedule polly::applyMaxFission(isl::schedule_node BandToFission) {
isl::ctx Ctx = BandToFission.ctx();
BandToFission = removeMark(BandToFission);
isl::schedule_node BandBody = BandToFission.child(0);
SmallVector<isl::schedule_node> FissionableStmts;
collectFissionableStmts(BandBody, FissionableStmts);
size_t N = FissionableStmts.size();
// Collect the domain for each of the statements that will get their own loop.
isl::union_set_list DomList = isl::union_set_list(Ctx, N);
for (size_t i = 0; i < N; ++i) {
isl::schedule_node BodyPart = FissionableStmts[i];
DomList = DomList.add(BodyPart.get_domain());
}
// Apply the fission by copying the entire loop, but inserting a filter for
// the statement domains for each fissioned loop.
isl::schedule_node Fissioned = BandToFission.insert_sequence(DomList);
return Fissioned.get_schedule();
}
isl::schedule polly::applyGreedyFusion(isl::schedule Sched,
const isl::union_map &Deps) {
LLVM_DEBUG(dbgs() << "Greedy loop fusion\n");
GreedyFusionRewriter Rewriter;
isl::schedule Result = Rewriter.visit(Sched, Deps);
if (!Rewriter.AnyChange) {
LLVM_DEBUG(dbgs() << "Found nothing to fuse\n");
return Sched;
}
// GreedyFusionRewriter due to working loop-by-loop, bands with multiple loops
// may have been split into multiple bands.
return collapseBands(Result);
}
|