20#include "mlir/Dialect/UB/IR/UBOps.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/Support/Debug.h"
23#include "llvm/Support/MathExtras.h"
27#include <unordered_map>
28#include <unordered_set>
30#define DEBUG_TYPE "aie2p-shuffle-planner"
38using Lanes = SmallVector<int32_t, 64>;
41constexpr unsigned kInf = std::numeric_limits<unsigned>::max() / 4;
44constexpr unsigned kMaxRegisterDepth = 3;
45constexpr unsigned kMaxRounds = 3;
46constexpr unsigned kMaxRoundsOnePair = 4;
48constexpr unsigned kMaxRoundStates = 50000;
49constexpr unsigned kMaxSolveSteps = 200000;
51constexpr unsigned kMaxRoundRegisters = 8;
53std::string bytesOf(ArrayRef<int32_t> v) {
54 return std::string(
reinterpret_cast<const char *
>(v.data()),
55 v.size() *
sizeof(int32_t));
63 SmallVector<uint8_t, 64> src;
66SmallVector<LaneMode> getLaneModes(
unsigned elemBits) {
67 SmallVector<LaneMode> modes;
68 unsigned eb = elemBits / 8, lanes = 512 / elemBits;
69 for (uint32_t m = 0; m <= getMaxEnumValForShuffleMode(); ++m) {
70 auto mode = symbolizeShuffleMode(m);
75 bool wholeLanes =
true;
76 for (
unsigned i = 0; i < lanes && wholeLanes; ++i) {
77 unsigned first = perm[i * eb];
78 wholeLanes = first % eb == 0;
79 for (
unsigned b = 1; b < eb && wholeLanes; ++b)
80 wholeLanes = perm[i * eb + b] == first + b;
81 lm.src.push_back(first / eb);
86 bool copiesLhs =
true, copiesRhs =
true;
87 for (
unsigned i = 0; i < lanes; ++i) {
88 copiesLhs &= lm.src[i] == i;
89 copiesRhs &= lm.src[i] == lanes + i;
91 if (copiesLhs || copiesRhs)
93 modes.push_back(std::move(lm));
97 llvm::stable_sort(modes, [](
const LaneMode &a,
const LaneMode &b) {
106class RegisterSearch {
109 RegisterSearch(
const ShuffleGather &gather, ArrayRef<ShuffleMode> only = {},
110 unsigned maxRounds = kMaxRounds)
111 : g(gather), lanes(512 / gather.
elemBits),
112 modes(getLaneModes(gather.
elemBits)), maxRounds(maxRounds) {
114 llvm::erase_if(modes, [&](
const LaneMode &lm) {
115 return !llvm::is_contained(only, lm.mode);
117 for (
unsigned k = 0; k < g.sourceLanes.size(); ++k) {
118 if (g.sourceIsSplat[k]) {
119 sources.push_back(splat(g.sourceOffset(k)));
122 int64_t n = g.sourceLanes[k];
123 for (int64_t c = 0; c * lanes < std::max<int64_t>(n, 1); ++c) {
125 for (
unsigned j = 0; j < lanes && c * lanes + j < n; ++j)
126 l[j] = g.sourceOffset(k) + c * lanes + j;
127 Node node{Node::Source};
130 sources.push_back(intern(l, node));
133 int64_t m = g.target.size();
134 for (int64_t r = 0; r * lanes < m; ++r) {
136 for (
unsigned j = 0; j < lanes && r * lanes + j < m; ++j)
137 t[j] = g.target[r * lanes + j];
138 targets.push_back(t);
141 for (
unsigned i = 0; i < modes.size(); ++i) {
143 if (!modes[i].twoInput || a.
hi)
145 for (
unsigned j = 0; j < modes.size(); ++j) {
149 loHi.push_back({i, j});
154 std::optional<ShufflePlan> searchRounds();
155 std::optional<ShufflePlan> searchPerRegister();
157 SmallVector<std::pair<ShuffleMode, ShuffleMode>> modePairs()
const {
158 SmallVector<std::pair<ShuffleMode, ShuffleMode>> pairs;
159 for (
auto [lo, hi] : loHi)
160 pairs.push_back({modes[lo].mode, modes[hi].mode});
165 unsigned lowerBound()
const;
173 bool matches(ArrayRef<int32_t> have, ArrayRef<int32_t> want)
const {
174 for (
unsigned i = 0; i < lanes; ++i)
175 if (want[i] >= 0 && have[i] != want[i])
179 static bool isAny(ArrayRef<int32_t> spec) {
180 return llvm::all_of(spec, [](int32_t l) {
return l < 0; });
183 static std::optional<int32_t> singleLabel(ArrayRef<int32_t> spec) {
184 std::optional<int32_t> label;
185 for (int32_t l : spec) {
188 if (label && *label != l)
194 bool isSplatSourceLabel(int32_t label)
const {
195 for (
unsigned k = 0; k < g.sourceLanes.size(); ++k)
196 if (g.sourceIsSplat[k] && g.sourceOffset(k) == label)
200 bool canSplat(int32_t label)
const {
201 return g.canSplat || isSplatSourceLabel(label);
204 unsigned intern(ArrayRef<int32_t> l,
const Node &node) {
205 auto [it, inserted] = index.try_emplace(bytesOf(l), registers.size());
207 registers.push_back({Lanes(l.begin(), l.end()), node});
210 unsigned splat(int32_t label) {
211 Node node{Node::Splat};
213 return intern(Lanes(lanes, label), node);
215 Lanes applyLanes(
unsigned modeIdx, ArrayRef<int32_t> lhs,
216 ArrayRef<int32_t> rhs)
const {
217 const LaneMode &lm = modes[modeIdx];
219 for (
unsigned i = 0; i < lanes; ++i) {
220 unsigned s = lm.src[i];
221 out[i] = s < lanes ? lhs[s] : rhs[s - lanes];
225 unsigned apply(
unsigned modeIdx,
unsigned lhs,
unsigned rhs) {
226 const LaneMode &lm = modes[modeIdx];
227 Lanes out = applyLanes(modeIdx, registers[lhs].lanes, registers[rhs].lanes);
228 Node node{Node::Shuffle};
231 node.rhs = lm.twoInput ? rhs : lhs;
232 return intern(out, node);
236 bool invert(
unsigned modeIdx, ArrayRef<int32_t> spec, Lanes &lhs,
238 const LaneMode &lm = modes[modeIdx];
239 lhs.assign(lanes, -1);
240 rhs.assign(lanes, -1);
241 for (
unsigned i = 0; i < lanes; ++i) {
244 unsigned s = lm.src[i];
245 int32_t &slot = s < lanes ? lhs[s] : rhs[s - lanes];
246 if (slot >= 0 && slot != spec[i])
260 unsigned cost = kInf;
262 SmallVector<ShuffleMode, 4> newModes;
263 enum { None, Existing, Splat, Mode } kind = None;
266 unsigned modeIdx = 0;
274 bool isBetterThan(
const Choice &other)
const {
275 return std::make_tuple(cost, -
static_cast<int>(info), newModes.size()) <
276 std::make_tuple(other.cost, -
static_cast<int>(other.info),
277 other.newModes.size());
280 const Choice &solve(ArrayRef<int32_t> spec,
unsigned depth);
281 unsigned build(ArrayRef<int32_t> spec,
unsigned depth);
285 SmallVector<LaneMode> modes;
287 SmallVector<std::pair<unsigned, unsigned>> loHi;
288 std::vector<Register> registers;
289 std::unordered_map<std::string, unsigned> index;
290 SmallVector<unsigned> sources;
291 SmallVector<Lanes, 4> targets;
293 SmallVector<unsigned> built;
294 SmallVector<ShuffleMode> usedModes;
295 std::unordered_map<std::string, Choice> memo;
299unsigned RegisterSearch::lowerBound()
const {
301 for (
const Lanes &t : targets)
302 if (!isAny(t) && llvm::none_of(sources, [&](
unsigned s) {
303 return registers[s].node.kind == Node::Source &&
304 matches(registers[s].lanes, t);
312ShufflePlan RegisterSearch::extract(ArrayRef<int> results)
const {
314 DenseMap<unsigned, int> newIndex;
315 std::function<int(
unsigned)> visit = [&](
unsigned r) ->
int {
316 if (
auto it = newIndex.find(r); it != newIndex.end())
318 Node node = registers[r].node;
319 if (node.kind == Node::Shuffle) {
320 node.lhs = visit(node.lhs);
321 node.rhs = visit(node.rhs);
323 if (node.kind != Node::Source)
325 plan.
nodes.push_back(node);
326 return newIndex[r] = plan.
nodes.size() - 1;
328 for (
int r : results)
329 plan.
results.push_back(r < 0 ? -1 : visit(r));
333std::optional<ShufflePlan> RegisterSearch::searchRounds() {
334 unsigned n = llvm::PowerOf2Ceil(std::max(sources.size(), targets.size()));
335 if (n > kMaxRoundRegisters)
337 unsigned bits = llvm::Log2_32(n);
340 using State = SmallVector<unsigned, 8>;
341 SmallVector<std::function<State(
const State &)>> rounds;
342 for (
unsigned mi = 0; mi < modes.size(); ++mi) {
343 if (modes[mi].twoInput)
345 rounds.push_back([
this, mi](
const State &s) {
348 t.push_back(apply(mi, r, r));
352 for (
auto [lo, hi] : loHi) {
354 for (
unsigned mi : {lo, hi})
355 rounds.push_back([
this, mi](
const State &s) {
358 t.push_back(apply(mi, r, r));
361 for (
unsigned b = 0; b < bits; ++b) {
362 unsigned bit = 1u << b;
364 rounds.push_back([
this, lo, hi, bit](
const State &s) {
366 for (
unsigned i = 0; i < s.size(); ++i)
368 t[i] = apply(lo, s[i], s[i | bit]);
369 t[i | bit] = apply(hi, s[i], s[i | bit]);
374 rounds.push_back([
this, lo, hi, bit](
const State &s) {
376 for (
unsigned i = 0; i < s.size(); ++i)
377 t.push_back(apply(i & bit ? hi : lo, s[i], s[i]));
384 auto resultsOf = [&](
const State &s) -> std::optional<SmallVector<int>> {
385 SmallVector<int> results;
386 for (
const Lanes &t : targets) {
388 results.push_back(-1);
391 auto it = llvm::find_if(
392 s, [&](
unsigned r) {
return matches(registers[r].lanes, t); });
395 results.push_back(*it);
401 for (
unsigned i = 0; i < n; ++i)
402 start.push_back(sources[i % sources.size()]);
403 std::optional<ShufflePlan> best;
406 SmallVector<ShuffleMode> used;
407 for (
const Node &node : p.nodes)
408 if (node.kind == Node::Shuffle && !llvm::is_contained(used, node.mode))
409 used.push_back(node.mode);
412 auto consider = [&](
const State &s) {
413 if (
auto results = resultsOf(s)) {
415 if (!best || plan.
cost < best->cost ||
416 (plan.
cost == best->cost && numModes(plan) < numModes(*best)))
417 best = std::move(plan);
424 std::unordered_set<std::string> seen{
425 bytesOf(SmallVector<int32_t>(start.begin(), start.end()))};
426 std::vector<State> frontier{start};
428 for (
unsigned depth = 1; depth <= maxRounds && !best; ++depth) {
429 std::vector<State> next;
430 for (
const State &s : frontier) {
431 for (
auto &round : rounds) {
432 if (++states > kMaxRoundStates)
435 if (!seen.insert(bytesOf(SmallVector<int32_t>(t.begin(), t.end())))
439 if (depth < maxRounds)
440 next.push_back(std::move(t));
443 frontier = std::move(next);
448static unsigned countLabels(ArrayRef<int32_t> lanes) {
449 SmallVector<int32_t, 64> labels;
450 for (int32_t l : lanes)
451 if (l >= 0 && !llvm::is_contained(labels, l))
453 return labels.size();
458const RegisterSearch::Choice &RegisterSearch::solve(ArrayRef<int32_t> spec,
460 std::string key = bytesOf(spec);
461 key.push_back(
static_cast<char>(depth));
462 if (
auto it = memo.find(key); it != memo.end())
466 for (
unsigned r : built)
467 if (matches(registers[r].lanes, spec)) {
469 best.kind = Choice::Existing;
471 best.result = registers[r].lanes;
475 if (
auto label = singleLabel(spec); label && canSplat(*label)) {
478 best.kind = Choice::Splat;
480 best.result.assign(lanes, *label);
482 if (depth > 0 && best.cost > 0) {
484 for (
unsigned mi = 0; mi < modes.size(); ++mi) {
485 if (++steps > kMaxSolveSteps)
487 if (!invert(mi, spec, lhs, rhs))
491 unsigned sub = std::min(depth - 1, best.cost - 1);
493 auto consider = [&](
const Choice &l,
const Choice *r) {
494 if (l.cost >= kInf || (r && r->cost >= kInf))
497 c.cost = 1 + l.cost + (r ? r->cost : 0);
498 c.kind = Choice::Mode;
504 c.result = applyLanes(mi, l.result, r ? r->result : l.result);
505 c.info = l.info + (r ? r->info : 0) + countLabels(c.result);
506 c.newModes = l.newModes;
508 for (ShuffleMode m : r->newModes)
509 if (!llvm::is_contained(c.newModes, m))
510 c.newModes.push_back(m);
511 if (!llvm::is_contained(usedModes, modes[mi].mode) &&
512 !llvm::is_contained(c.newModes, modes[mi].mode))
513 c.newModes.push_back(modes[mi].mode);
514 if (c.isBetterThan(best))
517 bool lhsAny = isAny(lhs), rhsAny = isAny(rhs);
518 if (!modes[mi].twoInput || rhsAny) {
519 Choice l = solve(lhs, sub);
521 consider(l,
nullptr);
525 Choice r = solve(rhs, sub);
527 consider(r,
nullptr);
533 for (
unsigned i = 0; i < lanes && agree; ++i) {
536 agree = both[i] < 0 || both[i] == rhs[i];
540 Lanes l = lhs, r = rhs;
542 Choice m = solve(both, sub);
543 consider(m,
nullptr);
547 Choice l = solve(lhs, sub);
548 if (1 + l.cost <= best.cost) {
549 Choice r = solve(rhs, sub);
554 return memo[key] = std::move(best);
558unsigned RegisterSearch::build(ArrayRef<int32_t> spec,
unsigned depth) {
559 std::string key = bytesOf(spec);
560 key.push_back(
static_cast<char>(depth));
561 Choice choice = memo.at(key);
563 switch (choice.kind) {
564 case Choice::Existing:
567 r = splat(choice.label);
570 unsigned lhs = build(choice.lhs, choice.sub);
571 unsigned rhs = choice.merged ? lhs : build(choice.rhs, choice.sub);
572 r = apply(choice.modeIdx, lhs, rhs);
573 if (!llvm::is_contained(usedModes, modes[choice.modeIdx].mode))
574 usedModes.push_back(modes[choice.modeIdx].mode);
578 llvm_unreachable(
"building a register without a plan");
580 if (!llvm::is_contained(built, r))
585std::optional<ShufflePlan> RegisterSearch::searchPerRegister() {
586 for (
unsigned s : sources)
587 if (registers[s].node.kind == Node::Source)
589 SmallVector<int> results;
590 for (
const Lanes &t : targets) {
592 results.push_back(-1);
601 while (solve(t, depth).cost > 1 && depth < kMaxRegisterDepth)
603 if (solve(t, depth).cost >= kInf)
605 results.push_back(build(t, depth));
607 return extract(results);
614 for (
unsigned i = 0; i < k; ++i)
622 for (
unsigned k = 0; k <
sourceLanes.size(); ++k) {
631 unsigned w = g.
elemBits, lanes = 512 / w;
632 int64_t m = g.
target.size();
633 int64_t half = std::min<int64_t>(256 / w, m);
636 [&](int32_t label) -> std::optional<std::pair<unsigned, int64_t>> {
637 for (
unsigned k = 0; k < g.
sourceLanes.size(); ++k) {
640 return std::make_pair(k, lane);
647 auto isRun = [&](int64_t begin, int64_t end, int64_t align,
648 std::optional<int64_t> *start =
nullptr) {
649 std::optional<std::pair<unsigned, int64_t>> origin;
650 for (int64_t i = begin; i < end; ++i) {
653 auto at = laneOf(g.
target[i]);
656 int64_t first = at->second - (i - begin);
657 if (first < 0 || first % align != 0 ||
658 (origin && *origin != std::make_pair(at->first, first)))
660 origin = {at->first, first};
663 *start = origin->second;
668 for (int64_t begin = 0; begin < m; begin += lanes) {
669 int64_t end = std::min<int64_t>(begin + lanes, m);
671 bool repeated =
false;
672 for (int64_t prev = 0; prev < begin && !repeated; prev += lanes)
673 repeated = std::equal(g.
target.begin() + begin, g.
target.begin() + end,
677 SmallVector<int32_t> labels;
679 for (int64_t i = begin; i < end; ++i)
682 if (!llvm::is_contained(labels, g.
target[i]))
683 labels.push_back(g.
target[i]);
688 if (labels.size() == 1) {
695 unsigned crossings = 0;
696 for (int64_t c = begin; c < end && moves; c += half) {
697 std::optional<int64_t> start;
698 moves = isRun(c, std::min(c + half, end), half, &start);
699 if (moves && start && m >= lanes &&
700 (c - begin) / half % 2 != *start / half % 2)
708 if (isRun(begin, end, 1)) {
714 unsigned misplaced = 0;
715 SmallVector<unsigned> from;
716 for (int64_t i = begin; i < end; ++i) {
719 auto at = laneOf(g.
target[i]);
720 if (!at || at->second != i) {
724 if (!llvm::is_contained(from, at->first))
725 from.push_back(at->first);
727 if (misplaced == 0 && from.size() <= 2) {
732 cost += std::min<unsigned>(used + labels.size(),
733 from.size() <= 1 ? 2 * misplaced : ~0u);
742 SmallVector<std::array<int32_t, 64>> regs;
743 for (
const Node &node : plan.
nodes) {
744 std::array<int32_t, 64> bytes;
750 for (
unsigned j = 0; j < lanes; ++j) {
751 int64_t lane = node.chunk * lanes + j;
753 for (
unsigned b = 0; b < eb; ++b)
754 bytes[j * eb + b] = (g.
sourceOffset(node.source) + lane) * eb + b;
759 bool fromSplat =
false;
760 for (
unsigned k = 0; k < g.
sourceLanes.size(); ++k)
762 if (node.label < 0 || node.label >= g.
numLabels() ||
765 for (
unsigned i = 0; i < 64; ++i)
766 bytes[i] = node.label * eb + i % eb;
769 case Node::Shuffle: {
770 if (node.lhs < 0 || node.rhs < 0 ||
771 static_cast<size_t>(std::max(node.lhs, node.rhs)) >= regs.size())
774 for (
unsigned i = 0; i < 64; ++i)
775 bytes[i] = perm[i] < 64 ? regs[node.lhs][perm[i]]
776 : regs[node.rhs][perm[i] - 64];
780 regs.push_back(bytes);
782 int64_t m = g.
target.size();
783 if (
static_cast<int64_t
>(plan.
results.size()) * lanes < m)
785 for (int64_t i = 0; i < m; ++i) {
788 int r = plan.
results[i / lanes];
789 if (r < 0 ||
static_cast<size_t>(r) >= regs.size())
791 for (
unsigned b = 0; b < eb; ++b)
792 if (regs[r][(i % lanes) * eb + b] !=
793 g.
target[i] * int32_t(eb) + int32_t(b))
800 SmallVector<ShuffleMode> used;
802 if (node.kind ==
Node::Shuffle && !llvm::is_contained(used, node.mode))
803 used.push_back(node.mode);
807const SmallVector<ShufflePlan, 2> &
809 auto [it, inserted] = cache.try_emplace(g.
key());
812 SmallVector<ShufflePlan, 2> found;
815 auto keep = [&](std::optional<ShufflePlan> p, StringRef what) {
817 llvm::dbgs() << what <<
": " << p->cost <<
" instructions, modes";
818 for (ShuffleMode m : p->modes())
819 llvm::dbgs() <<
" " << stringifyShuffleMode(m);
820 llvm::dbgs() <<
"\n";
823 found.push_back(std::move(*p));
825 RegisterSearch search(g);
826 keep(search.searchPerRegister(),
"per-register search");
827 if (found.empty() || found.front().cost > search.lowerBound())
828 keep(RegisterSearch(g).searchRounds(),
"round search");
831 for (
auto [lo, hi] : search.modePairs()) {
832 SmallVector<ShuffleMode> pair{lo, hi};
833 keep(RegisterSearch(g, pair, kMaxRoundsOnePair).searchRounds(),
834 "round search with one pair");
835 keep(RegisterSearch(g, pair).searchPerRegister(),
836 "per-register search with one pair");
839 SmallVector<unsigned> kept;
840 for (
unsigned i = 0; i < found.size(); ++i) {
841 SmallVector<ShuffleMode> mi = found[i].modes();
842 bool dominated =
false;
843 for (
unsigned j = 0; j < found.size() && !dominated; ++j) {
846 SmallVector<ShuffleMode> mj = found[j].modes();
847 bool fewerModes = llvm::all_of(
848 mj, [&](ShuffleMode m) {
return llvm::is_contained(mi, m); });
849 bool sameModes = fewerModes && mj.size() == mi.size();
850 bool sameCost = found[j].cost == found[i].cost;
851 dominated = found[j].cost <= found[i].cost && fewerModes &&
852 (!sameModes || !sameCost || j < i);
857 SmallVector<ShufflePlan, 2> &plans = it->second;
858 for (
unsigned i : kept)
859 plans.push_back(std::move(found[i]));
875std::optional<ShuffleGather> gatherOf(
const ShuffleRequest &req,
bool fold,
876 SmallVector<Value> &leaves) {
878 Type elemTy = resTy.getElementType();
879 if (resTy.getRank() != 1 || !elemTy.isIntOrFloat())
881 unsigned w = elemTy.getIntOrFloatBitWidth();
882 if (w != 8 && w != 16 && w != 32 && w != 64)
884 int64_t m = resTy.getNumElements();
886 if (m * w > 512 && (m * w % 512 != 0 || !llvm::isPowerOf2_64(m * w / 512)))
891 g.
canSplat = elemTy.isBF16() || elemTy.isF32() ||
892 elemTy.isSignlessInteger(8) || elemTy.isSignlessInteger(16) ||
893 elemTy.isSignlessInteger(32);
897 std::function<std::optional<SmallVector<std::pair<int, int64_t>>>(
898 Value, Operation *, unsigned)>
899 lanesOf = [&](Value v, Operation *user,
unsigned depth)
900 -> std::optional<SmallVector<std::pair<int, int64_t>>> {
901 auto vTy = cast<VectorType>(v.getType());
902 if (vTy.getRank() != 1)
904 int64_t n = vTy.getNumElements();
905 SmallVector<std::pair<int, int64_t>> lanes;
906 if (v.getDefiningOp<ub::PoisonOp>()) {
907 lanes.assign(n, {-1, 0});
910 auto producer = v.getDefiningOp<vector::ShuffleOp>();
911 if (fold && depth > 0 && producer &&
912 llvm::all_of(producer->getUsers(),
913 [&](Operation *u) { return u == user; })) {
914 auto a = lanesOf(producer.getV1(), producer, depth - 1);
915 auto b = lanesOf(producer.getV2(), producer, depth - 1);
918 int64_t n1 = a->size();
919 for (int64_t idx : producer.getMask())
920 lanes.push_back(idx < 0 ? std::make_pair(-1, int64_t(0))
921 : idx < n1 ? (*a)[idx]
925 if (n * w > 512 && n * w % 512 != 0)
927 int k = llvm::find(leaves, v) - leaves.begin();
928 if (k ==
static_cast<int>(leaves.size())) {
930 auto bcast = v.getDefiningOp<vector::BroadcastOp>();
932 !isa<VectorType>(bcast.getSourceType()));
935 for (int64_t i = 0; i < n; ++i)
936 lanes.push_back({k, i});
940 auto a = lanesOf(req.
v1, req.
user, 3);
941 auto b = lanesOf(req.
v2, req.
user, 3);
944 int64_t n1 = a->size();
945 for (int64_t idx : req.
mask) {
946 std::pair<int, int64_t> src = idx < 0
947 ? std::make_pair(-1, int64_t(0))
948 : (idx < n1 ? (*a)[idx] : (*b)[idx - n1]);
950 g.target.push_back(-1);
959bool isShuffleElemType(Type t,
unsigned bits) {
960 if (t.getIntOrFloatBitWidth() != bits)
962 return t.isSignlessInteger() || t.isBF16() || t.isF32();
965Value emitPlan(ConversionPatternRewriter &rewriter, Location loc,
966 const ShuffleGather &g,
const ShufflePlan &plan,
967 ArrayRef<Value> leaves, VectorType resTy) {
968 Type elemTy = resTy.getElementType();
970 int64_t lanes = 512 / w;
971 auto regTy = VectorType::get({lanes}, elemTy);
972 SmallVector<Value> vals;
973 for (
const Node &node : plan.
nodes) {
977 Value leaf = rewriter.getRemappedValue(leaves[node.source]);
984 SmallVector<int64_t> mask;
985 for (int64_t j = 0; j < lanes; ++j) {
986 int64_t lane = node.chunk * lanes + j;
987 mask.push_back(lane < n ? lane : vector::ShuffleOp::kPoisonIndex);
989 v = vector::ShuffleOp::create(rewriter, loc, leaf, leaf, mask);
994 for (
unsigned k = 0; k < g.
sourceLanes.size() && !scalar; ++k) {
999 scalar = rewriter.getRemappedValue(
1000 leaves[k].getDefiningOp<vector::BroadcastOp>().getSource());
1002 scalar = vector::ExtractOp::create(
1003 rewriter, loc, rewriter.getRemappedValue(leaves[k]), lane);
1006 v = aievec::BroadcastScalarOp::create(rewriter, loc, regTy, scalar);
1009 case Node::Shuffle: {
1011 Type opElemTy = isShuffleElemType(elemTy, mw)
1013 : Type(rewriter.getIntegerType(mw));
1014 auto opTy = VectorType::get({512 / mw}, opElemTy);
1015 auto cast = [&](Value x) -> Value {
1016 if (x.getType() == opTy)
1018 return vector::BitCastOp::create(rewriter, loc, opTy, x);
1020 Value lhs = cast(vals[node.lhs]);
1022 ? cast(vals[node.rhs])
1024 v = aievec::ShuffleOp::create(rewriter, loc, opTy, lhs, rhs, node.mode);
1026 v = vector::BitCastOp::create(rewriter, loc, regTy, v);
1033 SmallVector<Value> parts;
1035 parts.push_back(r >= 0 ? vals[r]
1036 : ub::PoisonOp::create(rewriter, loc, regTy));
1037 int64_t m = resTy.getNumElements();
1039 auto mask = llvm::to_vector(llvm::seq<int64_t>(0, m));
1040 return vector::ShuffleOp::create(rewriter, loc, parts[0], parts[0], mask);
1042 while (parts.size() > 1) {
1043 SmallVector<Value> next;
1044 for (
size_t i = 0; i < parts.size(); i += 2) {
1045 int64_t len = cast<VectorType>(parts[i].getType()).getNumElements();
1046 auto mask = llvm::to_vector(llvm::seq<int64_t>(0, 2 * len));
1047 next.push_back(vector::ShuffleOp::create(rewriter, loc, parts[i],
1048 parts[i + 1], mask));
1050 parts = std::move(next);
1055ShuffleRequest requestOf(vector::ShuffleOp op) {
1056 return {op.getV1(), op.getV2(), llvm::to_vector(op.getMask()),
1057 op.getResultVectorType(), op};
1061std::optional<ShuffleRequest> requestOf(vector::InterleaveOp op) {
1062 VectorType srcTy = op.getSourceVectorType();
1063 if (srcTy.getRank() != 1 || srcTy.isScalable())
1064 return std::nullopt;
1065 int64_t n = srcTy.getNumElements();
1066 SmallVector<int64_t> mask;
1067 for (int64_t i = 0; i < 2 * n; ++i)
1068 mask.push_back(i / 2 + (i % 2) * n);
1069 return ShuffleRequest{op.getLhs(), op.getRhs(), mask,
1070 op.getResultVectorType(), op};
1074std::optional<ShuffleRequest> requestOf(vector::DeinterleaveOp op,
1076 VectorType srcTy = op.getSourceVectorType();
1077 if (srcTy.getRank() != 1 || srcTy.isScalable())
1078 return std::nullopt;
1079 SmallVector<int64_t> mask;
1080 for (int64_t i = result; i < srcTy.getNumElements(); i += 2)
1082 return ShuffleRequest{op.getSource(), op.getSource(), mask,
1083 op.getResultVectorType(), op};
1086Value lower(ConversionPatternRewriter &rewriter, AIE2PShufflePlanner &planner,
1087 const ShuffleRequest &req,
const ShufflePlan &plan,
1088 const ShuffleGather &g, ArrayRef<Value> leaves) {
1090 emitPlan(rewriter, req.
user->getLoc(), g, plan, leaves, req.
resultType);
1092 llvm::dbgs() <<
"lowered with " << plan.
cost <<
" instructions, modes";
1093 for (ShuffleMode m : plan.
modes())
1094 llvm::dbgs() <<
" " << stringifyShuffleMode(m);
1095 llvm::dbgs() <<
"\n";
1102struct LowerStaticShuffleAIE2pPattern : OpConversionPattern<vector::ShuffleOp> {
1103 LowerStaticShuffleAIE2pPattern(MLIRContext *ctx,
1104 std::shared_ptr<AIE2PShufflePlanner> planner)
1105 : OpConversionPattern(ctx, 3), planner(std::move(planner)) {}
1108 matchAndRewrite(vector::ShuffleOp op, OpAdaptor adaptor,
1109 ConversionPatternRewriter &rewriter)
const override {
1110 ShuffleRequest req = requestOf(op);
1112 SmallVector<Value> leaves;
1113 std::optional<ShufflePlan> plan = planner->
planFor(req, &g, &leaves);
1115 return rewriter.notifyMatchFailure(op,
"no plan beats LLVM's lowering");
1116 rewriter.replaceOp(op, lower(rewriter, *planner, req, *plan, g, leaves));
1120 std::shared_ptr<AIE2PShufflePlanner> planner;
1123struct LowerInterleaveAIE2pPattern : OpConversionPattern<vector::InterleaveOp> {
1124 LowerInterleaveAIE2pPattern(MLIRContext *ctx,
1125 std::shared_ptr<AIE2PShufflePlanner> planner)
1126 : OpConversionPattern(ctx), planner(std::move(planner)) {}
1129 matchAndRewrite(vector::InterleaveOp op, OpAdaptor adaptor,
1130 ConversionPatternRewriter &rewriter)
const override {
1131 std::optional<ShuffleRequest> req = requestOf(op);
1133 SmallVector<Value> leaves;
1134 std::optional<ShufflePlan> plan;
1136 plan = planner->
planFor(*req, &g, &leaves);
1138 return rewriter.notifyMatchFailure(op,
"no plan beats LLVM's lowering");
1139 rewriter.replaceOp(op, lower(rewriter, *planner, *req, *plan, g, leaves));
1143 std::shared_ptr<AIE2PShufflePlanner> planner;
1148struct LowerDeinterleaveAIE2pPattern
1149 : OpConversionPattern<vector::DeinterleaveOp> {
1150 LowerDeinterleaveAIE2pPattern(MLIRContext *ctx,
1151 std::shared_ptr<AIE2PShufflePlanner> planner)
1152 : OpConversionPattern(ctx), planner(std::move(planner)) {}
1155 matchAndRewrite(vector::DeinterleaveOp op, OpAdaptor adaptor,
1156 ConversionPatternRewriter &rewriter)
const override {
1157 SmallVector<ShuffleRequest, 2> reqs;
1158 SmallVector<std::optional<ShufflePlan>, 2> plans;
1159 SmallVector<ShuffleGather, 2> gathers(2);
1160 SmallVector<SmallVector<Value>, 2> leaves(2);
1161 for (
unsigned r = 0; r < 2; ++r) {
1162 std::optional<ShuffleRequest> req = requestOf(op, r);
1165 reqs.push_back(*req);
1166 plans.push_back(planner->
planFor(*req, &gathers[r], &leaves[r]));
1168 if (!plans[0] && !plans[1])
1169 return rewriter.notifyMatchFailure(op,
"no plan beats LLVM's lowering");
1170 SmallVector<Value, 2> results;
1171 for (
unsigned r = 0; r < 2; ++r)
1172 results.push_back(plans[r]
1173 ? lower(rewriter, *planner, reqs[r], *plans[r],
1174 gathers[r], leaves[r])
1175 : vector::ShuffleOp::create(
1176 rewriter, op.getLoc(), adaptor.getSource(),
1177 adaptor.getSource(), reqs[r].mask)
1179 rewriter.replaceOp(op, results);
1183 std::shared_ptr<AIE2PShufflePlanner> planner;
1188struct LowerVectorTransposeToShuffleAIE2pPattern
1189 : OpConversionPattern<vector::TransposeOp> {
1190 using OpConversionPattern::OpConversionPattern;
1193 matchAndRewrite(vector::TransposeOp op, OpAdaptor adaptor,
1194 ConversionPatternRewriter &rewriter)
const override {
1195 VectorType srcTy = op.getSourceVectorType();
1196 VectorType resTy = op.getResultVectorType();
1197 if (srcTy.isScalable())
1199 ArrayRef<int64_t> perm = op.getPermutation();
1200 ArrayRef<int64_t> srcShape = srcTy.getShape();
1201 SmallVector<int64_t> srcStrides(srcShape.size(), 1);
1202 for (int64_t d = srcShape.size() - 2; d >= 0; --d)
1203 srcStrides[d] = srcStrides[d + 1] * srcShape[d + 1];
1204 int64_t n = srcTy.getNumElements();
1205 SmallVector<int64_t> mask;
1206 SmallVector<int64_t> idx(resTy.getRank(), 0);
1207 for (int64_t i = 0; i < n; ++i) {
1210 for (int64_t d = 0; d < resTy.getRank(); ++d)
1211 s += idx[d] * srcStrides[perm[d]];
1213 for (int64_t d = resTy.getRank() - 1; d >= 0; --d) {
1214 if (++idx[d] < resTy.getDimSize(d))
1219 Location loc = op.getLoc();
1220 auto flatTy = VectorType::get({n}, srcTy.getElementType());
1222 vector::ShapeCastOp::create(rewriter, loc, flatTy, adaptor.getVector());
1223 Value shuffled = vector::ShuffleOp::create(rewriter, loc, flat, flat, mask);
1224 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(op, resTy, shuffled);
1231std::optional<ShufflePlan>
1233 SmallVector<Value> *leaves) {
1234 SmallVector<Value> asIsLeaves;
1235 std::optional<ShuffleGather> asIs = gatherOf(req,
false, asIsLeaves);
1237 return std::nullopt;
1240 return std::nullopt;
1241 LLVM_DEBUG(llvm::dbgs() <<
"planning " << *req.
user
1242 <<
"\nLLVM estimate: " << llvmCost <<
"\n");
1248 unsigned fresh = llvm::count_if(p.modes(), [&](ShuffleMode m) {
1249 return !llvm::is_contained(usedModes, m);
1251 return std::make_tuple(p.cost + fresh, fresh, p.cost);
1253 std::optional<ShufflePlan> best;
1254 auto consider = [&](
const ShuffleGather &g, SmallVector<Value> &ls) {
1256 if (p.cost >= llvmCost || (best && score(p) >= score(*best)))
1265 SmallVector<Value> foldedLeaves;
1266 std::optional<ShuffleGather> folded =
1267 gatherOf(req,
true, foldedLeaves);
1268 if (folded && folded->key() != asIs->key())
1269 consider(*folded, foldedLeaves);
1270 consider(*asIs, asIsLeaves);
1275 for (ShuffleMode m : plan.
modes())
1276 if (!llvm::is_contained(usedModes, m))
1277 usedModes.push_back(m);
1281 RewritePatternSet &patterns, std::shared_ptr<AIE2PShufflePlanner> planner) {
1282 patterns.add<LowerStaticShuffleAIE2pPattern, LowerInterleaveAIE2pPattern,
1283 LowerDeinterleaveAIE2pPattern>(patterns.getContext(), planner);
1285 patterns.add<LowerVectorTransposeToShuffleAIE2pPattern>(patterns.getContext(),
1290 ConversionTarget &target, std::shared_ptr<AIE2PShufflePlanner> planner) {
1291 target.addDynamicallyLegalOp<vector::ShuffleOp>(
1292 [planner](vector::ShuffleOp op) {
1293 return !planner->planFor(requestOf(op)).has_value();
1295 target.addDynamicallyLegalOp<vector::InterleaveOp>(
1296 [planner](vector::InterleaveOp op) {
1297 std::optional<ShuffleRequest> req = requestOf(op);
1298 return !req || !planner->planFor(*req).has_value();
1300 target.addDynamicallyLegalOp<vector::DeinterleaveOp>(
1301 [planner](vector::DeinterleaveOp op) {
1302 for (
unsigned r = 0; r < 2; ++r) {
1303 std::optional<ShuffleRequest> req = requestOf(op, r);
1304 if (req && planner->planFor(*req))
std::optional< ShufflePlan > planFor(const ShuffleRequest &request, ShuffleGather *gather=nullptr, llvm::SmallVector< mlir::Value > *leaves=nullptr)
const llvm::SmallVector< ShufflePlan, 2 > & candidates(const ShuffleGather &gather)
void noteUsed(const ShufflePlan &plan)
void populateAIE2PShufflePlannerPatterns(mlir::RewritePatternSet &patterns, std::shared_ptr< AIE2PShufflePlanner > planner)
ShuffleModeShape getShuffleModeShape(ShuffleMode mode)
unsigned estimateLLVMShuffleCost(const ShuffleGather &gather)
void configureAIE2PShufflePlannerLegality(mlir::ConversionTarget &target, std::shared_ptr< AIE2PShufflePlanner > planner)
std::array< uint8_t, 64 > getShuffleModeBytePermutation(ShuffleMode mode)
bool simulateShufflePlan(const ShuffleGather &gather, const ShufflePlan &plan)
llvm::SmallVector< int64_t > sourceLanes
int32_t sourceOffset(unsigned k) const
llvm::SmallVector< int32_t > target
llvm::SmallVector< bool > sourceIsSplat
int32_t numLabels() const
llvm::SmallVector< int > results
llvm::SmallVector< Node > nodes
llvm::SmallVector< ShuffleMode > modes() const
llvm::SmallVector< int64_t > mask
mlir::VectorType resultType