Skip to content

Commit adca50e

Browse files
Merge pull request #363 from KurrinQu/feat/tpush-tpop-dir-both-bind-tile-20260325
feat: converge dir-both and bind-tile tpush/tpop lowering
2 parents c5ad014 + cd537cb commit adca50e

9 files changed

Lines changed: 381 additions & 268 deletions

File tree

docs/designs/ptoas-tpush-tpop-design.md

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,16 @@ pto.tfree_from_aiv { split = 0 }
241241

242242
### 4.1 逻辑 pipe
243243

244-
本文中的逻辑 pipe”指一条单向通信通道
244+
本文中的逻辑 pipe”指一条通信通道
245245

246246
- C2V:Cube producer -> Vector consumer
247247
- V2C:Vector producer -> Cube consumer
248248

249-
`DIR_MASK=3` 表示前端一个同时包含 C2V 和 V2C 的初始化请求,在 PTOAS lowering 后拆成两条单向逻辑 pipe:
250-
251-
- 一条 `dir_mask = 1` 的 C2V pipe
252-
- 一条 `dir_mask = 2` 的 V2C pipe
249+
`DIR_MASK=3` 表示前端一个同时包含 C2V 和 V2C 的初始化请求。在 PTOAS lowering
250+
后,生成单条 `dir_mask = 3` 的 DIR_BOTH 内部 pipe,同时承载 C2V 和 V2C 双向
251+
通信。该 pipe 携带两个地址操作数:`local_addr`(C2V consumer buf)和
252+
`peer_local_addr`(V2C consumer buf)。下游 TPUSH/TPOP/TFREE 共享同一 pipe
253+
handle。
253254

254255
### 4.2 `split` 的角色
255256

@@ -288,7 +289,7 @@ pto.tfree_from_aiv { split = 0 }
288289
`SLOT_NUM``DIR_MASK` 固定决定:
289290

290291
- `DIR_MASK = 1``2``SLOT_NUM = 8`
291-
- `DIR_MASK = 3`:拆成两条单向 pipe,且每条 `SLOT_NUM = 4`
292+
- `DIR_MASK = 3`(DIR_BOTH):单条 pipe,`SLOT_NUM = 4`(每方向 4 slot,总缓冲 2 × 4 × SLOT_SIZE)
292293

293294
`SLOT_NUM` 不由 `split` 决定。
294295

@@ -306,13 +307,26 @@ pto.tfree_from_aiv { split = 0 }
306307

307308
用于 A2/A3 路径。
308309

310+
单向示例:
311+
309312
```mlir
310313
%pipe = pto.initialize_l2g2l_pipe {
311314
dir_mask = 1,
312315
slot_size = 512,
313316
slot_num = 8,
314317
local_slot_num = 8
315-
}(%gm_addr, %local_addr) -> !pto.pipe
318+
}(%gm_addr : i32, %local_addr : i32) -> !pto.pipe
319+
```
320+
321+
DIR_BOTH 示例:
322+
323+
```mlir
324+
%pipe = pto.initialize_l2g2l_pipe {
325+
dir_mask = 3,
326+
slot_size = 512,
327+
slot_num = 4,
328+
local_slot_num = 4
329+
}(%gm_addr : i32, %c2v_addr : i32, %v2c_addr : i32) -> !pto.pipe
316330
```
317331

318332
#### 必需属性
@@ -327,10 +341,10 @@ pto.tfree_from_aiv { split = 0 }
327341
-`initialize_l2g2l_pipe` 承载
328342
- 表示 GM 路径下 consumer 侧 local slot buffer 的槽数
329343
- 仅在通过 GM 传递时对底层 `TPipe` 模板参数有意义,不改变 GM FIFO 的 `slot_num`
330-
- 缺省值等于该内部单向 pipe 的 `slot_num`
344+
- 缺省值等于该内部 pipe 的 `slot_num`
331345
- 因此当前固定规则下:
332346
- `DIR_MASK=1/2` 直接 lowering 时,`local_slot_num = 8`
333-
- `DIR_MASK=3` 拆成两条单向 pipe 后,每条 `local_slot_num = 4`
347+
- `DIR_MASK=3` 单条 DIR_BOTH pipe,`local_slot_num = 4`
334348
- `flag_base`
335349
- 由 PTOAS flag 分配阶段填写
336350
- frontend lowering 阶段可以缺省
@@ -339,23 +353,36 @@ pto.tfree_from_aiv { split = 0 }
339353
#### 操作数
340354

