Skip to content
Draft
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
640 changes: 640 additions & 0 deletions docs/2026-03-13-tpop-pipe-interface-spec-zh.md

Large diffs are not rendered by default.

249 changes: 249 additions & 0 deletions include/PTO/IR/PTOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3645,4 +3645,253 @@ def TPrintOp: PTO_TOp<"tprint", [
}];
}

//===----------------------------------------------------------------------===//
// TPUSH/TPOP Ring Buffer Communication Ops
//===----------------------------------------------------------------------===//

// --- Initialization ---

def InitializeL2G2LPipeOp : PTO_Op<"initialize_l2g2l_pipe", [
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Initialize a local-to-global-to-local pipe handle";
let description = [{
Called once at kernel startup. Binds a pipe whose producer writes through GM
and whose consumer exposes a local FIFO slot.

- gm_addr is required and must be a memref with gm address space.
- local_addr is optional; if omitted, plan memory allocates it.
- local_fifo_depth is optional; if present, it must be positive.
- dir_mask must be 1 (C2V) or 2 (V2C).
}];

let arguments = (ins
I8Attr:$dir_mask,
OptionalAttr<I8Attr>:$local_fifo_depth,
AnyType:$gm_addr,
Optional<AnyType>:$local_addr
);

let results = (outs PipeType:$pipe);
let hasVerifier = 1;

let assemblyFormat = [{
`{` `dir_mask` `=` $dir_mask
(`,` `local_fifo_depth` `=` $local_fifo_depth^)? `}`
`(` $gm_addr `:` type($gm_addr)
(`,` $local_addr^ `:` type($local_addr))? `)`
attr-dict `->` qualified(type($pipe))
}];
}

def InitializeL2LPipeOp : PTO_Op<"initialize_l2l_pipe", [
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Initialize a local-to-local pipe handle";
let description = [{
Called once at kernel startup. Binds a pipe whose producer and consumer
communicate through local FIFO storage.

- local_addr is optional; if omitted, plan memory allocates it.
- dir_mask must be 1 (C2V) or 2 (V2C).
}];

let arguments = (ins
I8Attr:$dir_mask,
Variadic<AnyType>:$local_addrs
);

let results = (outs PipeType:$pipe);
let hasVerifier = 1;

let assemblyFormat = [{
`{` `dir_mask` `=` $dir_mask `}`
(`(` $local_addrs^ `:` type($local_addrs) `)`)?
attr-dict `->` qualified(type($pipe))
}];
}

// --- Data Transfer: Push (producer, no DPS) ---

def TPushOp : PTO_TOp<"tpush", [
OpPipeInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Push tile data via unified pipe handle";

let arguments = (ins
PTODpsType:$tile,
PipeType:$pipe_handle
);

let results = (outs);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $tile `,` $pipe_handle `:` qualified(type($tile)) `,` qualified(type($pipe_handle)) `)`
attr-dict
}];

let extraClassDeclaration = [{
::mlir::pto::PIPE getPipe() {
auto isAddressSpace = [](Type ty, ::mlir::pto::AddressSpace target) -> bool {
if (auto tb = ::mlir::dyn_cast<::mlir::pto::TileBufType>(ty)) {
if (auto as = ::mlir::dyn_cast_or_null<::mlir::pto::AddressSpaceAttr>(
tb.getMemorySpace()))
return as.getAddressSpace() == target;
return false;
}
if (auto mr = ::mlir::dyn_cast<::mlir::MemRefType>(ty)) {
if (auto ms = mr.getMemorySpace()) {
if (auto as = ::mlir::dyn_cast<::mlir::pto::AddressSpaceAttr>(ms))
return as.getAddressSpace() == target;
}
return false;
}
return false;
};

auto pipeTy = ::mlir::dyn_cast<::mlir::pto::PipeType>(getPipeHandle().getType());
if (!pipeTy)
return ::mlir::pto::PIPE::PIPE_UNASSIGNED;

if (isAddressSpace(pipeTy.getSrcTileType(), ::mlir::pto::AddressSpace::ACC))
return ::mlir::pto::PIPE::PIPE_FIX;
if (isAddressSpace(pipeTy.getSrcTileType(), ::mlir::pto::AddressSpace::VEC))
return ::mlir::pto::PIPE::PIPE_MTE3;
return ::mlir::pto::PIPE::PIPE_UNASSIGNED;
}
}];
}

// --- Data Transfer: Frontend consumer ops ---

def TPopOp : PTO_Op<"tpop", [
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Wait for a consumer slot and return its slot id";

let arguments = (ins
PipeType:$pipe_handle
);

let results = (outs Index:$slot_id);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $pipe_handle `:` qualified(type($pipe_handle)) `)`
attr-dict `->` type($slot_id)
}];
}

def GetFifoTileOp : PTO_Op<"get_fifo_tile", [
ViewLikeOpInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Map a tpop slot id to the corresponding FIFO tile view";

let arguments = (ins
PipeType:$pipe_handle,
Index:$slot_id
);

let results = (outs PTODpsType:$tile);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $pipe_handle `,` $slot_id `:` qualified(type($pipe_handle)) `,` type($slot_id) `)`
attr-dict `->` qualified(type($tile))
}];

let extraClassDeclaration = [{
::mlir::Value getViewSource() { return getPipeHandle(); }
}];
}

def TFreeOp : PTO_Op<"tfree", [
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Release a consumer slot after the borrowed FIFO tile is no longer needed";

let arguments = (ins
PipeType:$pipe_handle,
Index:$slot_id
);

let results = (outs);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $pipe_handle `,` $slot_id `:` qualified(type($pipe_handle)) `,` type($slot_id) `)`
attr-dict
}];
}

