XLU 操作清单
本页中的每个 opcode、地址、偏移、位位置和立即数,都是从
libtpu-0.0.40-cp314wheel 中的libtpu.so按字节精确读取的(BuildID md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 strip —— 完整 C++ 符号,nm -C可解析每个方法)。.text和.rodataVMA 等于其文件偏移;.data.rel.roVMA − 0x200000 = 文件偏移。其他 libtpu 构建会有所不同。
摘要
XLU(Cross-Lane Unit,跨 lane 单元)是 TensorCore 中跨 lane 移动数据的引擎,也是 otherwise per-lane 的向量结构里 lane i 可以读取 lane j 的唯一位置。它支撑 transpose、任意 lane permute、lane rotate,以及 cross-lane reductions(sum/max/min 及其 argmax/argmin index 变体,普通和分段两种)。所有这些都共享一个 bundle slot —— VectorExtended(VEX)slot,该 slot 也由 MXU 和 EUP 超越函数 使用 —— 因此编译器在把它们放入 bundle 之前,必须对它们进行打包、在每代的 XLU 数量之间做平衡,并估算其延迟。
本页是权威的 XLU 参考。它分为三部分,每部分都锚定到二进制:
操作清单。 同一硬件的两个视图。在 IR 层级,后端通过
LloRegionBuilder的 cross-lane factory 集(Vsetperm/Vxpose/Vpermute/Vrotate/Vsetspr/reduce family/…)发出高层LloOpcode;每个 factory 都是薄包装器,调用一个LloInstruction::CreateVector*op-constructor,后者再用固定 opcode 立即数调用LloInstruction::New(LloOpcode, operand-Span, …)。在线路层级,每代 encoder 将这些操作打包到 bundle 的 VEX slot 中,作为 Jellyfish (v2)VectorExtendedOpcode—— 一个紧凑的 35 值 protobuf enum{0..34},其上半区间{13..34}就是 XLU/transpose/permute/cross-lane 家族。清单表列出两个编号空间以及它们之间的桥接。组合流水线。
LloXluGraphOptimizer::Optimize运行五阶段 XLU op-graph 重写 ——ComputeCombinablePairs(融合相邻且相同的 XLU ops)→AssignXlu(贪心 least-loaded XLU-unit 平衡)→ReorderToShortenCriticalPath(按延迟加权的 list scheduler)→ReemitReorderedCombinedXluOperations(发出融合 ops,共享 permute/segment-pattern prologue)→AssignSourceBus(VEX source-bus 打包)。每个放置、排序和融合决策都以同一个 cost function 为键。成本。
CyclesAddedByXluOperation—— 五个阶段共同使用的单一 marginal-latency 表达式 —— 以及PreXluAssignmentLatencyTableedge 模型(XLU↔XLU edges 为ceil(base / xlu_count))。另外还有 transpose-fusion slot-fit 几何:SupportsVectorXpose/ divisibility /NumVexSlots三重 gate predicate,用于决定两个 transpose tiles 是否能折叠为一个受 VEX-slot 约束的 fused transpose。
对于重新实现,契约是:
- factory →
CreateVector*→New(LloOpcode, operand-count)表,以及 bundle encoder 打包到其中的 JFVectorExtendedOpcode {0..34}清单。 ProtoUtils::Is*classifier ranges(proto-enum 编号),用于 gate matmul / push-gains / transpose / RPU dispatch。- 组合流水线顺序和 fusion predicate(相同 metadata + tracker-ready + cost-bounded;control ops 从不 fuse)。
CyclesAddedByXluOperation闭式形式,以及ceil(base/xlu_count)XLU edge weight。- transpose slot-fit predicate 和
VxposeMode/ElementCount几何。
| 优化器 | xla::jellyfish::LloXluGraphOptimizer::Optimize @ 0x126cdb80 |
| Factory 集 | LloRegionBuilder::V{setperm,setspr,permute,permuteres,rotate,xpose,xposeres,packBf16,…} |
| Op-constructor | LloInstruction::CreateVector* → LloInstruction::New(LloOpcode, Span<LloValue*>, …) @ 0x1d4cf560 |
| 线路 enum | platforms_deepsea::jellyfish::isa::VectorExtendedOpcode —— 紧凑 {0..34}(descriptor @ 0x1fa1fd00) |
| Classifiers | ProtoUtils::Is{MatrixMultiply,PushGains,Transpose,Rpu} @ 0x1e875b{20,80,40,60} |
| 组合 | ComputeCombinablePairs @ 0x126d2480 |
| 单元分配 | AssignXlu @ 0x126d3100(贪心 least-loaded;要求 XlusPerTensorCore() > 1) |
| 重排 | ReorderToShortenCriticalPath @ 0x126d3460(per-XLU max-heap list scheduler) |
| 重新发出 | ReemitReorderedCombinedXluOperations @ 0x126d5460 |
| Source bus | AssignSourceBus @ 0x126d70e0(此构建中仅 Pufferfish) |
| 成本 | CyclesAddedByXluOperation @ 0x126d22a0;edge = ceil(base / xlu_count) |
| XLU 数量 | Target::XlusPerTensorCore() = VectorIsa.xlu_count = DWORD[Target+0x4b0] |
| 可信度 | CONFIRMED(字节锚定),除非行内另有说明 |
两个编号空间
重新实现者最先会困惑的是:一个 XLU operation 有两个不同身份,而二进制两者都会使用。
IR 身份是 LloOpcode —— 后端约 461 项 opcode 空间中的一个值(opcode_name table @ 0x21ccfef0)。这是 LloRegionBuilder factories 发出的内容,也是每个 optimizer pass switch 的对象。与 XLU 相关的 LloOpcode 有:0x36 kVectorPermute、0x3a kVectorRotate、0x3b kVectorBroadcastLane、0x8b kVectorSetPermutePattern、0x8c kVectorSetSegmentPattern、0xa6 kVectorTranspose、0xa7 kVectorTransposeBinary、reduce family 0xf5..0x101、0x150 kVectorPermuteResult、0x154 kVectorTransposeResult 和 0x155 kVectorTransposeClear。
线路身份是每代 bundle-slot opcode。在 Jellyfish (v2) 上,它是 VectorExtendedOpcode proto enum,一个紧凑的 35 值 enum {0..34},被切分为三类 op:matmul {0..6}、push-gains/latch {7..12},以及 XLU/transpose/permute/cross-lane 家族 {13..34}。matmul 和 latch 区段属于 MXU 和 matprep/latch slots;{13..34} 区段是本页记录的 XLU 家族。
两个空间在 bundle encode 时桥接:factory 发出的高层 LloOpcode,在打包 bundle 的 VEX slot 时被降低到其 VectorExtendedOpcode ordinal。下面的清单表列出两者。
注意 —— proto 值 vs encoded-bundle 字段。
VectorExtendedOpcodevalue(protobuf enum 编号,等价于规范 op name index)是ProtoUtils::Is*classifiers 所索引的值。encoder OR 到 VEX slot bits 中的 encoded-bundle field 在{13..34}范围内不同于 proto value(SET_PERMUTE/SET_SEGMENT这一对会移动 encode-side mapping)。本页记录的是 proto-value 编号 —— classifiers 和 LLO bridge 使用的那一个。encoded-field value 是 JF bundle 布局 的另一个问题。
XLU Op-Factory 集
目的
后端 materialize 的每个 XLU op 都会经过 LloRegionBuilder cross-lane factory 集。重新实现者需要每个 factory 发出的精确 opcode + operand-span,因为这就是随后整个 optimizer 调度、encoder 打包的 LLO word。
算法
每个 factory 的形状相同,按字节精确来自 Vsetperm @ 0x1d52ba20:
// LloRegionBuilder::Vsetperm(LloValue* in, SetPermuteMode mode, int xlu, optional<int> bus)
LloValue* r = LloInstruction::CreateVectorSetPermutePattern(in, mode, xlu, bus, this->region());
return this->region()->AppendInstruction(r); // jmp AppendInstruction @0x1d50f9a0
```text
factory 是一个包装器;`CreateVector*` op-constructor 持有 opcode。每个 `CreateVector*` 在栈上构建 operand `Span`,然后调用一个 primitive —— 已在反编译中确认,例如 `CreateVectorSetPermutePattern` 发出 `LloInstruction::New(139, span, 1, region, …)`(139 = `0x8b`),`CreateVectorPermute` 发出 `New(54, span, 2, …)`(54 = `0x36`),`CreateVectorTranspose` 发出 `New(166, span, 2, …)`(166 = `0xa6`):
```c
// LloInstruction::New(LloOpcode op, Span<LloValue* const> operands,
// LloRegion*, LloValue*, PredicationPolarity, LloValue*) @0x1d4cf560
// op is the first immediate; the Span size is the operand count.
v = LloInstruction::New(/*op=*/166, /*operands=*/&span, /*count=*/2, region, 0, 0, ...);New 写入 WORD[value] = op(op word),并把 Span 连为 source operands。
Op → Factory → Opcode 表
所有 factories 都在命名空间 xla::jellyfish::LloRegionBuilder:: 中。发出的 opcode 是 New 的第一个 immediate;operand count 是 Span 的大小。十进制/十六进制都列出,因为反编译输出十进制。
| Factory @addr | CreateVector* @addr | 发出的 op(dec / hex) | operands |
|---|---|---|---|
Vsetperm @1d52ba20 | …SetPermutePattern @1d4d62a0 | 139 / 0x8b kVectorSetPermutePattern | 1 |
Vsetspr @1d52ba60 | …SetSegmentPattern @1d4d64a0 | 140 / 0x8c kVectorSetSegmentPattern | 1 |
Vpermute @1d52c180 | …Permute @1d4d55c0 | 54 / 0x36 kVectorPermute | 2 {data,pat} |
Vpermuteres @1d52bfa0 | …PermuteResult @1d4d5e40 | 336 / 0x150 kVectorPermuteResult | 1 |
Vrotate @1d52c6c0 | …Rotate @1d4d58a0 | 58 / 0x3a kVectorRotate | 2 {data,amt} |
Vbroadcastlane @1d52c9a0 | …LaneBroadcast @1d4d6080 | 59 / 0x3b kVectorBroadcastLane | 2 (+0x3d,1) |
Vxpose @1d54f580 | …Transpose @1d4dcfe0 | 166 / 0xa6 kVectorTranspose | 2 |
Vxposeres @1d5501e0 | …TransposeResult @1d4d5b60 | 340 / 0x154 kVectorTransposeResult | 0(FIFO pop) |
VxposeBinaryCompressedB16 @1d550220 | …TransposeBinaryCompressedB16 @1d4dd7e0 | 167 / 0xa7 kVectorTransposeBinary | 3(+scale) |
VpackBf16 @1d554680 | …Weird @1d4d4e20 | 174 / 0xae kVectorWeird | 1 |
VunpackUpperCF32 @1d567f20 | …Unpack @1d4d37c0 | 271 / 0x10f kVectorDynamicUnpack | … |
VunpackLowerCF32 @1d567e20 | …Unpack @1d4d37c0 | 271 / 0x10f kVectorDynamicUnpack | … |
Vunpackf32 @1d554620 | — composite — | VunpackLowerF32+CastTo(0x12)+VunpackUpperF32+CastTo(0x12) | — |
VpermuteSync @1d52baa0 | — composite — | Vpermute(0x36) then Vpermuteres(0x150) | — |
VpermuteSlane @1d52d220 | CreateVectorBinop @1d4d27c0 | (由 opcode 参数驱动) | — |
VpackiB16 @1d553380 / VpackcB16 @1d562700 | CreateVectorPack @1d4d3140 | (由 opcode 参数驱动) | 2 |
注意 ——
VxposeBinaryCompressedB16发出单个0xa7op(3 operands),不是 multiply/pow chain。它的 factory(@0x1d550220)和 constructor(@0x1d4dd7e0)把第三个 operand 作为LloModule::ScalarU32ConstantImplscale value;唯一额外动作是target().SupportsVsupp()gate(CHECK string 位于llo_region_builder.cc:8617)。XLU 路径上不存在New(0x156/0x158/0x159)调用。
JF VectorExtendedOpcode 清单 {0..34}
目的
这是 bundle encoder 打包进 JF VEX slot 的线路层 enum。{13..34} 区段是 XLU 家族;{0..12} 区段是 matmul + push-gains,记录在 MXU 和 matprep/latch slots 中。ProtoUtils::Is* classifiers 用 proto-enum value 做索引,以路由 decode/encode dispatch。
Classifier Ranges(binary-exact)
直接从二进制反编译而来 —— 这些是 proto-enum 编号中的真实 dispatch ranges:
IsMatrixMultiply(op) = (op < 7) & (0x77 >> op) // {0,1,2,4,5,6} (3 = DONE_WITH_GAINS excluded)
IsPushGains(op) = (unsigned)(op - 7) < 6 // {7..12}
IsTranspose(op) = (unsigned)(op - 15) < 2 // {15,16}
IsRpu(op) = (unsigned)(op - 17) < 0x12 // {17..34}
VectorExtendedUsesData(op) = (op != 3) // only op 3 reads no vector data operand
```text
`ProtoUtils::IsRpu`(@ `0x1e875b60`)计算 `(op - 17) < 0x12`,所以 RPU 区段是 `{17..34}` —— 从 `PERMUTE(17)` 到 `CROSS_LANE_SEGMENTED_MIN_INDEX_PERMUTE(34)`。`TRANSPOSE(15)`/`TRANSPOSE_START(16)` 归入 `IsTranspose`,不归入 `IsRpu`。
### 清单表
`mnemonic` 是 `ParserJf` cross-lane parse-pair string(assembler 侧);`LLO` 是 `LloRegionBuilder` factory 发出的高层 `LloOpcode`。
| 值 | 名称 | classifier | mnemonic / LLO bridge |
|---|---|---|---|
| 0 | `MATRIX_MULTIPLY` | `IsMatrixMultiply` | matmul |
| 1 | `MATRIX_MULTIPLY_LOW` | `IsMatrixMultiply` | matmul.low |
| 2 | `MATRIX_MULTIPLY_HIGH` | `IsMatrixMultiply` | matmul.hi |
| 3 | `DONE_WITH_GAINS` | `UsesData=false` | done-with-gains(无 vector operand) |
| 4 | `MATRIX_MULTIPLY_DONE_WITH_GAINS` | `IsMatrixMultiply` | matmul.dwg |
| 5 | `MATRIX_MULTIPLY_LOW_DONE_WITH_GAINS` | `IsMatrixMultiply` | matmul.low.dwg |
| 6 | `MATRIX_MULTIPLY_HIGH_DONE_WITH_GAINS` | `IsMatrixMultiply` | matmul.hi.dwg |
| 7 | `PUSH_GAINS` | `IsPushGains` | push-gains |
| 8 | `PUSH_GAINS_LOW` | `IsPushGains` | push-gains.low |
| 9 | `PUSH_GAINS_HIGH` | `IsPushGains` | push-gains.hi |
| 10 | `PUSH_GAINS_TRANSPOSED` | `IsPushGains` | push-gains.xpose |
| 11 | `PUSH_GAINS_LOW_TRANSPOSED` | `IsPushGains` | push-gains.low.xpose |
| 12 | `PUSH_GAINS_HIGH_TRANSPOSED` | `IsPushGains` | push-gains.hi.xpose |
| 13 | `SET_PERMUTE_CONTROL_REGISTER` | — | LLO `0x8b` `Vsetperm` |
| 14 | `SET_SEGMENT_PATTERN_REGISTER` | — | LLO `0x8c` `Vsetspr` |
| 15 | `TRANSPOSE` | `IsTranspose` | `vxpose` — LLO `0xa6` `Vxpose` |
| 16 | `TRANSPOSE_START` | `IsTranspose` | `vxpose.start` — LLO `0xa7` `VxposeBinaryCompressedB16` |
| 17 | `PERMUTE` | `IsRpu` | LLO `0x36` `Vpermute` |
| 18 | `LANE_ROTATE` | `IsRpu` | LLO `0x3a` `Vrotate` |
| 19 | `ROTATING_PERMUTE` | `IsRpu` |(rotating permute)|
| 20 | `CROSS_LANE_ADD` | `IsRpu` | `vadd.xlane` — LLO reduce-add |
| 21 | `CROSS_LANE_MAX` | `IsRpu` | `vmax.xlane` — LLO reduce-max |
| 22 | `CROSS_LANE_MIN` | `IsRpu` | `vmin.xlane` — LLO reduce-min |
| 23 | `CROSS_LANE_MAX_INDEX` | `IsRpu` | `vmax.index.xlane` |
| 24 | `CROSS_LANE_MIN_INDEX` | `IsRpu` | `vmin.index.xlane` |
| 25 | `CROSS_LANE_ADD_PERMUTE` | `IsRpu` | `vadd.xlane.perm` |
| 26 | `CROSS_LANE_MAX_PERMUTE` | `IsRpu` | `vmax.xlane.perm` |
| 27 | `CROSS_LANE_MIN_PERMUTE` | `IsRpu` | `vmin.xlane.perm` |
| 28 | `CROSS_LANE_MAX_INDEX_PERMUTE` | `IsRpu` | `vmax.index.xlane.perm` |
| 29 | `CROSS_LANE_MIN_INDEX_PERMUTE` | `IsRpu` | `vmin.index.xlane.perm` |
| 30 | `CROSS_LANE_SEGMENTED_ADD_PERMUTE` | `IsRpu` | `vadd.xlane.seg.perm` — LLO `0xfc` seg-reduce |
| 31 | `CROSS_LANE_SEGMENTED_MAX_PERMUTE` | `IsRpu` | `vmax.xlane.seg.perm` — LLO `0xfa` seg-reduce |
| 32 | `CROSS_LANE_SEGMENTED_MIN_PERMUTE` | `IsRpu` | `vmin.xlane.seg.perm` — LLO `0xfb` seg-reduce |
| 33 | `CROSS_LANE_SEGMENTED_MAX_INDEX_PERMUTE` | `IsRpu` | `vmax.index.xlane.seg.perm` |
| 34 | `CROSS_LANE_SEGMENTED_MIN_INDEX_PERMUTE` | `IsRpu` | `vmin.index.xlane.seg.perm` |
紧凑范围 `{0..34}` 由 `NameOfDenseEnum<descriptor,0,34>` 实例化 @ `0x2239bce8` 确认;名称是 protobuf `EnumValueDescriptorProto` identifiers(descriptor @ `0x1fa1fd00`)。两个 `SET_*` 名称也可作为 `.rodata` strings 独立可见;`Is*` classifier bodies 已按字节精确反编译(见上)。
> **注意 —— LLO reduce-family → cross-lane bridge。** 高层 reduce LLO ops `0xf5..0x101` 会降低到 `CROSS_LANE_*` 区段 `{20..34}`。分流由 `LloOpcodeIsSegmentedReduction(op) = (op - 250) < 3 = {0xfa,0xfb,0xfc}` 决定(binary-confirmed @ `0x1d60c340`):segment reduces 使用 `SET_SEGMENT_PATTERN_REGISTER`(`Vsetspr`)prologue,并降低到 `CROSS_LANE_SEGMENTED_*`;所有其他 reduces 使用 `SET_PERMUTE_CONTROL_REGISTER`(`Vsetperm`)prologue,并降低到非分段 `CROSS_LANE_*` ops。
---
## XLU Op 组合流水线
### 目的
XLU 是从一个与 MXU 共享的 slot 发出的稀缺多周期资源。两个执行*相同* cross-lane operation 的相邻 XLU ops(例如两个 sum-reduces 使用同一 permute pattern)可以融合为一次 cross-lane pass,并且只支付一次 pattern setup 成本。`LloXluGraphOptimizer::Optimize` 就是找到这些融合、在每代 XLU units 之间平衡保留下来的 ops、重排以缩短 critical path,并把它们打包到 VEX source buses 上的重写。
### 流水线顺序
五个阶段按以下精确顺序运行,来自 `Optimize` 函数体的字节映射(`@0x126cdb80`):
```text
AdjustEdgesBeforeXluAssignment @0x126d1de0 ; pre-adjust dependency-graph edges
build PreXluAssignmentLatencyTable ; XLU↔XLU edge = ceil(base / xlu_count)
CrossXlu Create (tracker #1, reverse=0) ; data-dependency tracker over the XLU ops
ComputeCombinablePairs @0x126d2480 ; (1) fuse-candidate pairs
[gate optimizer+0x28==1] AssignXlu @0x126d3100 ; (2) greedy least-loaded XLU-unit assign
CrossXlu Create (tracker #2, reverse=1) ; rebuilt on the unit-assigned graph
ReorderToShortenCriticalPath @0x126d3460 ; (3) latency-weighted list scheduler
ReemitReorderedCombinedXluOperations @0x126d5460; (4) emit fused/reordered LLO ops
[gate optimizer+0x28==1] AssignSourceBus @0x126d70e0 ; (5) VEX source-bus pack (Pufferfish only)dependency tracker(CrossXluOperationsDataDependencyTracker)会构建两次:一次在 combine 之前(reverse=0),一次在 reorder 之前(reverse=1,针对 post-combine、post-unit-assign graph)。两个阶段都会查询它的 XluOperationIsReady predicate(in-edge count == 0)。
阶段 1 — ComputeCombinablePairs
ComputeCombinablePairs(@0x126d2480)接收 XLU-op list(一个 vector<variant<TransposeTile, RpuOperation, XluControlOperation>*>)、cross-region from/to boundary LloValue pair,以及 dependency tracker,并返回可融合 pair 的 vector<pair<variant*,variant*>>。
它构建 per-op cost / value / cumulative-max arrays(critical-path DP),然后按 metadata key 将 ops 分组到 per-key btree_set<long> buckets 中;只要较晚的 op 与较早的 op 在同一 key 上碰撞、tracker-ready 且 cost-compatible,就发出一个 combinable pair。
两个 metadata keys,每个可融合 variant 一个:
| variant | key struct(byte-exact) | extractor |
|---|---|---|
RpuOperation(idx 1) | RpuOperationMetadata {u16 opcode@0, LloValue* op0@8, LloValue* op1@0x10 (gated u8@0x18==1)} | GetRpuTransposeOperationKeyFrom @ 0x126d8520 |
TransposeTile(idx 0) | TransposeTileMetadata {i32 height@0, i64@8, u16@0x10, u8 vxpose_mode@0x12, u8@0x13} | 内联于 $_0 visitor |
对于 RPU op,key 是 {opcode, source-operand-0, source-operand-1} —— 但 opcode == 0x3a(Vrotate)例外,它只使用一个 from-end operand(op1 = 0,has1 = 0)。对于 transpose tile,key 是 {height, anchor, vxpose-mode, …}。
完整 fusion predicate:
两个相邻 XLU ops 会融合为一个 cross-lane operation,当且仅当 (a) variant kind 相同且 fusion metadata 相同;(b) 第二个 op 在 dependency graph 中 list-scheduling-ready(
XluOperationIsReady,所有 predecessor XLU ops 已调度);并且 (c) 合并后 critical-path cost 仍有界(CyclesAddedByXluOperationDP arrays)。XluControlOperationops 从不 fuse ——$_2visitor arm(@0x2139b1c0)是硬LogFatal。
阶段 2 — AssignXlu
AssignXlu(@0x126d3100)是贪心 least-loaded bin-packer。它只在 XlusPerTensorCore() > 1 时有意义 —— 反编译中包含精确 CHECK string "dep_graph_->target()->XlusPerTensorCore() > 1"(line 2563),如果该代少于 2 个 XLU 则 LogFatal。
它分配一个 per-XLU running-cost record array(xlu_count 条记录,每条 0x20 B),然后对每个 combinable pair 扫描所有记录,选择 accumulated cost 最小的 XLU,将该 unit index 写入每个 backing LloInstruction 的 unit-selector field($_0 lambda @ 0x126db0a0),并把该 op 的 CyclesAddedByXluOperation 加到该 XLU 的 running cost。unit 选择会立即提交到 instruction word,因此后续 reorder 和最终 emission 都能看到它。
unit-selector 写入与下面的 ValidateAndSetXluAndSourceBus emission writer 按字节相同 —— AssignXlu 是同一字段在 scheduler 侧的生产者。
阶段 3 — ReorderToShortenCriticalPath
ReorderToShortenCriticalPath(@0x126d3460)是在已完成 unit-assigned 的 ops 上运行的标准 latency-weighted list scheduler。它分配一个 per-XLU PerXluOperations state struct(stride 0x60):
| offset | field | 作用 |
|---|---|---|
+0x00..0x38 | absl::btree_set<long, less, alloc, 256> | per-XLU pending op-index set |
+0x38 | i64 | remaining running-cycle accumulator(尚未调度 ops 的 Σ cost) |
+0x40 / +0x48 | LloValue* pair | last-scheduled op 的 source operand 0 / 1(next-delta anchor) |
+0x50 | variant* | 此 XLU 上 last-scheduled op(下一次 CyclesAdded 的 prev) |
+0x58 | i64 | per-XLU completion-time clock(critical-path frontier) |
Phase A 为每个 op 预计算 cost[i] = CyclesAddedByXluOperation(...),累计到 PerXlu[xlu][+0x38],并构建 pending sets。Phase B 运行 per-XLU priority_queue<pair<long,long>> max-heap,key 为 {marginal_cost, op_index}(less<> ⇒ longest-marginal-cost ready op 优先,tie 由更高 op-index 打破)。它弹出最高优先级 op;$_2 lambda 测试 XluOperationIsReady 加 completion-clock critical-path test([+0x58] + cost >= finish[idx]);ready 时,$_1 lambda 提交(从 pending set 删除,推进 [+0x58] = max([+0x58]+cost, finish[idx]),把 pair 写入输出);not-ready 时,$_3 lambda 重新定价并把 op 重新入队。一个 pre-test(cmp heap_top_cost, [PerXlu+0x38]; jl skip)只在仍然能缩短该 XLU 的 remaining path 时尝试 reorder —— 这就是函数名所描述的 critical-path-shortening gate。
阶段 4 — ReemitReorderedCombinedXluOperations
ReemitReorderedCombinedXluOperations(@0x126d5460)是把 scheduler 决策转成实际 LLO 的 IR rewriter。它构建新的 emission LloRegionBuilder 和 per-XLU PerXluState array(stride 0x20)—— per-XLU “currently-set pattern” cache:
| offset | field |
|---|---|
+0x00 | 当前在此 XLU 上设置的 permute-pattern source value |
+0x08 | 已发出的 Vsetperm result(共享 SetPermutePattern setup) |
+0x10 | 当前在此 XLU 上设置的 segment-pattern source value |
+0x18 | 已发出的 Vsetspr result(共享 SetSegmentPattern setup) |
对每个 combinable pair,它通过 factory 集发出一个 fused cross-lane op:
- RPU pair(variant idx 1):对于 producer 是
SetPermutePattern(0x8b)op 的每个 source,将其与PerXluState[xlu][+0x00]比较;如果不同则发出Vsetperm并缓存结果,否则复用缓存的 pattern。(SetSegmentPattern0x8c→Vsetspr,缓存在+0x18/+0x10。)然后发出 fused reduce body,消费单个共享 pattern result —— 两个 combinable reduces 折叠为一个 cross-lane reduce。ReplaceUsesOfInstruction重定向第二个 op 的 uses;RemoveNode删除原始 ops。 - Transpose pair(variant idx 0):匹配两个 tiles 的 geometry(见下面的 slot-fit),在
SupportsVectorXpose/NumVexSlots上 gate,发出一个 fusedVxpose/VxposeBinaryCompressedB16,重新安置两个 tiles 的 instructions(PopInstruction/AppendInstruction),并对每个 result chunk 发出Vxposeres和ReplaceUsesOfInstruction。
经济性:per-XLU SetPermute/SetSegment pattern op 每个 XLU 只发出一次并复用 —— 共享同一 pattern 的 N 个 reduces 只支付一次 pattern setup。
阶段 5 — AssignSourceBus
AssignSourceBus(@0x126d70e0)把每个 XLU op 的 operands 路由到 VEX source buses。它由 Target::HasVexSourceBuses()(vtable +0x408)gate —— 此构建中只有 Pufferfish (v4) 返回 true(JF/DF/VF/GL 返回 false),因此 source-bus pass 在这里的其他所有代上都是 no-op。
启用时,它按拓扑顺序遍历 dependency graph,并为每个满足 LloOpcodeUsesSourceBus(下面的 29-opcode set)的 op 绑定 bus。MXU ops(LloOpcodeUsesMxu)绑定到显式 MXU-indexed bus;纯 XLU ops 贪心占用下一个空闲 bus。bus pool 来自 SourceBusesForXlu(i) —— 在 Pufferfish 上为 {i, i+2}(反编译:[+8]=i, [+0xc]=i+2, count-tag=4)—— 因此 XLU 0 拥有 buses {0,2},XLU 1 拥有 {1,3},即 V0/V1/V2/V3 read ports 按 (V0,V2)/(V1,V3) 配对。落到同一 bus 的两个 ops 会获得新的 latency-weighted serialization edge(UpdateEdge)—— 共享 bus 的 structural hazard。
LloOpcodeUsesSourceBus(binary-confirmed @ 0x10c0d420)对精确 29 个 opcodes 返回 true:
{0x36, 0x3a, 0x3b} permute / rotate / broadcast-lane
{0x8b, 0x8c} set-permute-pattern / set-segment-pattern
{0x8f .. 0x96} 8 matmul-push ops (MXU operand path, UsesMxu)
{0xa6, 0xa7} transpose / transpose-binary
{0xf5 .. 0x101} 13 cross-lane reduce / index / segment-reduce ops
{0x155} transpose-clear
```text
### Scheduler 侧 Bit-Field
`AssignXlu`(unit)和 `AssignSourceBus`(bus)都会写 `WORD[LloInstruction + 0xb]`;LLO-emission validators `ValidateAndSetXluAndSourceBus` / `ValidateAndSetMxuAndSourceBus` 会重新断言同一字段。来自反编译的字节精确形式:
```c
// unit selector (XLU or MXU instance):
WORD[instr+0xb] = ((xlu & 3) << 8) | (WORD[instr+0xb] & 0xF8FF) | 0x400; // bits 8-9 + valid bit 10
// source bus (Pufferfish only):
WORD[instr+0xb] = ((bus & 3) << 11) | (WORD[instr+0xb] & 0xC7FF) | 0x2000; // bits 11-12 + valid bit 13source-bus 字段保存原始 2-bit index {0..3} —— 不是 SparseCore VexSourcePortEncoding proto enum,后者是另一条 datapath 上的不同 3-bit encoding(见 VPU Slot)。
XLU 成本模型
CyclesAddedByXluOperation
CyclesAddedByXluOperation(@0x126d22a0)是 combine DP、AssignXlu min-cost pick 和 reorder heap priority 共同使用的单一 marginal-latency 函数。按字节精确反编译,闭式形式为:
long CyclesAddedByXluOperation(variant* prev, variant* cur,
LloValue* from, LloValue* to, LatencyTable& tbl) {
if (cur == null) {
// empty-XLU base case → only a transpose prev contributes
if (prev == null || prev.discr != 0 /*TransposeTile*/) {
if (prev == null) return 0;
goto transpose_tail; // prev.discr == 0
}
return 0;
}
// MAIN EDGE: prev's anchor op → cur's anchor op
long cost = tbl.LatencyBetween( op_data(prev), op_data(cur) );
if (prev != null && prev.discr != 0 /*not TransposeTile*/) {
if (cur.discr == 0 /*cur is TransposeTile*/) goto transpose_tail;
CHECK(prev.discr == 1 /*RpuOperation*/); // XluControlOperation prev ⇒ LogFatal line 1012
// RPU prev: two inline source-operand identities at [prev+0x00], [prev+0x08]
if (prev.src0 && prev.src0->operands(0) != from)
cost += tbl.LatencyBetween( GetAnchorInstruction(cur), prev.src0 );
if (prev.src1 && prev.src1->operands(0) != to)
cost += tbl.LatencyBetween( GetAnchorInstruction(cur), prev.src1 );
return cost;
}
transpose_tail: // prev is TransposeTile (or cur==null path)
long n = prev.read_set_size; // [prev+0x08]
if (n >= 2)
cost += (n - 1) * tbl.LatencyBetween( readset[0].op_data, readset[1].op_data );
return cost;
}
```text
解释:
- 主导项是从 XLU 上一个 op 到新 op 的 **per-(op,op) edge latency**,取自 optimizer 的 table。
- 对 RPU op,**其两个 source operands 中每一个不是 cross-region boundary value**(`from`/`to`)的 operand,都会额外增加一次 `LatencyBetween(cur_anchor, prev_source)` —— 这是在 XLU 上 materialize 非 boundary source 的 fan-in penalty。Boundary operands 免费。
- 对 transpose chain,成本是 `(read-set − 1)` 份第一个和第二个 tile elements 之间的 latency —— per-extra-chunk transpose-sequence latency。
- `XluControlOperation` 作为 `prev` 是硬 `LogFatal` —— control ops 从不定价,这与它们从不 combinable 一致。
`GetAnchorInstruction`(`@0x126cda00`)把 variant 解析到其 anchor `LloValue`:idx 1(RPU)→ `[v+0x10]`;idx 2(control)→ `[v]`;idx 0(transpose)→ 最后一个 read-set element;`op_data(v) = [resolve(v) + 0x10]`。
### PreXluAssignmentLatencyTable Edge
`LatencyBetween` 运行在 optimizer 自己的 `PreXluAssignmentLatencyTable` 上,这是对每代 base `LatencyTable`(由 `LatencyTable::Create(TpuVersion)` registry dispatch 选择)的 wrapper。其 `LatencyBetweenInternal`(`@0x126e0e40`)按反编译字节精确为:
```c
long LatencyBetweenInternal(LloValue* from, LloValue* to) {
if (IsXluOp(from.op) && IsXluOp(to.op)) {
int raw = delegate.LatencyBetween(from, to); // [this+0x18] per-gen base table
int div = xlu_count; // [this+0x20]
return ceil(raw / div); // div>0: quotient + (raw > quotient*div ? 1 : 0)
}
return delegate.LatencyBetween(from, to); // pass-through for non-XLU edges
}IsXluOp(op) 是 21 个 XLU opcodes {0x8b, 0x8c, 0xa6, 0xa7, 0xf5..0x101, 0x14f, 0x150, 0x154, 0x155}(反编译中的 case labels)与 low-band bit-mask op <= 0x3b && bt(0xc40000000000000, op) = {0x36, 0x3a, 0x3b} 的并集 —— Vpermute / Vrotate / Vbroadcastlane。
XLU↔XLU edge 是每代 base latency 除以可用 cross-lane units 数量:XLU 越多,单个 XLU↔XLU edge 越便宜 —— 这就是整个 optimizer 用来定价的 parallelism discount。
xlu_count = Target::XlusPerTensorCore() = VectorIsa.xlu_count = DWORD[Target+0x4b0]。
Transpose Slot-Fit 几何
目的
fused transpose 必须适配每代 VEX-slot budget。reemit transpose 路径在把两个 tile fusion 折叠为一个 Vxpose 之前,会用一个三条件 predicate gate 它。
VxposeMode 和 ElementCount
VxposeMode 是 5 值 enum;ElementCount(mode) 是该 mode 的 elements-per-chunk,从 0xb53c830 处的表读取。按字节精确确认(.rodata 的 xxd 给出 01 00 00 00 02 00 00 00 04 00 00 00 01 00 00 00 02 00 00 00):
| mode | 名称 | ElementCount | 含义 |
|---|---|---|---|
| 0 | B32 | 1 | full-width 32-bit transpose(默认) |
| 1 | Compressed B16 | 2 | bf16-compressed,2 elements/chunk |
| 2 | Compressed B8 | 4 | b8-compressed,4 elements/chunk |
| 3 | Segmented B32 | 1 | segmented 32-bit transpose |
| 4 | Segmented B16 | 2 | segmented bf16 transpose |
三重 Gate Predicate
来自 reemit transpose block(@0x126d5b1b..0x126d5f5a)的字节精确形式:
| gate | 条件 | 接受 / 拒绝 |
|---|---|---|
| G1 | target->SupportsVectorXpose(vxpose_mode) == true(vtable +0x100) | 如果该 gen 不支持此 mode,则 reject(no fusion) |
| G2 | [tile+0x30] % (SublaneCount() * ElementCount(mode)) == 0(imul + idiv + test-remainder) | 如果 chunk dimension 不是整数量的 slot-sized element chunks,则 reject |
| G3 | NumVexSlots() != 0(vtable +0x690) | accept → emit;打包到 NumVexSlots() 个 VEX slots |
| G3′ | 如果 NumVexSlots() == 0:ChunksPerTile() == (chunks >> mode_shift) | 如果不相等则 reject(no-VEX-slot path) |
带有 VEX slots 的 gen 会把 fused transpose 打包到 NumVexSlots() 个 vector_extended slots;没有 VEX slots 的 gen 只能融合正好占用一个 tile chunks 的 transpose。(在 gates 之前,两个 tiles 已经必须在 GetNumberOfChunksInTransposeSequence、vxpose_mode、TransposeResultChunkCount 和 GetTransposeWidth 上匹配。)
Per-Gen Target Overrides
来自 per-Target vtable slots 的字节精确结果。NumVexSlots() 是每代 vector_extended slot 数量 —— 与 MXU 和 EUP 使用的是同一个 slot;在 JF 上,它是 41-byte bundle 的单个 VEX slot。
| Target (gen) | NumVexSlots() | SupportsVectorXpose(mode) |
|---|---|---|
JellyfishTarget (v2) | 1(return 1) | mode == 0(仅 B32) |
PufferfishTarget (v4) | 2 | mode != 2(除 Compressed B8 外全部) |
GhostliteTarget (v6e) | 2 | mode < 3(B32 / Compressed B16 / B8) |
ViperfishTarget (v5p) | 2 | mode != 2(除 Compressed B8 外全部) |
Target (base) | LogFatal | abstract |
注意 —— PF/VF
SupportsVectorXposebodies(0x1d4940a0/0x1d49a000)是return a2 != 2;:它们接受除 mode 2(Compressed B8)以外的每个VxposeMode。cmp esi, 2; ret形式是不等测试,不是mode == 2。参见 Transpose Reservation Latency 获取VxposeModeordinal 清单。
示例 —— 两个 kVectorAddReduceF32 融合(Pufferfish v4,xlu_count = 2)
ComputeCombinablePairs发出 pair{&R_a, &R_b}—— 相同RpuOperationMetadata{0xf7, src0, src1}(0xf7 ≠ 0x3a,所以有两个 source operands),R_btracker-ready,cost-compatible。AssignXlu:xlu_count == 2(≥ 2 OK)。两者都适配 XLU 0(min cost,tie → index 0)。$_0lambda 把unit = 0 | valid写入两个 instruction words。成本= CyclesAddedByXluOperation(...) = ceil(B / 2),其中B是每代 base reduce-edge latency —— 因为两个 XLU 并行运行,所以是串行延迟的一半。- tracker 在 unit-assigned graph 上重建(
reverse = 1)。ReorderToShortenCriticalPath初始化PerXluOperations[0],将R_a/R_b按$_3marginal cost 入队,并优先调度 longest-cost ready op。 ReemitReorderedCombinedXluOperations:pair{R_a, R_b},两者都是 RPU,xlu = 0。只发出一次Vsetperm(缓存在PerXluState[0][+0x08]);两个 reduces 折叠为一个 fused cross-lane reduce,消费该单个 pattern setup。ReplaceUsesOfInstruction重定向R_b的 uses;RemoveNode删除原始 ops。AssignSourceBus(HasVexSourceBuses = true):把 fused op 打包到来自SourceBusesForXlu(0) = {0,2}的一个 V-port 上;2-bit bus index 写入WORD[+0xb]bits 11-13。
对比 segment reduce pair(0xfc):LloOpcodeIsSegmentedReduction(0xfc) = true,所以共享 prologue 是 Vsetspr(缓存在 PerXluState[+0x18]),而不是 Vsetperm。对比 transpose pair(0xa6):variant idx 0;reemit 匹配 vxpose-mode/height/chunk-count,在三重 gate slot-fit predicate 上 gate,发出一个 fused Vxpose,重新安置两个 tiles 的 instructions,并对每个 result chunk 发出 Vxposeres。
未固定内容
SetPermuteModeenumerator names(第 2 个Vsetpermarg):该值作为DWORD[variant+0x40]穿过,但未定位到SetPermuteModeToString。LOW。VunpackUpperCF32/VunpackLowerCF32的精确CreateVectorUnpackarm + 其VpackFormat:存在多个Newarms(0x10f和 dynamic-opcode arm),CF32-specific half 未隔离。LOW。VpermuteSlane/VpackiB16/VpackcB16发出的 opcodes 作为参数传给CreateVectorBinop/CreateVectorPack;每个调用的具体值未隔离。LOW。- PF/VF
SupportsVectorXpose精确 mode mask(ICF-foldedcmp esi,2; retthunk)。LOW。 LatencyBetweenstochastic perturbation(当[table+0x10] != 0时添加UniformDistribution(0,0x65)):存在于 cost path 中,其启用条件和 scheduling effect 未隔离。
交叉引用
- VPU Slot —— per-lane vector ALU;EUP/XLU push-pop protocol 和不同的 SparseCore
VexSourcePortEncoding。 - MatPrep/IAR/Latch Slot —— 与 XLU 共享
VectorExtendedslot 的 matmul{0..6}/ push-gains{7..12}区段。 - vcreate_mask & M-Register —— masked-scan family 所选择的 SparseCore vector-mask file。
- ResultFifo & ArchRegister ——
Vxposeres/Vpermuterespops 所 drain 的 result FIFOs。 - Bundle Model —— encoder 将 XLU op 打包进入的 VLIW bundle;每代
NumVexSlots()budget。