341355
- `gm_addr`
342-
- `local_addr`
356+
- `local_addr`:C2V consumer buf(或单向时唯一方向的 consumer buf)
357+
- `peer_local_addr`(可选):V2C consumer buf,仅 `dir_mask = 3` 时出现
343358

344359
### 5.3 `pto.initialize_l2l_pipe`
345360

346361
用于 A5 路径。
347362

363+
单向示例:
364+
348365
```mlir
349366
%pipe = pto.initialize_l2l_pipe {
350367
dir_mask = 1,
351368
slot_size = 512,
352369
slot_num = 8
353-
}(%local_addr) -> !pto.pipe
370+
}(%local_addr : i32) -> !pto.pipe
371+
```
372+
373+
双向(DIR_BOTH)示例:
374+
375+
```mlir
376+
%pipe = pto.initialize_l2l_pipe {
377+
dir_mask = 3,
378+
slot_size = 1024,
379+
slot_num = 4
380+
}(%c2v_addr : i32, %v2c_addr : i32) -> !pto.pipe
354381
```
355382

356383
#### 必需属性
357384

358-
- `dir_mask`
385+
- `dir_mask`:合法值 `1`(C2V)、`2`(V2C)、`3`(DIR_BOTH)
359386
- `slot_size`
360387
- `slot_num`
361388

@@ -368,6 +395,11 @@ pto.tfree_from_aiv { split = 0 }
368395

369396
#### 操作数
370397

398+
- `local_addr`:C2V consumer buf(或单向时唯一方向的 consumer buf)
399+
- `peer_local_addr`(可选):V2C consumer buf,仅 `dir_mask = 3` 时出现
400+
401+
#### 操作数
402+
371403
- `local_addr`
372404

373405
### 5.4 `pto.tpush`
@@ -417,31 +449,30 @@ pto.tfree(%pipe) { split = 0 }
417449

418450
### 6.3 `DIR_MASK=3`
419451

420-
前端一个 init op 固定拆成两条内部 pipe:
452+
前端一个 init op 生成**单条** DIR_BOTH 内部 pipe:
421453

422-
- `%pipe_c2v``dir_mask = 1``slot_num = 4`
423-
- `%pipe_v2c``dir_mask = 2``slot_num = 4`
424-
425-
若 lowering 为 `initialize_l2g2l_pipe`,则两条内部 pipe 还满足:
426-
427-
- `%pipe_c2v``local_slot_num = 4`
428-
- `%pipe_v2c``local_slot_num = 4`
454+
- `%pipe``dir_mask = 3``slot_num = 4`
455+
- 若 lowering 为 `initialize_l2g2l_pipe``local_slot_num = 4`
429456

430457
地址选择规则:
431458

432-
- `%pipe_c2v` 使用 `C2V_CONSUMER_BUF`
433-
- `%pipe_v2c` 使用 `V2C_CONSUMER_BUF`
459+
- `local_addr` = `C2V_CONSUMER_BUF`
460+
- `peer_local_addr` = `V2C_CONSUMER_BUF`
461+
462+
`FrontendPipeHandles``c2vPipe``v2cPipe` 指向同一个 pipe Value。
434463

435464
### 6.4 前端数据传输 op 与内部 pipe 的绑定
436465

437466
绑定规则固定如下:
438467

