177 if (
n <= 0 || outputNode < 0 || outputNode >=
n)
181 std::vector<char> live(
static_cast<size_t>(
n), 0);
183 std::vector<int> stack = {outputNode};
184 while (!stack.empty()) {
185 int u = stack.back();
187 if (u < 0 || u >=
n || live[
static_cast<size_t>(
u)])
continue;
188 live[
static_cast<size_t>(
u)] = 1;
190 if (nd.in0 >= 0) stack.push_back(nd.in0);
191 if (nd.in1 >= 0) stack.push_back(nd.in1);
192 if (nd.in2 >= 0) stack.push_back(nd.in2);
193 if (nd.in3 >= 0) stack.push_back(nd.in3);
194 if (nd.in4 >= 0) stack.push_back(nd.in4);
200 std::vector<int> order = topoOrder(g, live, outputNode);
201 constantFold(g, order);
204 order = topoOrder(g, live, outputNode);
207 std::vector<int> consumerCount(
static_cast<size_t>(
n), 0);
208 for (
int id : order) {
209 const auto &nd = g.
node(
id);
210 auto inc = [&](
int p) {
211 if (
p >= 0 && live[
static_cast<size_t>(
p)]) ++consumerCount[
static_cast<size_t>(
p)];
220 std::vector<char> visited(
static_cast<size_t>(
n), 0);
221 std::vector<int> groupOf(
static_cast<size_t>(
n), -1);
222 std::vector<char> absorbed;
223 std::vector<FusedGroup> groups;
227 for (
int idx =
int(order.size()) - 1;
idx >= 0; --
idx) {
228 const int id = order[
static_cast<size_t>(
idx)];
229 if (!live[
static_cast<size_t>(
id)] || visited[
static_cast<size_t>(
id)])
continue;
234 fillGroupAttrs(grp, g,
id);
235 std::vector<int> stack = {
id};
236 while (!stack.empty()) {
237 int u = stack.back();
239 if (!live[
static_cast<size_t>(
u)] || visited[
static_cast<size_t>(
u)])
continue;
240 visited[
static_cast<size_t>(
u)] = 1;
241 groupOf[
static_cast<size_t>(
u)] =
int(groups.size());
243 const auto &nd = g.
node(
u);
244 const int inputs[5] = {nd.
in0, nd.in1, nd.in2, nd.in3, nd.in4};
245 for (
int k = 0; k < 5; ++k) {
246 const int p = inputs[k];
247 if (
p >= 0 && live[
static_cast<size_t>(
p)] &&
249 consumerCount[
static_cast<size_t>(
p)] == 1) {
254 std::stable_sort(grp.
nodes.begin(), grp.
nodes.end(),
255 [&](
int a,
int b) { return positionOf(order, a) < positionOf(order, b); });
256 absorbed.push_back(0);
257 groups.push_back(std::move(grp));
261 for (
int id : order) {
262 if (!live[
static_cast<size_t>(
id)] || visited[
static_cast<size_t>(
id)])
continue;
265 visited[
static_cast<size_t>(
id)] = 1;
266 groupOf[
static_cast<size_t>(
id)] =
int(groups.size());
269 grp.kind = kindForOp(g.
node(
id).
type);
270 fillGroupAttrs(grp, g,
id);
271 grp.nodes.push_back(
id);
272 absorbed.push_back(0);
273 groups.push_back(std::move(grp));
277 for (
size_t gi = 0; gi < groups.size(); ++gi) {
284 std::vector<int> consumers;
285 for (
int id : order) {
286 const auto &nd = g.
node(
id);
287 const int inputs[5] = {nd.
in0, nd.in1, nd.in2, nd.in3, nd.in4};
288 for (
int k = 0; k < 5; ++k)
289 if (inputs[k] ==
m) consumers.push_back(
id);
291 if (consumers.size() != 1)
continue;
292 const int c = consumers[0];
293 const size_t egIdx =
static_cast<size_t>(groupOf[
static_cast<size_t>(
c)]);
294 if (egIdx >= groups.size() || absorbed[egIdx])
continue;
297 if (consumerCount[
static_cast<size_t>(
c)] != 1 &&
c != outputNode)
continue;
303 const auto &mn = g.
node(
m);
305 const auto &nd = g.
node(
u);
306 if (!isEpilogueOp(nd.type)) {
310 if (nd.in0 != prev) {
315 const int other = nd.
in1;
320 const auto &bn = g.
node(other);
321 const bool biasOk = (bn.rank == 1 && bn.dims[0] == mn.dims[mn.rank - 1]) ||
322 (bn.rank == 2 && bn.dims[0] == 1 &&
323 bn.dims[1] == mn.dims[mn.rank - 1]);
328 if (biasNode == -1) biasNode = other;
330 if (nd.in1 >= 0 && nd.type !=
OpType::Add && nd.in1 !=
u && nd.in1 != prev) {
343 for (
int u : eg.
nodes) groupOf[
static_cast<size_t>(
u)] = int(gi);
348 for (
size_t gi = 0; gi < groups.size(); ++gi) {
349 if (absorbed[gi])
continue;
351 std::vector<char> inGroup(
static_cast<size_t>(
n), 0);
352 for (
int u : grp.
nodes) inGroup[
static_cast<size_t>(
u)] = 1;
355 const auto &nd = g.
node(
u);
356 const int inputs[5] = {nd.
in0, nd.in1, nd.in2, nd.in3, nd.in4};
357 for (
int k = 0; k < 5; ++k) {
358 const int p = inputs[k];
359 if (
p < 0 || inGroup[
static_cast<size_t>(
p)])
continue;
366 std::stable_sort(grp.
nodes.begin(), grp.
nodes.end(),
367 [&](
int a,
int b) { return positionOf(order, a) < positionOf(order, b); });
374 opt.
nodeSlot.assign(
static_cast<size_t>(
n), -1);
377 std::vector<size_t> exec;
378 for (
size_t gi = 0; gi < groups.size(); ++gi)
379 if (!absorbed[gi]) exec.push_back(gi);
380 std::stable_sort(exec.begin(), exec.end(), [&](
size_t a,
size_t b) {
381 return positionOf(order, groups[a].outputNode) < positionOf(order, groups[b].outputNode);
385 std::vector<int> groupIndexByNode(
static_cast<size_t>(
n), -1);
386 for (
size_t gi = 0; gi < groups.size(); ++gi)
388 for (
int u : groups[gi].nodes) groupIndexByNode[
static_cast<size_t>(
u)] = int(gi);
393 std::vector<int> realProducer(
static_cast<size_t>(
n), -1);
394 for (
int id = 0;
id <
n; ++
id) {
400 realProducer[
static_cast<size_t>(
id)] = cur;
404 std::vector<int> lastUse(exec.size(), -1);
405 for (
size_t gi = 0; gi < exec.size(); ++gi) {
408 const int real = realProducer[
static_cast<size_t>(
p)];
409 const int prod = real >= 0 ? groupIndexByNode[
static_cast<size_t>(real)] : -1;
410 if (prod < 0)
continue;
411 const auto it = std::find(exec.begin(), exec.end(),
size_t(prod));
412 if (it == exec.end())
continue;
413 const size_t pIdx =
static_cast<size_t>(it - exec.begin());
414 lastUse[pIdx] = std::max(lastUse[pIdx],
int(gi));
416 if (grp.
outputNode == outputNode && lastUse[gi] < 0) lastUse[gi] = int(gi);
423 std::vector<Slot> slots;
424 std::vector<char> persistent(
static_cast<size_t>(
n), 0);
426 auto allocate = [&](
int nodeId,
int currentTime,
int freeAt) {
427 if (opt.
nodeSlot[
static_cast<size_t>(nodeId)] >= 0)
return;
428 const int size = g.
node(nodeId).
size;
430 for (
size_t s = 0;
s < slots.size(); ++
s) {
431 if (slots[
s].capacity >= size && slots[
s].freeAt <= currentTime) {
437 best = int(slots.size());
438 slots.push_back({size, freeAt});
440 slots[
static_cast<size_t>(best)].freeAt = freeAt;
442 opt.
nodeSlot[
static_cast<size_t>(nodeId)] = best;
446 for (
int id : order) {
447 const auto &nd = g.
node(
id);
449 persistent[
static_cast<size_t>(
id)] = 1;
450 allocate(
id, 0, 1 << 30);
453 persistent[
static_cast<size_t>(outputNode)] = 1;
456 for (
size_t gi : exec) {
460 const auto &nd = g.
node(out);
461 if (nd.in0 < 0)
throw eve::Exception(
"optimizeGraph: alias without input");
462 const int srcSlot = opt.
nodeSlot[
static_cast<size_t>(nd.in0)];
463 if (srcSlot < 0)
throw eve::Exception(
"optimizeGraph: alias input without slot");
464 opt.
nodeSlot[
static_cast<size_t>(out)] = srcSlot;
467 if (persistent[
static_cast<size_t>(out)]) {
468 allocate(out, 0, 1 << 30);
470 const size_t giPos =
static_cast<size_t>(std::find(exec.begin(), exec.end(), gi) -
472 allocate(out,
int(giPos), lastUse[giPos] + 1);
477 for (
size_t s = 0;
s < slots.size(); ++
s) opt.
slotSize[
s] = slots[
s].capacity;
478 for (
size_t s = 0;
s < slots.size(); ++
s)
482 std::vector<FusedGroup> finalGroups;
483 std::vector<int> oldToNew(groups.size(), -1);
484 for (
size_t i = 0; i < groups.size(); ++i)
486 oldToNew[i] = int(finalGroups.size());
487 finalGroups.push_back(std::move(groups[i]));
489 opt.
groups = std::move(finalGroups);
491 for (
size_t gi : exec) {
492 const int mapped = oldToNew[gi];
493 if (mapped >= 0) opt.
groupOrder.push_back(mapped);