AutoProto / AutoOr 解析
本页所有地址均适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(构建libtpu_lts_20260413_b_RC00,build-id md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,ELF x86-64 DYN,未 strip;反混淆后的 C++ 符号按原文引用)。其他版本会有所不同。
摘要
具有 AUTO / ENABLED / DISABLED 语义的 TPU 编译开关,在 TpuCompilationEnvironment (TCE) 中并不是以普通 bool 或 int 存储的。它存储为指向 xla::jellyfish::AutoProto 的指针,这是一个单 oneof 消息:其活跃分支携带显式值,或者当 oneof_case_ == 0 时表示未设置 (AUTO)。从该 proto 到具体编译决策的桥梁是 AutoOr<T> 模板:AutoOr<T>::FromProtoOrDie 读取 oneof,将结果打包成一个present-bit / value 代码,然后每个开关手写的访问器把该代码折叠为编译器实际使用的值。本页负责说明这一解析模型:AutoOr<T> 包装器、它的 present-bit 打包方式、每个开关的 AUTO→默认极性,以及分派它的 ObjectView<TCE> 访问器区段。
参考框架是一个三态选项(带有文档化 fallback 的 std::optional<bool>),但有两个 libtpu 特有的细节,重新实现者必须精确复现。第一,present-bit 被打包进与值相同的机器字,位置取决于类型类别:bool 为 bit 8,int32/enum 为 bit 32,int64 使用成对的 dl has-byte,string/message 使用单独的 has-flag。消费者从不测试 optional::has_value();它测试打包返回值中固定位置的一个 bit。第二,也就是没有 XLA-CPU/GPU 对应物的部分,AUTO→具体 fallback 不是字段的属性。它是消费者的属性:同一个全 AUTO 默认实例 (AutoProto_globals_) 供给每个未设置的开关,而两种不同的消费者惯用法(AUTO=off 与 AUTO=on)会把同一个 0x000 打包代码读取为相反的布尔值。每个访问器中固化的 bit 极性就是文档化默认值。
本页结构如下:AutoProto 的 wire/内存布局;AutoOr<T>::FromProtoOrDie 解析器模板及其按类型类别的打包方式;每个开关的 AUTO→具体 fallback 惯用法(两种 bool 极性、int64 sentinel 表、enum-0 规则、message 默认实例规则);托管全部 130 个解析器的 ObjectView<TCE> 访问器区段;以及第二种 inline-TristateProto 存储类,它共享三态语义,但不使用 AutoProto 机制。字符串解析入口(auto/enabled/disabled → AutoProto)见 autoor-parse-grammar.md;反向文本见 autoor-unparse.md;12 个 message 分支的子默认值见 autoproto-message-arms.md。
对重新实现而言,契约是:
AutoProto布局 — oneof body 在+0x10,判别器oneof_case_在+0x1c,oneof_case_ == 0⇒ AUTO;全零默认实例AutoProto_globals_是任何 null TCE 字段的 fallback。AutoOr<T>打包返回值 — 每个类型类别的 present-bit 位置,以及AUTO会返回 present-bit 清除的全零代码 (0x000)。- 每个开关的极性 — 决定
AUTO解析为何值的是消费者,而不是字段;包括 45/26 bool 划分、18 项 int64 sentinel 集合,以及 enum-0 / message-default 规则。
| 解析器模板 | AutoOr<bool>::FromProtoOrDie @ 0xf795300 — if (oneof_case_==0) return 0; else return val | 0x100 |
| 分支读取器 | AutoOrTypeTraits<bool>::FromAutoProto @ 0xf7953e0 — 检查 +0x1c==1,读取 body +0x10 |
AutoProto 布局 | oneof body +0x10 (8 B),oneof_case_ +0x1c (u32);0=AUTO。Oneof 名称 "value"(str @ 0x867f0e9) |
| 默认实例 | AutoProto_globals_ @ 0x223c8968 — body +0x10 = 0,case +0x1c = 0 ⇒ 全 AUTO |
| 解析表 / 全局量 | AutoProto::_table_ @ 0x21cfa788 · AutoProto_globals_ @ 0x223c8968 |
| 解析器访问器区段 | 0x1d6b6420 .. 0x1d6b9f60 — 130 个 ObjectView<TpuCompilationEnvironment> 单字段访问器 |
| 类型类别打包 | bool (present<<8)|val8 · int32/enum (present<<32)|val32 · int64 {rax=val, dl=has} · string/msg {val, has-byte} |
| 极性统计 | 45 个 AUTO=off bool · 26 个 AUTO=on bool · 18 个 int64 sentinel · 11 个 enum/int bt-32 · 2 个 composite · + msg/enum 分支 |
| 第二存储类 | 67 个 inline TristateProto.Value 枚举 — cmpl $2,+OFF (ENABLED==2),不是 AutoProto |
| 置信度 | CONFIRMED(字节锚定对照反编译),除非某行或标注另有说明 |
1. AutoProto 消息
目的
AutoProto 是一个三态开关的存储单元。它是一个 protobuf 消息,恰好带有一个 oneof(命名为 "value"),其 30 个分支覆盖 TCE 开关可携带的每种值类型:8 种标量类型、10 个枚举和 12 个子消息。活跃分支持有显式用户值;非活跃 oneof (oneof_case_ == 0) 表示该开关保留为 AUTO。TCE 以 AutoProto* 指针字段持有这些值,每个三态开关一个(1121 个 TCE 字段中约 330 个)。解析器读取下面的布局;30 分支分派和子消息分支由 autoproto-message-arms.md 负责。
内存布局
每个 AutoOrTypeTraits<T>::FromAutoProto 读取器和每个消费者访问器都读取相同两个槽位,因此布局固定且精确到字节:
AutoProto object
+0x00 protobuf message header (vtable / arena / unknown-fields)
+0x10 oneof body (8 bytes) ── scalar value, or sub-message pointer
+0x1c oneof_case_ (uint32) ── active arm's AutoProto FIELD NUMBER; 0 = unset = AUTO
```text
`+0x1c` 处的判别器是活跃分支的*字段号*,而不是从 0 开始的索引:bool 是分支 #1,int64 #2,uint64 #3,int32 #4,uint32 #5,double #6,float #7,string #8,然后是更高编号的 enum 和 message 分支(见 [autoproto-message-arms.md](autoproto-message-arms.md))。`+0x10` 处的 body 覆盖所有分支:`bool` 在那里读取一个字节,`int64` 读取八个字节,`double` 通过 `vmovsd` 读取,子消息读取一个指针。读取器根据 `oneof_case_` 判断如何解释这八个字节。
> **注意 —** `+0x1c` 偏移是从对象基址开始的 `4*7` 个 word,在反编译中可见为 `*((_DWORD *)a1 + 7)`(bool 解析器)和 `*(_DWORD *)(a2 + 28)`(bool 读取器)。不要把它与 proto 的 `_has_bits_` 混淆;oneof 没有 has-bits,case 字段*就是*存在性指示器。
### 全 AUTO 默认实例
```text
AutoProto_globals_ @ 0x223c8968 (.data file off 0x21fc8968)
+0x10 oneof body = 0x0
+0x1c oneof_case_ = 0 ⇒ AUTO这是每个未设置开关指向的单一默认实例。当某个 TCE AutoProto* 字段为 null(env 从未设置它)时,消费者在调用解析器前会替换为 &AutoProto_globals_,每个访问器中均确认为 if (!v1) v1 = &AutoProto_globals_;。因为它的 oneof_case_ 为 0,解析器返回全零打包代码 (0x000),而消费者的极性决定具体默认值。因此,三态开关的默认值不是一个存储的数字,而是消费者中的一条解析规则(§3)。
2. AutoOr<T> 解析器模板
目的
AutoOr<T>::FromProtoOrDie(const AutoProto&) 是把 AutoProto 转换为打包 (present, value) 代码的函数。它以 C++ 值类型为模板参数;二进制中按类型类别携带不同实例化,每个实例化骨架相同,但打包方式依类型而异。OrDie 后缀对实际路径来说名不副实,因为 AUTO guard 会在 fatal 路径可达之前返回。
入口点
<accessor>(ObjectView<TpuCompilationEnvironment>) ── 0x1d6b6420..0x1d6b9f60
├─ load env[+OFF] ── the AutoProto* for this knob
├─ if null → &AutoProto_globals_ ── 0x223c8968 (all-AUTO)
├─ AutoOr<T>::FromProtoOrDie(autoproto) ── 0xf795300 (bool) / 0x1092f7e0 (int64) / …
│ └─ AutoOrTypeTraits<T>::FromAutoProto ── 0xf7953e0 (bool); checks +0x1c, reads +0x10
└─ apply consumer polarity ── bit test on the packed return (§3)
```text
### 算法
bool 解析器 `@ 0xf795300` 是规范函数体;其他类型类别只在最终打包上不同:
```c
function AutoOr<bool>::FromProtoOrDie(AutoProto* p): // 0xf795300
if p->oneof_case_ == 0: // *((_DWORD*)p + 7) == +0x1c
return 0 // AUTO ⇒ packed 0x000, present-bit clear
sor = AutoOrTypeTraits<bool>::FromAutoProto(p) // 0xf7953e0 → StatusOr<bool>
if not sor.ok(): // unreachable: case!=0 guarantees the arm is set
LOG(FATAL) << "Failed to convert AutoProto into an AutoOr: " // flag_types.h:845
<< sor.status() << "\nProto: " << AutoProtoToStr(p)
val8 = sor.value // the byte read at p+0x10 by the traits reader
return val8 | 0x100 // SET present-bit (bit8); pack (present<<8)|val8分支读取器确认了布局和存在性语义:
function AutoOrTypeTraits<bool>::FromAutoProto(out, AutoProto* p): // 0xf7953e0
if p->oneof_case_ != 1: // *(_DWORD*)(p+28) != 1 — bool is arm #1
out = MakeError("bool is not set in AutoProto: " + AutoProtoToStr(p)) // flag_types.h:447
return // StatusOr not-ok
out.value = *(byte*)(p + 0x10) // body byte
out.ok = 1 // StatusOr ok-slot
```text
> **怪异点 —** `FromProtoOrDie` 会在调用读取器*之前*先以 `oneof_case_ == 0` 做 guard,而读取器只有在 case 是某个*其他*分支编号(类型不匹配,例如由 bool 读取器读取 int64 分支)时才会 fatal。因此,对于类型正确的开关,`LOG(FATAL)` 路径是死路径:未设置的 oneof 返回 `0x000`,类型正确的已设置 oneof 返回 `val | 0x100`。重新实现者可以省略 fatal 分支并保持行为,但必须精确复现这个*双 bit 代码*:present 在 bit 8,value 在 bit 0,因为消费者按位置测试它。
### 按类型类别的打包返回表示
present-bit 对每种类型类别处于不同位置,因为值占用不同宽度,且打包不得冲突:
| 类型类别 | 解析器 | 打包返回值 | Present-bit | 已验证 |
|---|---|---|---|---|
| `bool` | `AutoOr<bool>::FromProtoOrDie @ 0xf795300` | `(present<<8) \| val8` | bit 8 (`\| 0x100`) | CONFIRMED — `return v11 \| 0x100u` |
| `int32` / enum (`_Value`) | `AutoOr<int>::FromProtoOrDie @ 0x10979760` · `AutoOr<EffortLevel>::FromProtoOrDie @ 0x109294a0` | `(present<<32) \| val32` | bit 32 (`\| 0x100000000`) | CONFIRMED — `return v11 \| 0x100000000LL` |
| `int64` | `AutoOr<long>::FromProtoOrDie @ 0x1092f7e0` | `{rax = value, dl = has}` | 成对的 `dl` 字节 (`test $1,%dl`) | CONFIRMED — 返回 `v10[1]`,has-bit 在 `dl` |
| `uint32` / `uint64` / `double` / `float` | 类似的实例化 | value 在低位,present 在该宽度的最高位 / 成对 has | 按类型 | HIGH(惯用法匹配 bool/int) |
| `string` / message | message traits(§ message arms) | `{value-or-submessage, separate has-byte}` | 单独 has-byte | HIGH(struct-return,has 在 `+0x58`) |
enum 案例已通过 `AutoOr<ExecutionOptions_EffortLevel>::FromProtoOrDie @ 0x109294a0` 验证,其函数体除最终 `return v11 | 0x100000000LL` 外与 bool 解析器逐字节相同:因为 enum 值占用低 32 位,present-bit 移到了 bit 32。int64 案例 `@ 0x1092f7e0` 返回 `v10[1]`(值寄存器),并在 `dl` 中携带 has-bit,这是经典的双寄存器 `StatusOr` 风格返回,因此其消费者测试 `dl & 1`,而不是值字中的某个 bit。
> **陷阱 —** 不存在单一的 `AutoOr<T>` ABI。若重新实现为每种类型都返回统一的 `{bool present; int64 value}` struct,将与消费者不匹配,因为消费者是按*打包*返回值编译的,并测试固定 bit(`0x100`、`0x100000000` 或 `dl & 1`)。present-bit 位置是契约的一部分,不是实现细节。
---
## 3. `AUTO` → 具体 fallback
### 目的
当 oneof 未设置时,`FromProtoOrDie` 返回全零打包代码。消费者访问器把该代码转换为具体编译值,且*消费者硬编码 `AUTO` 变成什么*。不存在字段级默认表;默认值是每个访问器末尾的一段指令序列。五种惯用法覆盖整个统计集合。
### 惯用法 A — `AUTO=off` bool(45 个开关)
只有显式 `true` 会启用;`AUTO` 和显式 `false` 都解析为 `false`。
```c
function MxuLatencyBalancingUseSequenceDependencies(env): // 0x1d6b9c80
p = env[+0xbe8] // the AutoProto*
if !p: p = &AutoProto_globals_ // 0x223c8968
code = AutoOr<bool>::FromProtoOrDie(p) // 0x000 if AUTO, val|0x100 if set
return (~code & 0x101) == 0 // true IFF present(bit8) AND value(bit0)(~code & 0x101) == 0 仅当 bit 8(present)和 bit 0(value)都置位时为 true,也就是打包代码恰为 0x101。AUTO (0x000) 和 present-false (0x100) 都得到 false。因此 MxuLatencyBalancingUseSequenceDependencies 在 v0.0.40 中默认 OFF:全 AUTO 的 AutoProto_globals_ 解析为 0x000,无法通过 == 0x101 测试,返回 false。45 个 AUTO=off 开关集合(偏移见 tce-field-offsets-defaults.md)都共享这个 not; test $0x101; sete 函数体,其中包括 EnablePipelinedLoopUnrolling (+0x2f0)、EnableIlpLatencyHidingScheduler (+0x648)、ForceAsyncAllToAll (+0xbc8)、EnableDataDependentScOpAggregation (+0xc40)。
惯用法 B — AUTO=on bool(26 个开关)
只有显式 false 会禁用;AUTO 和显式 true 都解析为 true。
function AllowSplitVmem(env): // 0x1d6b70a0
p = env[+0x4a8]
if !p: p = &AutoProto_globals_
code = AutoOr<bool>::FromProtoOrDie(p)
return (code & 0x101) != 0x100 // false ONLY when present AND value==0
```text
`(code & 0x101) != 0x100` 仅对精确代码 `0x100`(present,value 0)为 false。`AUTO` (`0x000`) 和 present-true (`0x101`) 都得到 `true`。因此 **`AllowSplitVmem` 默认 ON**,并且它背后使用的是与上面 OFF 默认开关相同的全 AUTO `AutoProto_globals_`。26 个 `AUTO=on` 开关集合共享这个 `and $0x101; cmp $0x100; setne` 函数体,包括 `EnableMsaSyncCopyReplacement` (`+0x2f8`)、`EnableCollectivePipeliner` (`+0x8a8`)、`EnableScsOverlays` (`+0xc50`)、`IsMosaicCompatibilityModeEnabled` (`+0x470`)。
> **怪异点 —** 同一个打包代码 `0x000` 在惯用法 A 和惯用法 B 中解析为相反的布尔值。极性不在字段、proto 或默认实例中,而是访问器末尾的字面算术。重新实现者无法从存储推导开关默认值;必须读取消费者的测试。这是本页最重要的事实:**bit 极性就是文档化默认值。**
### 惯用法 C — `AUTO` → int64 sentinel(18 个开关)
int64 解析器在 `rax` 中返回值,在 `dl` 中返回 has-bit。消费者在 has-bit 清除时替换为每个开关自己的常量:
```c
function DcnTransferCountThreshold(env): // 0x1d6b64e0
p = env[+0xbd0]
if !p: p = &AutoProto_globals_
value = AutoOr<long>::FromProtoOrDie(p) // value in rax, has-bit in dl
if (has & 1) == 0: // AUTO ⇒ has clear
return 0x7FFFFFFFFFFFFFFF // INT64_MAX sentinel
return valueAUTO sentinel 随开关而变,它就是该访问器用 cmove 代入的常量。已解码 12 个;其余 6 个遵循相同的 test $1,%dl; cmove rcx 惯用法,但各自有自己的常量:
| 开关 | Env 偏移 | AUTO sentinel |
|---|---|---|
DcnTransferCountThreshold | +0xbd0 | INT64_MAX (0x7fffffffffffffff) |
IciRsPipeliningThresholdBytes | +0xa98 | INT64_MAX |
AllGatherMinBytesForSparseCoreOffload | +0xaf0 | 0 |
AllGatherStepCount | +0x8a0 | 1 |
GatherExpanderConcatElementGatherThreshold | +0x600 | 4 |
RaggedAllToAllMaxRdmaSizeKib | +0x658 | 8 |
SparseCoreOffloadQueuingOverlapLimit | +0x738 | 64 |
RotatedPincerVmemShardCopyLoopIterNum | +0xbd8 | 64 |
MaxNumOperandsToEnableWindowCheck | +0xb60 | 128 |
HostCommandHandlerReapInterval | +0xba0 | 1024 |
AutoMaxMetadataStringLength | +0x688 | 100000 |
MaxFetchAndAddValue | +0x8c8 | 1000000000 |
惯用法 D — AUTO → enum-0 / int-default(11 个开关)
int32/enum 解析器打包 (present<<32)|val32。消费者测试 bit 32,并在 AUTO 时替换为 0(DEFAULT/第一个 enum 值,或零):
function GetBufferAssignmentAlgorithm(env): // 0x1d6b9cc0 (pattern)
p = env[+0xc18]
if !p: p = &AutoProto_globals_
code = AutoOr<EnumT_Value>::FromProtoOrDie(p) // (present<<32)|val32
if bt(code, 32): // present?
return (int32)code // the explicit enum/int value
return 0 // AUTO ⇒ enum-0 / zero
```text
`bt $0x20; cmovb; else 0` 函数体覆盖 `GetMlirVerifierOptions` (`+0x978`)、`ScHbmSpillStack` (`+0xc68`, int32)、`TpuScatterExpanderAutounrollFactor` (`+0x800`, int32)、`NumSerializedTablesToOptimizeHbm` (`+0x558`, uint32)、float 类型的 `SparseCoreMismatchDetectorAtol`/`Rtol` (`+0x340`/`+0x348`),以及 `SparseCoreElementwiseShapeScalingFactor` (`+0xb48`, float)。对 enum 案例而言,`0` 是 proto 的第一个/默认 enum 值;对 int 案例而言,是字面零。
### 惯用法 E — `AUTO` → message 默认实例
message 类型的 `AutoProto` 分支(12 个子消息分支)会把 `AUTO` 解析为*空的默认子消息*,而不是标量。`GetIlpLatencyHidingSchedulerOptions` 通过 `FromProtoOrDie @ 0x1d6bb6e0` 把 `AUTO` 解析为 `IlpLatencyHidingSchedulerOptions(arena)`(空消息);`GetSparseCoreAssertLevel` 通过 `FromProtoOrDie @ 0x1d6bcb20` 把 `AUTO` 解析为代码定义的 level。子消息的*内部*字段默认值由 [autoproto-message-arms.md](autoproto-message-arms.md) 负责;这里的 AUTO 答案仅仅是“该分支消息类型的一个空实例”。
### 惯用法 F — primary/override composite(已解码 2 个)
少数访问器读取*两个* `AutoProto` 字段,第二个字段在显式 present 时覆盖第一个字段:
```c
function ShouldEnablePostMsaSyncSliceFusion(env): // composite
base = resolve(env[+0x548], AUTO=off) // primary knob, Idiom A
override = AutoOr<bool>::FromProtoOrDie(env[+0x448])
if (override & 0x100) == 0: // override not present
return base
return override & 1 // explicit override winsPadOperationsInputTiles (+0x618) 是第二个已解码 composite,遵循相同的 present-bit gate。其余 composite(EnableMixedPrecisionAddTransform 读取 +0x830&+0x840;AllReduceMinBytesForSparseCoreOffload 读取 +0x1590&+0xa48)遵循该模式,但其第二字段语义尚未逐一解码 (LOW)。
4. 解析器访问器区段
目的
全部 130 个单字段三态解析器位于一个 .text 区段 0x1d6b6420 .. 0x1d6b9f60,每个都是自由函数 xla::jellyfish::<Name>(ObjectView<TpuCompilationEnvironment>)。它们结构一致:在固定 env 偏移处加载字段的 AutoProto*,以 AutoProto_globals_ 作为 null fallback,调用有类型的 FromProtoOrDie,再应用 §3 中的一种惯用法。该区段是可读的编译开关表面,也就是进入原本按偏移索引的 TCE 的人类命名入口。
分派维度
该区段是一个 130 行空间,但可由三条轴完整描述,因此这里无需倾倒 130 行(偏移和 flag 名称见 tce-field-offsets-defaults.md):
| 轴 | 取值 | 来源 |
|---|---|---|
| 类型类别 | bool, int64, int32/enum, float, message | 被调用的 AutoOr<T> 实例化 |
AUTO 极性 | off / on / sentinel / enum-0 / msg-default / composite | 访问器尾部的 §3 惯用法 |
| Env 偏移 | 每个开关一个,+0x270 .. +0xcd0(AutoProto* 字段) | _table_ 字段号→偏移映射 |
该区段统计:45 个 AUTO=off bool、26 个 AUTO=on bool、18 个 int64 sentinel、11 个 enum/int bt-32、2 个 primary/override composite,外加 enum-instance 和 message-default 分支。少数多字段 composite 读取 2 个以上 AutoProto 字段,并单独计数。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
AutoOr<bool>::FromProtoOrDie | 0xf795300 | bool 解析器 — (present<<8)|val8 打包 |
AutoOrTypeTraits<bool>::FromAutoProto | 0xf7953e0 | bool 分支读取器 — +0x1c==1,body +0x10 |
AutoOr<long>::FromProtoOrDie | 0x1092f7e0 | int64 解析器 — {rax, dl} has-pair |
AutoOr<int>::FromProtoOrDie | 0x10979760 | int32 解析器 — (present<<32)|val32 |
AutoOr<ExecutionOptions_EffortLevel>::FromProtoOrDie | 0x109294a0 | enum 解析器 — | 0x100000000 |
AutoProto::_table_ | 0x21cfa788 | 30 分支 oneof 的解析表 |
AutoProto_globals_ | 0x223c8968 | 全 AUTO 默认实例 |
MxuLatencyBalancingUseSequenceDependencies | 0x1d6b9c80 | AUTO=off 示例 (+0xbe8) |
AllowSplitVmem | 0x1d6b70a0 | AUTO=on 示例 (+0x4a8) |
DcnTransferCountThreshold | 0x1d6b64e0 | int64 sentinel 示例 (+0xbd0, INT64_MAX) |
GetBufferAssignmentAlgorithm | 0x1d6b9cc0 | enum bt-32 示例 (+0xc18) |
EnableLloLinter | 0x1d6b6740 | inline-Tristate 示例 (+0x15ac, §5) |
注意 — 不要混淆这两个解析表。
AutoProto::_table_ @ 0x21cfa788是 AutoProto 消息自己的TcParseTableBase,也是驱动 30 分支FromAutoProto反射的 descriptor。TCE 的主_table_ @ 0x21cfa9e0(1121 个字段号→偏移的 oracle)是附近地址处的另一个表:它们在.data.rel.ro中相邻,是因为两个 proto 位于同一翻译单元。应按符号交叉引用,而不是凭地址观察。
5. 第二存储类 — inline TristateProto
目的
并非每个三态开关都是 AutoProto。第二类存储方式有 67 个字段,它把三态存为 inline TristateProto.Value 枚举,也就是 TCE 结构体中的一个直接 4 字节字段,没有 AutoProto 指针,也没有 AutoOr<T> 调用。语义相同(AUTO/ENABLED/DISABLED),但机制是直接枚举比较。重新实现者必须同时处理两类;它们不能互换。
算法
function EnableLloLinter(env): // 0x1d6b6740
return env[+0x15ac] == 2 // ENABLED == 2; AUTO==0, DISABLED==1 → false
```text
没有解析器,没有 fallback 实例,也没有 present-bit。枚举值直接通过 `cmpl $0x2, +OFF(%rdi); sete` 读取。`ENABLED` 是常量 `2`;`AUTO` (`0`) 和 `DISABLED` (`1`) 比较结果都为不相等,因此都解析为 `false`。这里的 `AUTO`→具体值是字段的 *proto enum 默认值*(`TristateProto` 统计分为 21 个 default-`ENABLED`、37 个 default-`AUTO`、9 个 default-`DISABLED`)。
### 两类的区别
| 属性 | 类 A — `AutoProto*` | 类 B — inline `TristateProto` |
|---|---|---|
| 存储 | `AutoProto*` 指针字段 | 4 字节 enum 字段 |
| 字段数量 | ~330(130 个命名单字段解析器) | 67 |
| 解析器 | `AutoOr<T>::FromProtoOrDie` + 极性 | 直接 `cmpl $2,+OFF; sete` |
| Present 模型 | 打包 present-bit(bit 8 / 32 / `dl`) | enum 值 (`ENABLED==2`) |
| `AUTO` 默认值 | 消费者极性 (§3) | proto enum 默认值 |
| Null fallback | `&AutoProto_globals_` | n/a(值为 inline) |
> **陷阱 —** 开关名称(`Enable…`/`Should…`)无法告诉你它的类别。`EnableLloLinter` 是 inline `TristateProto` (`cmpl $2`),而 `EnablePipelinedLoopUnrolling` 是 `AutoProto`(`AUTO=off` bool)。枚举三态开关时,重新实现者必须按访问器函数体分类:pointer-load-and-resolve(类 A)对比 inline-compare(类 B),而不是按名称或其听起来像三态的类型分类。
---
## 相关组件
| 组件 | 关系 |
|---|---|
| `AutoOr<bool>::FromProtoOrDie @ 0xf795300` | 解析器模板 — present-bit 打包 |
| `AutoProto_globals_ @ 0x223c8968` | 每个未设置开关 fallback 到的全 AUTO 默认实例 |
| `AutoProto::_table_ @ 0x21cfa788` | 驱动 `FromAutoProto` 的 30 分支 oneof 解析表 |
| `ObjectView<TCE>` band `0x1d6b6420..0x1d6b9f60` | 130 个命名单字段解析器及其极性尾部 |
| `TpuCompilationEnvironment` (TCE) | 托管 `AutoProto*` 字段和 inline `TristateProto` 字段 |
## 交叉引用
- [overview.md](overview.md) — 三层配置流水线;本页负责 Stage 3,即 AUTO 解析器
- [tpu-compilation-environment.md](tpu-compilation-environment.md) — 托管 `AutoProto*` 与 inline-`Tristate` 字段的 TCE proto,以及字段号→偏移 oracle
- [tce-field-offsets-defaults.md](tce-field-offsets-defaults.md) — 字节精确的字段号→偏移→默认值映射;此处每个解析器引用的 env 偏移
- [autoproto-message-arms.md](autoproto-message-arms.md) — 30 分支 oneof 和 12 个 message 分支的子默认值(惯用法 E)
- [autoor-parse-grammar.md](autoor-parse-grammar.md) — 字符串→`AutoProto` 入口(`auto`/`enabled`/`disabled`/literal)
- [autoor-unparse.md](autoor-unparse.md) — 反向 `AbslUnparseFlag<AutoOr<T>>` 文本方向