439468
| 前端 op | 所在函数 | 方向 | 使用的内部 pipe |
440469
|---|---|---|---|
441-
| `tpush_to_aiv` | Cube | C2V | `dir_mask = 1` |
442-
| `tpop_from_aic` | Vector | C2V | `dir_mask = 1` |
443-
| `tfree_from_aic` | Vector | C2V | `dir_mask = 1` |
444-
| `tpush_to_aic` | Vector | V2C | `dir_mask = 2` |
470+
| `tpush_to_aiv` | Cube | C2V | `c2vPipe` |
471+
| `tpop_from_aic` | Vector | C2V | `c2vPipe` |
472+
| `tfree_from_aic` | Vector | C2V | `c2vPipe` |
473+
| `tpush_to_aic` | Vector | V2C | `v2cPipe` |
474+
475+
`DIR_MASK=3` 时,`c2vPipe``v2cPipe` 指向同一个 DIR_BOTH pipe,下游 TPUSH/TPOP/TFREE 只关心 pipe handle 是否存在,不关心是否是同一个。
445476
| `tpop_from_aiv` | Cube | V2C | `dir_mask = 2` |
446477
| `tfree_from_aiv` | Cube | V2C | `dir_mask = 2` |
447478

@@ -602,16 +633,17 @@ pass 在模块级按两步执行:
602633
其中第一步的实现方式是:
603634

604635
- 遍历模块内所有 `pto.initialize_l2l_pipe` / `pto.initialize_l2g2l_pipe`
605-
- 若其 `local_addr` 来自 `reserve_buffer`,则以“当前函数 + reserve 名字 + dir_mask”识别逻辑 pipe
606-
- 若其 `local_addr` 来自 `import_reserved_buffer`,则以“peer_func + reserve 名字 + dir_mask”识别逻辑 pipe
636+
- 对每条 init op 的每个地址操作数,以”函数 + reserve 名字 + 方向”构建 PipePeerKey 并归入逻辑 pipe 分组:
637+
- `dir_mask = 1/2`:只有 `local_addr`,方向即 `dir_mask`
638+
- `dir_mask = 3`(DIR_BOTH):一条 pipe 携带两个地址,分别归入两个逻辑方向——`local_addr` 归入 C2V(方向 1),`peer_local_addr` 归入 V2C(方向 2)
607639
- 将 peer 两侧引用到同一逻辑 pipe 的内部 init op 归并到同一组
608640
- 若某条 init 未显式提供 `flag_base`,则其 `local_addr` 必须来自 `reserve_buffer``import_reserved_buffer`
609641
- 对每个逻辑 pipe 分组,要求必须形成完整 peer init pair:恰好两条 init,且分别来自 peer 两侧函数;若 peer 信息不完整则直接报错
610642
- 在同一组内,若任一侧已显式提供 `flag_base`,则该值作为该组最终值;若两侧显式值冲突则报错
611643
- 若同组两侧都未显式提供 `flag_base`,则按默认规则回填:
612644
- 单向场景:`flag_base = 0`
613645
- 双向场景:C2V 组 `flag_base = 0`,V2C 组 `flag_base = 2`
614-
- 所谓“双向场景”,是指同一对 peer 函数之间同时存在 `dir_mask = 1``dir_mask = 2` 两个逻辑 pipe 分组
646+
- “双向场景”指同一对 peer 函数之间同时存在 C2V 和 V2C 两个逻辑 pipe 分组;DIR_BOTH 的一条物理 pipe 天然产生这两个分组
615647
- 完成分组决策后,将最终 `flag_base` 回填到该组内所有尚未显式填写的 init op,保证 peer 两侧一致
616648

617649
第二步的实现方式是:
@@ -663,16 +695,18 @@ pass 在模块级按两步执行:
663695

664696
### 8.3 双向场景
665697

666-
当前规划中,当 `DIR_MASK = 3` 时,可采用
698+
当前规划中,当 `DIR_MASK = 3`(DIR_BOTH)时,虽然物理上只有一条 pipe,但 resolve pass 将其拆为两个逻辑方向分别分配 `flag_base`
667699

