Skip to content
Open
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
15 changes: 10 additions & 5 deletions docs/designs/ptoas-auto-sync-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@
| `scope` | `pto::AddressSpace` | 地址空间(GM/MAT/VEC/ACC/LEFT/RIGHT 等) |
| `baseAddresses` | `SmallVector<uint64_t>` | 已知的偏移列表,配合 `allocateSize` 做精确区间重叠 |
| `allocateSize` | `uint64_t` | 字节大小 |
| `addressProvenance` | `AddressProvenance` | 区分 root-relative 偏移、已知 absolute 物理地址、未知/动态 absolute 地址 |

`operator==`(第 111 行)要求 `baseAddresses`、`rootBuffer`、`scope`、
`allocateSize`、`baseBuffer` 全部相等才算同一缓冲;这是别名分析里的「严格相等」
判定,命中后可直接走依赖路径。
`operator==` 要求 `baseAddresses`、`rootBuffer`、`scope`、
`allocateSize`、`baseBuffer`、`addressProvenance` 全部相等才算同一缓冲;
这是别名分析里的「严格相等」判定,命中后可直接走依赖路径。

### 3.4 同步指令:`SyncOperation`

Expand Down Expand Up @@ -381,8 +382,12 @@ hazard 判定按三类关系展开:

1. `scope` 不同,直接不别名。
2. `GM` 先比较 root,必要时追踪 `realRoot`。
3. 地址和大小都已知,就做区间重叠。
4. 信息缺失(地址未知、size 未知),保守按“可能重叠”。
3. 两侧都是已知 absolute 本地物理地址时,即使 allocation SSA root 不同,
也按半开字节区间 `[base, base + size)` 判断重叠;区间端点相接不算 alias。
4. 任一侧是未知/动态 absolute 本地地址时,保守按“可能重叠”。
5. 其余 root-relative 地址保持原有 root/view 链分析;不同 root 下相同的相对
offset(例如 level-2 规划前的 `baseAddresses={0}`)不会被误当作同一物理地址。
6. size 未知,或 absolute view offset / 区间端点计算溢出时,保守按“可能重叠”。

这套规则在信息不足时会偏保守,但不会牺牲正确性。后续会继续补强动态 shape/offset 场景下的精细分析。

Expand Down
5 changes: 3 additions & 2 deletions docs/designs/ptoas-multi-buffer-explicit-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ lit test/lit/pto/multi_tile_prefetch_gss.pto
| **`MemAlias` 自动识别 const-slot disjoint**:不同常量 slot 的 access baseAddresses disjoint → 不发同步 | ✅ | `lib/PTO/Transforms/InsertSync/MemoryDependentAnalyzer.cpp` (复用既有 range-overlap 逻辑) |
| **GSS 别名链穿透 `pto.bind_tile` / `pto.slot_marker`** | ✅ | `lib/PTO/Transforms/Utils.cpp` (`getOperationAliasInfo`) |
| **InsertSync 处理 arith.select-on-memref**:保留为防御逻辑(Resolve 移到 sync 后实际不再产生此场景) | ✅ | `lib/PTO/Transforms/InsertSync/PTOIRTranslator.cpp` |
| **`GetEventIdNum` 按多 slot 推 N**:back-edge dep 双侧 `baseAddresses.size() == N` 时返回 N | ✅ | `lib/PTO/Transforms/InsertSync/InsertSyncAnalysis.cpp` |
| **`GetEventIdNum` 按多 slot 推 N**:仅当 back-edge 的每个 local dep pair 都是同 N 的 `AlternativeSlots` 且双侧 slot SSA 可恢复时返回 N;`Segments`、缺失 slot SSA 或混合依赖回退到单个静态 event | ✅ | `lib/PTO/Transforms/InsertSync/InsertSyncAnalysis.cpp` |
| **`SyncOperation` 携带 slot SSA**:`slotSSAExpr` + `slotCount` 字段,set/wait 各自记录自己一侧的 slot SSA | ✅ | `include/PTO/Transforms/InsertSync/SyncCommon.h`, `lib/PTO/Transforms/InsertSync/{SyncCommon,InsertSyncAnalysis}.cpp` |
| **dyn event-id codegen**:`CreateSetWaitOpForMultiBuffer` 发 `pto.set_flag_dyn` / `pto.wait_flag_dyn`,event id 由 N-way `arith.select` 链根据 `slot % N` 选自分配的 N 个静态 event id | ✅ | `lib/PTO/Transforms/InsertSync/SyncCodegen.cpp` |
| **`SyncEventIdAllocation` N event ids 分配**:复用既有 `eventIdNum > 1` 路径(已支持 N),自动给 set/wait 对分配 N 个 hardware event id | ✅ pre-existing | `lib/PTO/Transforms/InsertSync/SyncEventIdAllocation.cpp` |
Expand All @@ -480,11 +480,12 @@ lit test/lit/pto/multi_tile_prefetch_gss.pto
| **GSS 同 SSA 行为对齐 InsertSync**:`getMultiBufferEventIdInfo` 不再因 all-equal slot SSA 早退,对同 SSA 的 producer/consumer 也走 N dyn event id 路径;GSS 同 SSA 现在 emit 与 InsertSync 完全一样的 prefetch pipeline | ✅ | `lib/PTO/Transforms/GraphSyncSolver/SyncSolver.cpp` |
| **EmitC 穿透 `arith.select`-on-memref**:`PTOMaterializeTileHandles::computeExplicitAddress` 沿 select 两支递归求 i64 地址,配合 dyn slot 路径的 select 链;EmitC TASSIGN 拿到正确地址 | ✅ | `lib/PTO/Transforms/PTOMaterializeTileHandles.cpp` |
| **`multi_tile_get` lowering 防御**:`op->getOperand(0)` 取 source(绕开类型 cast),因为 alloc_multi_tile replace 之后 source 已经变成 memref,typed accessor `getSource()` 会断言 | ✅ | `lib/PTO/Transforms/PTOViewToMemref.cpp` |
| lit 测试(17 个):parse/print、verifier、const slot lowering、dyn slot lowering、N=3 / N=4 端到端、无 loop unroll、const-slot sync disjoint、dyn-slot sync 编译、GSS multi-buffer compile、prefetch dyn event-id (InsertSync)、prefetch GSS dyn flag、affine disjoint slots、const preload + dyn loop select、preload + loop set/wait、unknown slot GSS 保守降级 | ✅ | `test/lit/pto/multi_tile_*.pto` |
| lit 测试(21 个):parse/print、verifier、const slot lowering、dyn slot lowering、N=3 / N=4 端到端、无 loop unroll、const-slot sync disjoint、dyn-slot sync 编译、GSS multi-buffer compile、prefetch dyn event-id (InsertSync)、prefetch GSS dyn flag、affine disjoint slots、const preload + dyn loop select、preload + loop set/wait、unknown slot GSS 保守降级等 | ✅ | `test/lit/pto/multi_tile_*.pto` |