// --- Data Transfer: Internal lowered consumer ops ---

def DeclareTileOp : PTO_Op<"declare_tile", [Pure]> {
let summary = "Declare a tile value whose address will be rebound later";

let results = (outs PTODpsType:$tile);

let assemblyFormat = [{
attr-dict `->` qualified(type($tile))
}];
}

def TPopInternalOp : PTO_TOp<"tpop_internal", [
PTO_DpsInitOpInterface,
OpPipeInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Lowered consumer pop that binds a declared tile to a FIFO slot";

let arguments = (ins
PTODpsType:$tile,
PipeType:$pipe_handle
);

let results = (outs);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $tile `,` $pipe_handle `:` qualified(type($tile)) `,` qualified(type($pipe_handle)) `)`
attr-dict
}];

let extraClassDeclaration = [{
::mlir::pto::PIPE getPipe() {
if (getPipeHandle().getDefiningOp<::mlir::pto::InitializeL2G2LPipeOp>())
return ::mlir::pto::PIPE::PIPE_MTE2;
if (getPipeHandle().getDefiningOp<::mlir::pto::InitializeL2LPipeOp>())
return ::mlir::pto::PIPE::PIPE_S;
return ::mlir::pto::PIPE::PIPE_UNASSIGNED;
}
::mlir::MutableOperandRange getDpsInitsMutable() { return getTileMutable(); }
}];
}

def TFreeInternalOp : PTO_TOp<"tfree_internal", [
OpPipeInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
]> {
let summary = "Lowered consumer slot release";

let arguments = (ins
PipeType:$pipe_handle
);

let results = (outs);
let hasVerifier = 1;

let assemblyFormat = [{
`(` $pipe_handle `:` qualified(type($pipe_handle)) `)`
attr-dict
}];

let extraClassDeclaration = [{
::mlir::pto::PIPE getPipe() { return ::mlir::pto::PIPE::PIPE_S; }
}];
}

#endif // MLIR_DIALECT_PTO_IR_PTOOPS
12 changes: 12 additions & 0 deletions include/PTO/IR/PTOTypeDefs.td
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,15 @@ def TileBufType : TypeDef<PTO_Dialect, "TileBuf"> {
int32_t getPadValueI32() const; // 0 null, 1 zero, 2 max, 3 min
}];
}

def PipeType : TypeDef<PTO_Dialect, "Pipe"> {
let mnemonic = "pipe";
let summary = "Pipe handle type for TPUSH/TPOP unified static schedule";
let parameters = (ins
"mlir::Type":$srcTileType,
"mlir::Type":$dstTileType
);
let assemblyFormat = [{
`<` $srcTileType `,` $dstTileType `>`
}];
}
4 changes: 3 additions & 1 deletion include/PTO/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum class PTOArch {
std::unique_ptr<Pass> createPTOHighDimLoweringPass();
std::unique_ptr<Pass> createPTOVFloopGatherPass();
std::unique_ptr<Pass> createLoweringSyncToPipePass();
std::unique_ptr<Pass> createPTOLowerTPopPass();

// Creates a pass for ...
std::unique_ptr<Pass> createPTOInsertSyncPass();
Expand All @@ -64,7 +65,8 @@ std::unique_ptr<Pass> createPTORemoveRedundantBarrierPass();
std::unique_ptr<Pass> createPTOViewToMemrefPass();
std::unique_ptr<mlir::Pass> createPTOInsertLoadStoreForMixCVPass();
std::unique_ptr<Pass> createInferPTOLayoutPass();
// Declare register function
std::unique_ptr<Pass> createPTOVerifyTFreePass();

void registerPTOPasses();

} // namespace pto
Expand Down
34 changes: 34 additions & 0 deletions include/PTO/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,38 @@ def PTOLoweringSyncToPipe : Pass<"pto-lowering-sync-to-pipe", "func::FuncOp"> {
];
}

def PTOLowerTPop : Pass<"pto-lower-tpop", "func::FuncOp"> {
let summary = "Lower slot-based tpop/get_fifo_tile/tfree to internal pipe consumer ops";
let description = [{
Rewrites frontend pipe-consumer IR into internal ops:
- `pto.tpop + pto.get_fifo_tile` -> `pto.declare_tile + pto.tpop_internal`
- `pto.tfree` -> `pto.tfree_internal`
Before lowering, verifies that each `pto.tpop` result has exactly one
matching `pto.get_fifo_tile` and exactly one explicit `pto.tfree`.
}];

let constructor = "mlir::pto::createPTOLowerTPopPass()";

let dependentDialects = [
"mlir::pto::PTODialect"
];
}

def PTOVerifyTFree : Pass<"pto-verify-tfree", "func::FuncOp"> {
let summary = "Verify lowered pto.tfree_internal placement for explicit tpop/tfree pairs";
let description = [{
For each lowered `pto.tpop_internal` in section.cube / section.vector, this
pass verifies that there is a matching explicit
`pto.tfree_internal(pipe_handle)` later in the same block, that the
borrowed tile is not used after that free, and that the same pipe does not
accumulate multiple outstanding pops before the matching free.
}];

let constructor = "mlir::pto::createPTOVerifyTFreePass()";

let dependentDialects = [
"mlir::pto::PTODialect"
];
}

#endif // MLIR_DIALECT_PTO_PASSES
Loading