668-
- C2V pipe`flag_base = 0`
669-
- V2C pipe`flag_base = 2`
700+
- C2V 方向`flag_base = 0`
701+
- V2C 方向`flag_base = 2`
670702

671703
因此双向固定占用两组逻辑 flag:
672704

673705
- C2V:`0` / `1`
674706
- V2C:`2` / `3`
675707

708+
对于单条 DIR_BOTH pipe,最终 `flag_base` 取 C2V 方向的值(`0`),底层 pto-isa `TPipe<flagBase, Direction::DIR_BOTH, ...>` 会自动管理两个方向的 flag 对。
709+
676710
### 8.4 与地址传播的关系
677711

678712
地址传播 pass 在识别出 `import_reserved_buffer``reserve_buffer` 的 peer 对应关系后,同时可以完成 peer pipe 的 `flag_base` 对齐。
@@ -758,10 +792,21 @@ EmitC 将以下内部 init op 映射到底层 `TPipe`:
758792
- `dir_mask`
759793
- `slot_size`
760794
- `slot_num`
761-
- `local_slot_num`
795+
- `local_slot_num`(仅 `initialize_l2g2l_pipe`
762796
- `flag_base`
763-
- `gm_addr`
797+
- `gm_addr`(仅 `initialize_l2g2l_pipe`
764798
- `local_addr`
799+
- `peer_local_addr`(仅 `dir_mask = 3` 时)
800+
801+
`dir_mask``Direction` 枚举的映射:
802+
803+
| `dir_mask` | `Direction` 令牌 |
804+
|---|---|
805+
| 1 | `Direction::DIR_C2V` |
806+
| 2 | `Direction::DIR_V2C` |
807+
| 3 | `Direction::DIR_BOTH` |
808+
809+
`dir_mask = 3` 时,EmitC 将 `local_addr` 作为 C2V consumer buf、`peer_local_addr` 作为 V2C consumer buf 传入 `TPipe` 构造函数。
765810

766811
其中:
767812

include/PTO/IR/PTOOps.td

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,8 @@ def InitializeL2G2LPipeOp : PTO_Op<"initialize_l2g2l_pipe", [
13821382
OptionalAttr<I32Attr>:$local_slot_num,
13831383
OptionalAttr<I32Attr>:$flag_base,
13841384
AnyType:$gm_addr,
1385-
AnyType:$local_addr
1385+
AnyType:$local_addr,
1386+
Optional<AnyType>:$peer_local_addr
13861387
);
13871388

13881389
let results = (outs PipeType:$pipe);
@@ -1395,7 +1396,8 @@ def InitializeL2G2LPipeOp : PTO_Op<"initialize_l2g2l_pipe", [
13951396
(`,` `local_slot_num` `=` $local_slot_num^)?
13961397
(`,` `flag_base` `=` $flag_base^)?
13971398
`}`
1398-
`(` $gm_addr `:` type($gm_addr) `,` $local_addr `:` type($local_addr) `)`
1399+
`(` $gm_addr `:` type($gm_addr) `,` $local_addr `:` type($local_addr)
1400+
(`,` $peer_local_addr^ `:` type($peer_local_addr))? `)`
13991401
attr-dict `->` qualified(type($pipe))
14001402
}];
14011403
}
@@ -1410,7 +1412,8 @@ def InitializeL2LPipeOp : PTO_Op<"initialize_l2l_pipe", [
14101412
I32Attr:$slot_size,
14111413
I32Attr:$slot_num,
14121414
OptionalAttr<I32Attr>:$flag_base,
1413-
AnyType:$local_addr
1415+
AnyType:$local_addr,
1416+
Optional<AnyType>:$peer_local_addr
14141417
);
14151418