### 当前限制

- **affine 分析仅覆盖核心几种形态**:`compareSlotSSA` 当前能证 `iv % N` / `(iv ± c) % N` / 同 SSA / 纯常量;不能证 `(iv * c) % N`、跨函数 / 跨循环的 SSA 等价、非 `arith.remui` 包装的 slot 表达式。命中不到时退回 kUnknown / 保守 N dyn event id。
- **混合 back-edge 依赖回退到静态 event**:只要同一同步候选还包含 `Segments` 或其他不能与 slot lane 一一对应的 local dep pair,InsertSync 就使用单个静态 event 覆盖全部 hazard,而不是只按 multi-buffer slot 派发。
- **PlanMemory N>2 不复用 Stage1**:N>2 的兄弟 slot 不走 SPEC_LEVEL_1 "ping/pong 相邻摆放"优化,用更多内存。N=2 路径不变。
- 初版仅支持 `loc=vec` / `loc=mat` local memory。
- function argument / return 上的 `multi_tile_buf` 不支持(多 buffer 所有权限定在 ptoas 内)。
Expand Down
10 changes: 10 additions & 0 deletions include/PTO/IR/PTOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@ def GetTensorViewStrideOp : PTO_Op<"get_tensor_view_stride", [Pure]> {

def AllocTileOp : PTO_Op<"alloc_tile", [AttrSizedOperandSegments]> {
let summary = "Allocates a tile buffer (logical buffer).";
let description = [{
Without `addr`, this op declares a logical local allocation whose physical
address is selected by PTOAS memory planning.

Under `--pto-level=level3`, `addr` is required and denotes an absolute byte
address in the tile's local address space. Synchronization analysis compares
constant absolute ranges across distinct allocation SSA roots. If an
absolute address is dynamic, analysis conservatively treats it as possibly
aliasing any allocation in the same local address space.
}];

let arguments = (ins
Optional<I64>:$addr,
Expand Down
1 change: 1 addition & 0 deletions include/PTO/Transforms/InsertSync/PTOIRTranslator.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class PTOIRTranslator {
// 处理 View/Alias (MakeTensorView, Subview, Mov)
void UpdateAliasBufferInfo(Value result, Value source);
void UpdateConservativeAliasBufferInfo(Value result, Value source);
void UpdateConservativeSubviewAliasBufferInfo(Value result, Value source);
void UpdateMemrefSubViewAliasBufferInfo(memref::SubViewOp op);
void UpdateTileSubViewAliasBufferInfo(pto::SubViewOp op);
void UpdateSlotMarkerAliasBufferInfo(pto::SlotMarkerOp op);
Expand Down
51 changes: 43 additions & 8 deletions include/PTO/Transforms/InsertSync/SyncCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,43 @@ enum class TCoreType {
CUBE_OR_VECTOR,
CUBE_AND_VECTOR
};

/// Describes how BaseMemInfo::baseAddresses must be interpreted.
enum class AddressProvenance {
/// Addresses are byte offsets relative to rootBuffer.
RootRelative,
/// Addresses are known absolute byte addresses in the local address space.
KnownAbsolute,
/// The buffer has an absolute address, but its value is dynamic or unknown.
UnknownAbsolute
};

/// Describes what multiple entries in BaseMemInfo::baseAddresses represent.
enum class AddressListKind {
/// Each entry is the start of a discontiguous segment of one logical view.
Segments,
/// Each entry is an alternative physical slot for the same logical view.
AlternativeSlots
};

/// Meminfo of the target buffer
/// 用于追踪 Buffer 的别名和根节点
struct BaseMemInfo {
BaseMemInfo(
Value baseBuffer, Value rootBuffer, pto::AddressSpace scope,
SmallVector<uint64_t> baseAddresses, uint64_t allocateSize,
bool hasKnownPhysicalAddresses = false)
AddressProvenance addressProvenance = AddressProvenance::RootRelative,
AddressListKind addressListKind = AddressListKind::Segments,
bool hasAppliedReinterpret = false,
bool hasAppliedSubviewOffset = false,
bool hasInexactSubviewRange = false)
: baseBuffer(baseBuffer), rootBuffer(rootBuffer), scope(scope),
baseAddresses(std::move(baseAddresses)), allocateSize(allocateSize),
hasKnownPhysicalAddresses(hasKnownPhysicalAddresses) {}
addressProvenance(addressProvenance),
addressListKind(addressListKind),
hasAppliedReinterpret(hasAppliedReinterpret),
hasAppliedSubviewOffset(hasAppliedSubviewOffset),
hasInexactSubviewRange(hasInexactSubviewRange) {}

/// baseBuffer: 当前操作直接使用的 Buffer (可能是 View 或 Alias)
Value baseBuffer;
Expand All @@ -100,9 +126,14 @@ struct BaseMemInfo {
pto::AddressSpace scope;
SmallVector<uint64_t> baseAddresses; // 用于 Offset 分析
uint64_t allocateSize;
// PlanMemory materializes static local allocations as pointer_cast constants.
// This distinguishes their physical addresses from root-relative offsets.
bool hasKnownPhysicalAddresses;
AddressProvenance addressProvenance;
AddressListKind addressListKind;
/// True once a reinterpret_cast descriptor offset has been modeled.
bool hasAppliedReinterpret;
/// True once a subview offset has adjusted the physical address list.
bool hasAppliedSubviewOffset;
/// True when the range is a conservative parent envelope, not an exact view.
bool hasInexactSubviewRange;

bool areVectorEqual(const SmallVector<uint64_t>& vec1,
const SmallVector<uint64_t>& vec2) const {
Expand All @@ -117,7 +148,9 @@ struct BaseMemInfo {
if (!areVectorEqual(baseAddresses, other.baseAddresses)) return false;
if (rootBuffer != other.rootBuffer) return false;
if (scope != other.scope) return false;
if (hasKnownPhysicalAddresses != other.hasKnownPhysicalAddresses)
if (addressProvenance != other.addressProvenance)
return false;
if (addressListKind != other.addressListKind)
return false;
// allocateSize 和 baseBuffer 的严格相等性在某些别名分析中可能太强了,
// 但为了保持原有逻辑,先保留。重点是 rootBuffer 必须一致。
Expand All @@ -129,13 +162,15 @@ struct BaseMemInfo {
std::unique_ptr<BaseMemInfo> clone() const {
return std::make_unique<BaseMemInfo>(
baseBuffer, rootBuffer, scope, baseAddresses, allocateSize,
hasKnownPhysicalAddresses);
addressProvenance, addressListKind, hasAppliedReinterpret,
hasAppliedSubviewOffset, hasInexactSubviewRange);
}

std::unique_ptr<BaseMemInfo> clone(Value cloneBaseBuffer) const {
return std::make_unique<BaseMemInfo>(
cloneBaseBuffer, rootBuffer, scope, baseAddresses, allocateSize,
hasKnownPhysicalAddresses);
addressProvenance, addressListKind, hasAppliedReinterpret,
hasAppliedSubviewOffset, hasInexactSubviewRange);
}
};

Expand Down
Loading
Loading