Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/Dialect/AIE/IR/AIEDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1433,11 +1433,22 @@ LogicalResult PacketFlowOp::verify() {
if (body.empty())
return emitOpError("should have non-empty body");

int numSources = 0, numDests = 0;
for (auto &ops : body.front()) {
if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
return ops.emitOpError("cannot be contained in a PacketFlow op");
if (isa<PacketSourceOp>(ops))
++numSources;
if (isa<PacketDestOp>(ops))
++numDests;
}

if (numSources != 1)
return emitOpError("must have exactly one aie.packet_source (got ")
<< numSources << ")";
if (numDests < 1)
return emitOpError("must have at least one aie.packet_dest");

return success();
}

Expand Down
9 changes: 8 additions & 1 deletion lib/Dialect/AIE/Transforms/AIECreatePathFindFlows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,19 @@ AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
TileOp srcTile, destTile;
TileID srcCoords, destCoords;

// Pass 1: extract source (order-independent: dest may appear before source)
for (Operation &Op : b.getOperations()) {
if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
srcTile = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
srcPort = pktSource.port();
srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
} else if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
}
}
if (!srcTile)
return pktFlowOp.emitOpError("packet_flow has no packet_source");
// Pass 2: process each destination using the source extracted above
for (Operation &Op : b.getOperations()) {
if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
destTile = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
destPort = pktDest.port();
destCoords = {destTile.colIndex(), destTile.rowIndex()};
Expand Down
10 changes: 9 additions & 1 deletion lib/Dialect/AIE/Transforms/AIEPathFinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,20 @@ LogicalResult DynamicTileAnalysis::runAnalysis(DeviceOp &device) {
Port srcPort, dstPort;
TileOp srcTile, dstTile;
TileID srcCoords, dstCoords;
// Pass 1: extract source (order-independent: dest may appear before source)
for (Operation &Op : b.getOperations()) {
if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
srcTile = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
srcPort = pktSource.port();
srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
} else if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
}
}
if (!srcTile)
return pktFlowOp.emitOpError("packet_flow has no packet_source");

// Pass 2: process each destination using the source extracted above
for (Operation &Op : b.getOperations()) {
if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
dstTile = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
dstPort = pktDest.port();
dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
Expand Down
56 changes: 56 additions & 0 deletions test/create-packet-flows/badpacket_flow_source_count.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===- badpacket_flow_source_count.mlir ------------------------*- MLIR -*-===//
//
// This file is licensed 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
//
// (c) Copyright 2026 Advanced Micro Devices, Inc.
//
//===----------------------------------------------------------------------===//

// RUN: aie-opt --verify-diagnostics --split-input-file %s

// Regression tests for issue #2583 verifier gap:
// PacketFlowOp must have exactly one packet_source and at least one
// packet_dest. Verify that the verifier rejects zero-source and
// multiple-source flows.

// Test 1: zero sources — must be rejected by verifier.
module {
aie.device(xcvc1902) {
%t11 = aie.tile(1, 1)
// expected-error@+1 {{must have exactly one aie.packet_source (got 0)}}
aie.packet_flow(0x0) {
aie.packet_dest<%t11, Core : 0>
}
}
}

// -----

// Test 2: multiple sources — must be rejected by verifier.
module {
aie.device(xcvc1902) {
%t11 = aie.tile(1, 1)
%t12 = aie.tile(1, 2)
// expected-error@+1 {{must have exactly one aie.packet_source (got 2)}}
aie.packet_flow(0x0) {
aie.packet_source<%t11, West : 0>
aie.packet_source<%t12, West : 0>
aie.packet_dest<%t11, Core : 0>
}
}
}

// -----

// Test 3: zero dests — must be rejected by verifier.
module {
aie.device(xcvc1902) {
%t11 = aie.tile(1, 1)
// expected-error@+1 {{must have at least one aie.packet_dest}}
aie.packet_flow(0x0) {
aie.packet_source<%t11, West : 0>
}
}
}
68 changes: 68 additions & 0 deletions test/create-packet-flows/packet_flow_dest_before_source.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//===- packet_flow_dest_before_source.mlir ---------------------*- MLIR -*-===//
//
// This file is licensed 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
//
// (c) Copyright 2026 Advanced Micro Devices, Inc.
//
//===----------------------------------------------------------------------===//

// RUN: aie-opt --aie-create-pathfinder-flows %s | FileCheck %s

// Regression test for issue #2583:
// AIEPathfinderPass assumed packet_source always precedes packet_dest in a
// block. MLIR provides no such ordering guarantee. This test verifies that
// placing aie.packet_dest before aie.packet_source produces correct output.

// Test 1: Single dest before source (basic reproducer from issue #2583)
// CHECK-LABEL: aie.device(xcvc1902)
// CHECK: %[[T11:.*]] = aie.tile(1, 1)
// CHECK: %[[SW11:.*]] = aie.switchbox(%[[T11]]) {
// CHECK: %[[AMSEL0:.*]] = aie.amsel<0> (0)
// CHECK: aie.masterset(Core : 0, %[[AMSEL0]])
// CHECK: aie.packet_rules(West : 0) {
// CHECK: aie.rule(31, 0, %[[AMSEL0]])
// CHECK: }
// CHECK: }

// Test 2: Multiple dests before source (fanout with dest-first ordering)
// CHECK: %[[T12:.*]] = aie.tile(1, 2)
// CHECK: %[[SW12:.*]] = aie.switchbox(%[[T12]]) {
// CHECK: aie.masterset(Core : 0,
// CHECK: aie.masterset(Core : 1,
// CHECK: aie.packet_rules(West : 0) {

// Test 3: keep_pkt_header with dest-first ordering
// CHECK: %[[T13:.*]] = aie.tile(1, 3)
// CHECK: %[[SW13:.*]] = aie.switchbox(%[[T13]]) {
// CHECK: aie.masterset(Core : 0,
// CHECK: aie.packet_rules(West : 0) {

module @packet_flow_dest_before_source {
aie.device(xcvc1902) {

// Test 1: single dest before source
%t11 = aie.tile(1, 1)
aie.packet_flow(0x0) {
aie.packet_dest<%t11, Core : 0>
aie.packet_source<%t11, West : 0>
}

// Test 2: multiple dests before source
%t12 = aie.tile(1, 2)
aie.packet_flow(0x0) {
aie.packet_dest<%t12, Core : 0>
aie.packet_dest<%t12, Core : 1>
aie.packet_source<%t12, West : 0>
}

// Test 3: keep_pkt_header with dest-first ordering
%t13 = aie.tile(1, 3)
aie.packet_flow(0x0) {
aie.packet_dest<%t13, Core : 0>
aie.packet_source<%t13, West : 0>
} {keep_pkt_header = true}

}
}
Loading