14161419
let results = (outs PipeType:$pipe);
@@ -1422,7 +1425,8 @@ def InitializeL2LPipeOp : PTO_Op<"initialize_l2l_pipe", [
14221425
`slot_num` `=` $slot_num
14231426
(`,` `flag_base` `=` $flag_base^)?
14241427
`}`
1425-
`(` $local_addr `:` type($local_addr) `)`
1428+
`(` $local_addr `:` type($local_addr)
1429+
(`,` $peer_local_addr^ `:` type($peer_local_addr))? `)`
14261430
attr-dict `->` qualified(type($pipe))
14271431
}];
14281432
}
@@ -1488,6 +1492,22 @@ def DeclareTileOp : PTO_Op<"declare_tile", [Pure]> {
14881492
}];
14891493
}
14901494

1495+
def DeclareTileMemRefOp : PTO_Op<"declare_tile_memref", [Pure]> {
1496+
let summary = "Internal memref placeholder for a tile whose address is assigned later";
1497+
let description = [{
1498+
Internal lowering op used by PTOViewToMemref. This op does not allocate
1499+
storage; it only provides a memref-typed SSA handle so later passes can
1500+
attach tile metadata through pto.bind_tile before the address is filled by
1501+
pipe operations such as pto.tpop.
1502+
}];
1503+
1504+
let results = (outs AnyMemRef:$result);
1505+
1506+
let assemblyFormat = [{
1507+
attr-dict `->` qualified(type($result))
1508+
}];
1509+
}
1510+
14911511
def TPopOp : PTO_TOp<"tpop", [
14921512
PTO_DpsInitOpInterface,
14931513
OpPipeInterface,

lib/PTO/IR/PTO.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7838,8 +7838,8 @@ static LogicalResult verifyFrontendSplitOp(Operation *op,
78387838
static LogicalResult verifyPipeShape(Operation *op, int8_t dirMask, int32_t slotSize,
78397839
int32_t slotNum,
78407840
std::optional<int32_t> flagBase) {
7841-
if (dirMask != 1 && dirMask != 2)
7842-
return op->emitOpError("expects 'dir_mask' to be 1 or 2");
7841+
if (dirMask != 1 && dirMask != 2 && dirMask != 3)
7842+
return op->emitOpError("expects 'dir_mask' to be 1, 2, or 3");
78437843
if (slotSize <= 0)
78447844
return op->emitOpError("expects 'slot_size' to be greater than 0");
78457845
if (slotNum != 4 && slotNum != 8)
@@ -7917,15 +7917,26 @@ LogicalResult InitializeL2G2LPipeOp::verify() {
79177917
"expects 'local_slot_num' to be less than or equal to slot_num");
79187918
}
79197919

7920+
if (getDirMask() == 3 && !getPeerLocalAddr())
7921+
return emitOpError("expects 'peer_local_addr' when dir_mask is 3");
7922+
if (getDirMask() != 3 && getPeerLocalAddr())
7923+
return emitOpError("'peer_local_addr' is only allowed when dir_mask is 3");
79207924
return success();
79217925
}
79227926

79237927
LogicalResult InitializeL2LPipeOp::verify() {
7924-
return verifyPipeShape(getOperation(), getDirMask(), getSlotSize(),
7925-
getSlotNum(),
7926-
getFlagBaseAttr()
7927-
? std::optional<int32_t>(getFlagBaseAttr().getInt())
7928-
: std::nullopt);
7928+
if (failed(verifyPipeShape(getOperation(), getDirMask(), getSlotSize(),
7929+
getSlotNum(),
7930+
getFlagBaseAttr()
7931+
? std::optional<int32_t>(getFlagBaseAttr().getInt())
7932+
: std::nullopt)))
7933+
return failure();
7934+
7935+
if (getDirMask() == 3 && !getPeerLocalAddr())
7936+
return emitOpError("expects 'peer_local_addr' when dir_mask is 3");
7937+
if (getDirMask() != 3 && getPeerLocalAddr())
7938+
return emitOpError("'peer_local_addr' is only allowed when dir_mask is 3");
7939+
return success();
79297940
}
79307941

79317942
LogicalResult TPushOp::verify() {

0 commit comments

Comments
 (0)