AutoOr 解析语法
地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so。其他版本会有所不同。
摘要
当用户设置 XLA_FLAGS=--xla_tpu_<knob>=auto(或 true、0.5、42、MODE_NAME)时,libtpu 不会把原始字符串交给通用 flag 解析器。该 token 会经过 AutoOr<T> 特有的两阶段路径。首先,一个以 FieldDescriptor 为键的 arm 选择器,即 ParseAutoOrFromString<30> @ 0x1d7504c0,尾调用到 <25> @ 0x1d7eac40,通过 CommandLineFlag 的类型标签查找该 flag 注册的 C++ 类型,并分派到该类型唯一的 AutoOr<T>::ParseFlag 实例。真正的语法在这个按类型划分的解析器里:30 个实例都以完全相同的 "auto" 哨兵检查开头;如果不匹配,则委托给 T 的标准 absl/proto2 原语(SimpleAtob、safe_strto32_base、SimpleAtod、EnumDescriptor 名称查找、proto-text 读取器,等等)。解析出的值随后被打包成另一侧 round-trip 解析器会读回的精确 present-bit / value 编码。
参照框架是 absl::ParseFlag / AbslParseFlag,这是 absl 用来教其 flag 库识别新类型的定制点。AutoOr<T> 就是这样一种类型,ParseFlag 是它的 AbslParseFlag 重载。libtpu 特有的变化在于双层结构:"auto" 哨兵在 absl 原语之上检查,使用原始、大小写敏感的 bcmp 与惰性构造的 std::string "auto" 比较,因此只有精确的小写四字节 token 是 AUTO 哨兵;而它下面的类型化 token 集(true/yes/1、枚举名)是大小写不敏感的,因为 absl/proto2 原语会折叠大小写。若重新实现者把整个语法当作大小写不敏感,或把 "AUTO" 路由到哨兵,就会偏离二进制行为。
本页负责输入语法:两个 arm-arity 分派器以及 flag 如何到达它们;通用的 "auto" token 检测惯用法;按类型类划分的解析器分派(bool / int / float / string / enum / message)及其接受的 token 集;以及把每个解析结果打包回 (present, value) 编码。该编码解析为具体编译值的解析阶段,即 present-bit 位置、全 AUTO 默认实例、按消费者区分的 AUTO→default 极性,由 autoproto-autoor-resolution.md 负责;反向文本输出方向由 autoor-unparse.md 负责;结构化 message arm 子语法由 autoproto-message-arms.md 负责。本页是 string→code 的一半;那一页是 code→value 的一半。两者合起来闭合 round-trip。
重新实现时,契约是:
- arm 选择器分派器 ——
ParseAutoOrFromString<30>/<25>不是类型解析器;它们根据 flag 的CommandLineFlag类型标签(call *0x38)切换,调用匹配的AutoOr<T>::ParseFlag,通过NormalizeFieldType<AutoOr<T>>折叠结果,并在失败时发出每个 arm 的错误。 "auto"哨兵 —— 每个ParseFlag实例都以长度比较 + 大小写敏感的bcmp开头,对象是 lazy-staticstd::string "auto";匹配时写入全零(present-bit-clear)编码,并在类型化解析器运行之前返回成功。- 按类型打包 —— bool
(val | 0x100),int32/enum(val | 0x100000000),int64/uint64/double/float{value@+0, has=1@+8},string{body, idx@+0x18=0, has@+0x20=1},与 resolver 读回的代码逐字节一致。
| arm 选择器 (30) | ParseAutoOrFromString<30> @ 0x1d7504c0 — 5-arm 前缀,尾调用到 <25> |
| arm 选择器 (25) | ParseAutoOrFromString<25> @ 0x1d7eac40 — 24-arm 链,默认 "Not an AutoOr." |
| 分派键 | call *0x38(CommandLineFlag) → 类型标签,与 FastTypeTag<AutoOr<T>>::kDummyVar 比较 |
| 每个 arm 的折叠 | AutoOr<T>::ParseFlag(value, &out, &err) 然后 NormalizeFieldType<AutoOr<T>>(sret, out, fd) |
"auto" 字面量 | .rodata 0x85ca81f "auto" — 所有 30 个 ParseFlag 实例中的 lazy std::string ctor |
| bool 解析器 | AutoOr<bool>::ParseFlag @ 0x1d6ba160 → SimpleAtob;打包 val | 0x100 |
| int32 解析器 | AutoOr<int>::ParseFlag @ 0x1d744080 → safe_strto32_base;打包 val | 0x100000000 |
| int64 解析器 | AutoOr<long>::ParseFlag @ 0x1d743480 → safe_strto64_base;打包 {value, has@+8} |
| enum 解析器 | 例如 AutoOr<ScavengingMode_Value>::ParseFlag @ 0x1d748280 → AbslParseFlagImpl 名称查找 |
| 错误字符串 | "Failed to parse '%s' into flag %s: %s" (0x8584d92);enum 0xa0307af;无 arm 0xa03f8b1 |
| 置信度 | CONFIRMED(字节锚定 vs 反编译),除非某行或 callout 另有说明 |
1. 入口路径
目的
ParseAutoOrFromString<N> 是显式值输入:调用者提供 flag、它的 proto2::FieldDescriptor 和一个原始 string_view,并得到 StatusOr<variant<…20…>> —— 已解析的 AutoOr<T> 被折叠进 20 个备选项的 TCE field variant。当 absl flag 库为一个注册为 AutoOr<T> 的 flag 解析 XLA_FLAGS token 时,会到达这里。
flag 值如何到达
XLA_FLAGS=--xla_tpu_<knob>=<value>
└─ absl flag registry parses the raw string into the flag's STORED type
(the flag is registered as AutoOr<bool>; absl invokes
AutoOr<bool>::ParseFlag at flag-SET time — §3's grammar runs here first)
└─ xla::jellyfish::SetFieldFromFlagString @ 0x1d73fcc0 (free function)
/ TpuCompEnvReflection::ReadFlag @ 0x1d74af60 ── bridge flag → TCE env
├─ ReadFlag dispatches on CommandLineFlag::*0x38 (the flag's TypeId)
│ ├─ ReadFlagImpl<T> ── non-AutoOr wrapper types (plain bool/float/…)
│ └─ ReadAutoOr<30> @ 0x1d74ca00 / ReadAutoOr<25> @ 0x1d785f00
│ ── for AutoOr-typed fields; mirrors ParseAutoOrFromString<N>
│ but re-parses the flag's CURRENT value (CommandLineFlag::*0x18)
└─ NormalizeFieldType<AutoOr<T>> folds AutoOr<T> into the variant
└─ SetEnvField @ 0x1d752ae0 writes it into the TCE AutoProto oneof
```text
同一套按类型语法有两个入口点。`ParseAutoOrFromString<N>` 把值作为参数(显式字符串路径);`ReadAutoOr<N>` 从 `CommandLineFlag` 读取 flag 的当前值并重新解析。两者共享 §3 的 `AutoOr<T>::ParseFlag` 实例,因此这里描述的语法是两条路径的唯一事实来源。
> **NOTE —** 每个 arm 错误路径的文件/行锚点是 `platforms/xla/service/jellyfish/tpu_compilation_environment_reflection.cc`(字符串 `0x877a8ca`),可在 `ParseAutoOrFromString<30>` 内 `"Failed to parse…"` 分支的 `MakeErrorImpl<3>` 调用中看到。分派器和 `ReadAutoOr` 镜像位于同一个翻译单元。
---
## 2. arm 选择器分派器
### 目的
`ParseAutoOrFromString<30>` 和 `<25>` 是 **arm 选择器,而不是解析器**。每个都是由 flag 注册的 C++ 类型驱动的直线 if 链。`<N>` 是 oneof arity:`<30>` 覆盖 30-arm `AutoProto` oneof,`<25>` 覆盖 25-arm 子集。`<30>` 处理 30-arm oneof 独有的 5 个 arm,然后尾调用 `<25>` 处理共同部分。`<25>` 处理 24 个 arm,并落入 `"Not an AutoOr."` 错误。
### 算法
分派键是 flag 在 vtable offset `0x38` 的类型标签访问器,与每个候选 arm 的编译期 `FastTypeTag<AutoOr<T>>::kDummyVar` 地址比较:
```c
function ParseAutoOrFromString<30>(sret, CommandLineFlag& flag, // 0x1d7504c0
FieldDescriptor& fd, string_view value):
tag = flag.vtable[0x38](flag) // *(*(flag)+56)(flag) — registered TypeId
// ── the 5 arms in the 30-arm oneof but NOT in the 25-arm one ──
if tag == &FastTypeTag<AutoOr<BufferAssignmentAlgorithmProto_Value>>::kDummyVar:
ok = AutoOr<…>::ParseFlag(value.ptr, value.len, &local, &err) // 0x1d74a900
if ok: NormalizeFieldType<AutoOr<…>>(sret, local, fd) // build variant arm
else: sret = MakeErrorImpl("Failed to parse '%s' into flag %s: %s") // 0x8584d92
return
if tag == &FastTypeTag<AutoOr<TpuCustomCallMemorySpaceSpec>>::kDummyVar: … // 0x1d74a3c0
if tag == &FastTypeTag<AutoOr<BundleInstrumentationOptions>>::kDummyVar: … // 0x1d749ce0
if tag == &FastTypeTag<AutoOr<SparseCoreAssertLevel>>::kDummyVar: … // 0x1d7495e0
if tag != &FastTypeTag<AutoOr<AccumulatorTransformations>>::kDummyVar: // 0x1d748c80
return ParseAutoOrFromString<25>(sret, flag, fd, value.ptr, value.len) // TAIL CALL
… handle AccumulatorTransformations …function ParseAutoOrFromString<25>(sret, flag, fd, value): // 0x1d7eac40
tag = flag.vtable[0x38](flag)
if tag == &FastTypeTag<AutoOr<bool>>::kDummyVar: … ParseFlag @0x1d6ba160 …
if tag == &FastTypeTag<AutoOr<int>>::kDummyVar: … ParseFlag @0x1d744080 …
… 22 more arms (scalars + remaining enum/message types) …
// no arm matched: this FieldDescriptor is not an AutoOr-typed TCE field
sret = MakeErrorImpl("Not an AutoOr.") // 0xa03f8b1
```text
每个匹配的 arm 在成功时只做两件事:用原样的 `value` 调用该类型的 `ParseFlag`,然后调用 `NormalizeFieldType<AutoOr<T>>(sret, local, fd)` 来构造带正确标签的 variant 备选项(`fd` 参数只决定 variant 标签,不参与解析)。如果 `ParseFlag` 失败,该 arm 通过 `MakeErrorImpl<3>` 把 `"Failed to parse '%s' into flag %s: %s"` status 构建到 `sret` 中,格式化值、flag 名称(`flag.vtable[0]`)和解析器的错误字符串。反编译显示每个 arm 都重复 `call *(…+56)` 类型标签访问器,以及用于把每个 arm 的 scratch variant 清零初始化的 `vmovaps`/`vxorps`;该 if 链完全展开,不是 switch table。
### 5 vs 24 的拆分
> **QUIRK —** `<30>` 分派器存在的唯一目的,是先剥离 30-arm oneof 中有而 25-arm oneof 中没有的 5 个 arm,然后尾调用 `<25>`。重新实现者可以把它折叠成行为相同的单个 30 路 switch;两个函数的拆分是模板实例化产物(同一个泛型中有两个 oneof arity),不是语义边界。5 个前缀 arm 是:`BufferAssignmentAlgorithmProto_Value`(enum)、`TpuCustomCallMemorySpaceSpec`、`BundleInstrumentationOptions`、`AccumulatorTransformations` 和 `SparseCoreAssertLevel`(其余为 message arm)。反编译已验证:`<30>` 主体最后的非匹配是 `... != FastTypeTag<AutoOr<AccumulatorTransformations>> → jmp ParseAutoOrFromString<25>(a1,a2,a3,a4,a5)`。
### 函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
| `ParseAutoOrFromString<30>` | `0x1d7504c0` | 5-arm 前缀选择器 + 尾调用到 `<25>` |
| `ParseAutoOrFromString<25>` | `0x1d7eac40` | 24-arm 选择器 + `"Not an AutoOr."` 默认 |
| `ReadAutoOr<30>` | `0x1d74ca00` | `<30>` 的当前值镜像 |
| `ReadAutoOr<25>` | `0x1d785f00` | `<25>` 的当前值镜像 |
| `TpuCompEnvReflection::ReadFlag` | `0x1d74af60` | 根据 `CommandLineFlag::*0x38` 分派 |
| `xla::jellyfish::SetFieldFromFlagString` | `0x1d73fcc0` | string→TCE 桥接(自由函数) |
| `NormalizeFieldType<AutoOr<T>>` | per-type | 将 `AutoOr<T>` 折叠进 20-alt variant |
---
## 3. `"auto"` token
### 目的
AUTO 哨兵检测是所有 30 个 `ParseFlag` 实例中完全相同的一段语法。它在类型化解析器*之前*检查,因此显式 `auto` 永远不会到达 `SimpleAtob` / `safe_strto32_base` / 枚举查找;它会短路到全零(present-bit-clear)编码,resolver 将其读作 AUTO。
### 算法
bool 实例 `@ 0x1d6ba160` 是规范主体;其他每个类型的序言直到 AUTO 写入宽度之前都逐字节相同:
```c
function AutoOr<bool>::ParseFlag(value_ptr, value_len, AutoOr<bool>* out): // 0x1d6ba160
// lazy function-local static std::string kAuto = "auto"
if !guard.kAuto: // __cxa_guard_acquire
NoDestructor<std::string>::construct(kAuto, "auto") // src literal 0x85ca81f
guard.release()
// kAuto length: SSO uses byte-17 size; long string uses kAuto[1]
auto_len = (kAuto[2] < 0) ? kAuto[1] : SHIBYTE(kAuto[2])
if value_len != auto_len: // length gate before bcmp
goto typed
data = (kAuto[2] < 0) ? kAuto[0] : &kAuto // SSO inline vs heap ptr
if bcmp(value_ptr, data, value_len) == 0: // CASE-SENSITIVE compare
*out = 0 // AUTO: all-zero, present-bit CLEAR
return true
typed:
ok = absl::flags_internal::AbslParseFlag(value_ptr, value_len, &scratch)
if ok: *out = scratch | 0x100 // pack present<<8 | val8
return okkAuto 字符串是惰性构造的函数局部 static(每个模板实例化一个;bool 实例的存储在 .data 中,文件里为零,首次调用时构建)。其构造来源是 .rodata 字面量 0x85ca81f = "auto",已验证 bool、int、long 和 enum 实例都指向同一个 RIP 目标。存在性测试是先比较长度,再做原始 bcmp,所以它是大小写敏感的:只有精确的小写 auto 是哨兵。"AUTO" 和 "Auto" 会落入类型化解析器,在那里失败(没有 scalar 解析器接受它们),除非它们恰好命中某个有效枚举值。
AUTO 写入宽度随类型类而变;这是序言中唯一的按类型差异:
| 类型类 | AUTO 写入(反编译) | 结果编码 |
|---|---|---|
bool | *out = 0 (movw $0,(out)) | 0x000 |
int32 / enum | *out = 0 (movq $0,(out)) | 0 |
int64/uint64/double/float | vmovups %xmm0,(out)(清零 16 B) | {value=0, has=0} |
string | out+0x18 = 0xff; out+0x20 = 0 | absent variant index |
GOTCHA —
"auto"比较是整个语法中唯一大小写敏感的 token。它下面的所有类型化 token 都折叠大小写(bool 通过EqualsIgnoreCase,enum 通过 lower/upper 重试;见 §4、§6)。如果重新实现者在哨兵检查前把输入转成小写,就会错误地接受"AUTO"作为哨兵;如果完全不做大小写折叠,又会错误拒绝"TRUE"。这个拆分是有意的:AUTO 是精确保留字,value token 则对用户友好。QUIRK — AUTO 分支返回
true(解析成功),但 present-bit 清零;它不是错误,也不是存储值。这使得=auto在解析层与“flag 未设置”不可区分:两者都给 resolver 输入0x000编码,而 autoproto-autoor-resolution.md 中的消费者极性决定具体默认值。解析器不知道,也不需要知道,AUTO 会解析为什么。
4. scalar 语法 — bool / int / uint / int64 / uint64 / double / float / string
目的
八个 scalar arm 委托给 absl::flags_internal::AbslParseFlag(T*),它会剥离 ASCII 空白,然后调用 absl 针对 T 的核心 numeric/bool 原语。解析值会打包进 present-bit 编码;空白剥离和 radix-auto 数值行为都原样继承自 absl。
按类型解析器和打包
| 类型 | ParseFlag | absl 被调用方(strip-WS → core) | OK 打包 |
|---|---|---|---|
bool | 0x1d6ba160 | AbslParseFlag(bool*) 0x21112840 → SimpleAtob 0x211716c0 | val | 0x100 |
int32 | 0x1d744080 | AbslParseFlag(int*) 0x21112a40 → safe_strto32_base 0x21173ce0 | val | 0x100000000 |
uint32 | 0x1d743e60 | AbslParseFlag(uint*) 0x21112b60 → safe_strtou32_base 0x211742a0 | val | 0x100000000 |
int64 | 0x1d743480 | AbslParseFlag(long*) 0x21112c80 → safe_strto64_base 0x21173e20 | {value@+0, has=1@+8} |
uint64 | 0x1d7ed180 | AbslParseFlag(ulong*) 0x21112da0 → safe_strtou64_base 0x21174360 | {value@+0, has=1@+8} |
double | 0x1d744be0 | AbslParseFlag(double*) 0x21112ee0 → SimpleAtod 0x21171580 | {dbl@+0, has=1@+8} |
float | (via NormalizeFieldType) | AbslParseFlag(float*) 0x21112ec0 → SimpleAtof 0x21171440 | {flt@+0, has=1@+8} |
string | 0x1d7437e0 | AbslParseFlag(string*) 0x21112f00(原样复制) | {body, idx@+0x18=0, has@+0x20=1} |
bool token 集
SimpleAtob @ 0x211716c0 会把剥离空白后的输入以大小写不敏感方式(通过 EqualsIgnoreCase 0x211711c0 → memcasecmp 0x21171120)按顺序与两个固定集合比较:
TRUE : "true" "t" "yes" "y" "1"
FALSE : "false" "f" "no" "n" "0"
```text
匹配时写入 bool,`ParseFlag` 再打包 `movzbl b; or $0x100` → `(present<<8)|val8`。所以 `=true`/`=yes`/`=1`/`=t`/`=y` → `0x101`;`=false`/`=no`/`=0`/`=f`/`=n` → `0x100`。任何其他 token 都会使 `SimpleAtob` 失败,`AbslParseFlag` 返回 false,然后该 arm 构建 `"Failed to parse '%s' into flag %s: %s"`。
### 数值 radix 和符号
int/uint 解析器调用 `safe_strto{,u}{32,64}_base` 并自动检测 base:前导 `0x`/`0X` 选择 base 16(`cmpb $0x30; cmp $0x78/$0x58` 探测),否则为 base 10;有符号 `strto` 会消费前导 `+`/`-` 符号(`add $0xd5; test $0xfd` sign skip)。int32 打包是 `movabs $0x100000000; or %val,%rdx` —— present-bit 在 bit 32,值在低 32 位。int64/uint64/double/float 打包把值存到 `+0`,并写入字面量 `movb $1, +0x8` has-byte。
> **NOTE —** `-1` token 会按 int32 原生解析为 `0xFFFFFFFF`(低 32 位)并设置 present-bit,即打包为 `0x1FFFFFFFF`。这里没有对 `-1` 做特殊处理;`-1` 的“哨兵”含义(以及 int64 knob 中 `INT64_MAX` 的含义)完全存在于 [autoproto-autoor-resolution.md](autoproto-autoor-resolution.md) 的 resolver/consumer 一侧。解析器只把 `-1` 看成另一个有符号整数。
### string — 无语法
`AutoOr<string>::ParseFlag` `@ 0x1d7437e0` 会把 `value` 原样复制到 variant 的 `std::string` 中(body 位于 `+0/+8/+16` SSO,variant-index 字节 `+0x18 = 0`,has-byte `+0x20 = 1`)。除精确的 `"auto"` 之外,任何字节序列都会被接受;`"auto"` 会先被 §3 哨兵截获(写入 `+0x18 = 0xff` absent-variant index)。这一层没有校验、转义或引号语法。
---
## 5. round-trip 收敛
每个 scalar/enum 解析器发出的打包与 resolver 读回的编码逐字节一致,因此输入和解析方向在每个类型类上收敛到同一种表示:
```text
bool : (present<<8)|val8 =auto/unset → 0x000
=true → 0x101 =false → 0x100
int32 / enum : (present<<32)|val32 =auto → 0
=42 → 0x10000002A
int64/uint64/double/float : {value@+0, has@+8} =auto → {0,0}
=1024 → {1024, has=1}
uint32 : (present<<32)|val32
string : {body, idx@+0x18, has@+0x20} =auto → idx 0xff随后,消费者极性测试(autoproto-autoor-resolution.md §3 —— AUTO=off 的 not;test 0x101;sete 与 AUTO=on 的 and 0x101;cmp 0x100;setne)会把这个打包编码转成有效编译值。用户写入 --xla_tpu_<knob>=true 会落到 0x101;=false 落到 0x100;=auto(或保留 flag 未设置)落到 0x000。flag 字符串和 AutoProto oneof 是同一个打包编码的两种编码形式,因此 =auto token 与从未设置的 field 在下游不可区分。
6. enum 语法 — 名称查找 + 数字回退
目的
10 个 enum arm 接受符号值名称(大小写不敏感)或作为有效枚举编号的裸整数。解析器通过 proto2 EnumDescriptor 解析名称,先尝试小写和大写重试,再回退到数值解析;失败时发出 descriptor 驱动的错误,列出每个有效名称。
算法
以 AutoOr<ScavengingMode_Value>::ParseFlag @ 0x1d748280 为例;所有 enum arm 共享主体,区别只在 *_descriptor() 被调用方:
function AutoOr<EnumT_Value>::ParseFlag(value, value_len, AutoOr<EnumT>* out): // 0x1d748280
… "auto" sentinel (§3); on match *out = 0; return true … // movq $0
desc = EnumT_Value_descriptor() // e.g. 0x1db209e0
ok = proto2::internal::AbslParseFlagImpl(value, &v, desc, &err) // 0x20ec6cc0
if !ok: return false // arm emits enum error
*out = v | 0x100000000 // present<<32 | val32
return true
```text
```c
function proto2::internal::AbslParseFlagImpl(value, int& out, EnumDescriptor& desc, err): // 0x20ec6cc0
node = desc.FindValueByName(value) // 0x20e58020
if node: out = node->number; return true // mov 0x4(node),%eax
lower = AsciiStrToLower(value) // 0xe6b0560
if lower != value and (node = desc.FindValueByName(lower)): out = node->number; return true
upper = AsciiStrToUpper(value) // 0xee994c0
if (node = desc.FindValueByName(upper)): out = node->number; return true
if safe_strto32_base(value, &n, 10): // 0x21173ce0 — bare integer?
node = desc.FindValueByNumber(n) // 0x20e58060
if node: out = n; return true // accept iff it names a real value
err = "Invalid value '%s' for enum '%s'. Supported values are: %s." // 0xa0307af
joining every value name with ", " // 0xa299c7c
return false因此 enum knob 接受:任意大小写的 value name(原样形式,加上小写和大写形式对 EnumDescriptor 的重试);或裸整数,但只有它是有效枚举编号时才接受(FindValueByNumber 作为门控;即便数字解析成功,越界整数也会被拒绝)。失败时错误会枚举每个合法名称,并用 ", " 连接。
enum arm 映射
10 个 enum ParseFlag 实例(每个 → 其 *_descriptor(),再进入共享 AbslParseFlagImpl):
| Enum type | ParseFlag | 分派器 |
|---|---|---|
ScavengingMode_Value | 0x1d748280 | <25> |
Bf16EmissionMode_Value | 0x1d7488e0 | <25> |
MlirVerifierOptions_Value | 0x1d7486c0 | <25> |
CollectiveMatmulModeProto_Value | 0x1d748060 | <25> |
ExpandedScopedAlternateMemoryMode_Value | 0x1d747760 | <25> |
ExecutionOptions_EffortLevel | 0x1d747360 | <25> |
RematerializationOptions_RematerializationAlgorithm | 0x1d7484a0 | <25> |
PrecisionTracerModeProto_Value | 0x1d7ecbc0 | <25> |
SkipConfigTypeProto_Value | 0x1d7eccc0 | <25> |
BufferAssignmentAlgorithmProto_Value | 0x1d74a900 | <30>(前缀) |
NOTE — 符号普查显示正好有 10 个
AutoOr<EnumT_Value>::ParseFlag实例,上表列出了全部 10 个。九个经由<25>;第十个(BufferAssignmentAlgorithmProto_Value)是五个<30>前缀 arm 之一。每个实例都共享同一主体:descriptor 查找 + lower/upper 重试 + numeric fallback。
7. message 语法 — proto-text 进入默认 sub-message
目的
message arm 会把结构化 proto-text 值解析进一个默认 sub-message 实例。有 13 个 message 类型的 ParseFlag 实例;其中 12 个可由 <30>/<25> 分派器到达(第 13 个 CostModelLoggingOptions @ 0x12fcfd00 有 ParseFlag 实例,但没有被任何选择器分派)。"auto" 哨兵之上的语法是 key=value 读取器,而不是 scalar 原语;解析出的 sub-message 与 has-byte 一起保存在 variant 中。
算法
以 AutoOr<CostModelFlagOptions>::ParseFlag @ 0x1d744f80 为例:
function AutoOr<MsgT>::ParseFlag(value, value_len, AutoOr<MsgT>* out): // 0x1d744f80
… "auto" sentinel (§3); on match → empty default-instance; return true …
msg = MsgT(arena) // construct, e.g. 0x1db23d40
ok = proto2::Message::AbslParseFlagImpl(msg, value, &err) // 0x20ef2120
if !ok: return false
out.variant = move(msg); out.has = 1
return true
```text
`proto2::Message::AbslParseFlagImpl` `@ 0x20ef2120` 会按某个分隔符(absl `ByChar` Splitter `@ 0xe6d1240`)拆分值,并通过 `Message` vtable slot(`*0x10`)把每个 key=value 对交给按字段 setter。AUTO 情况生成空的默认实例,也就是 [autoproto-message-arms.md](autoproto-message-arms.md) 在解析方向枚举的同一个默认表面。
### message arm 映射
13 个 message 类型的 `ParseFlag` 实例(`CostModelLoggingOptions` 有实例但没有分派器 arm):
```text
0x1d744f80 CostModelFlagOptions 0x1d745680 EmitterLearnedCostModelOptions
0x1d745d80 SparseCoreOffloadingOptions 0x1d746460 RepeatedStrings
0x1d744420 RepeatedIntegers 0x1d746e20 ShardyOptions
0x1d747b00 IlpLatencyHidingSchedulerOptions 0x12fcfd00 CostModelLoggingOptions
0x1d7ecdc0 BufferContentsSanitizerConfig
0x1d748c80 AccumulatorTransformations (<30> prefix)
0x1d7495e0 SparseCoreAssertLevel (<30> prefix)
0x1d749ce0 BundleInstrumentationOptions(<30> prefix)
0x1d74a3c0 TpuCustomCallMemorySpaceSpec(<30> prefix)GOTCHA — 精确的 message 文本语法尚未字节确认。反编译显示
AbslParseFlagImpl按分隔符(ByCharSplitter)拆分,并通过*0x10按字段分派,但分隔符字符、语法是标准 protoTextFormat还是自定义key=val,key=val形式,以及每个字段的 tokenization 都没有转录(LOW)。重新实现者如果要支持 message-arm CLI override(例如--xla_tpu_<msg_knob>=field=val,field2=val2),必须先对照二进制验证分隔符再依赖它。上面的 scalar 和 enum 语法是字节精确的;这里只是骨架。
8. 错误路径
三种不同失败,各有固定 .rodata 字符串:
| 触发条件 | 字符串 | 地址 | 构建者 |
|---|---|---|---|
scalar/message/enum ParseFlag 返回 false | "Failed to parse '%s' into flag %s: %s" | 0x8584d92 | 每个 arm 的 MakeErrorImpl<3> |
| enum 名称未找到且不是有效数字 | "Invalid value '%s' for enum '%s'. Supported values are: %s." | 0xa0307af | AbslParseFlagImpl(名称由 ", " 0xa299c7c 连接) |
| 没有分派器 arm 匹配 flag 的类型标签 | "Not an AutoOr." | 0xa03f8b1 | <25> 默认 |
前两种是值错误(flag 是 AutoOr<T>,但 token 无法解析)。第三种是类型错误(FieldDescriptor 根本不是 AutoOr 类型的 TCE field),只能由把非 AutoOr flag 交给 ParseAutoOrFromString 的调用者触发,这是编程错误,不是用户输入错误。"Failed to parse" status 通过 absl::str_format_internal::FormatPack 使用三个 %s 参数格式化:值、flag 名称(经由 flag 的 vtable slot 0)和内部解析器的错误字符串。
相关组件
| 组件 | 关系 |
|---|---|
ParseAutoOrFromString<30> @ 0x1d7504c0 | 本页记录的 30-arm 显式值选择器 |
ReadAutoOr<30> @ 0x1d74ca00 | 共享同一 ParseFlag 语法的当前 flag 镜像 |
AutoOr<bool>::ParseFlag @ 0x1d6ba160 | 规范的 "auto" 哨兵 + 类型化解析主体 |
proto2::internal::AbslParseFlagImpl @ 0x20ec6cc0 | enum 名称查找引擎(lower/upper 重试 + numeric fallback) |
absl::SimpleAtob @ 0x211716c0 | 大小写不敏感的 bool token 集 |
NormalizeFieldType<AutoOr<T>> | 将已解析的 AutoOr<T> 折叠进 20 备选项 TCE field variant |
交叉引用
- overview.md — 三层配置流水线;本页负责输入(string→code)阶段
- autoproto-autoor-resolution.md — 解析方向:present-bit 打包、全 AUTO 默认实例,以及解释本页产生编码的按消费者
AUTO→default 极性 - autoor-unparse.md — 反向
AbslUnparseFlag<AutoOr<T>>文本输出方向(code→display string) - autoproto-message-arms.md — 30-arm oneof 和 message arm 的结构化 sub-default(§7 message 语法)
- tpu-compilation-environment.md — 这些 flag 设置的 TCE proto,以及 field#→offset oracle
- xla-flag-atlas.md — 供给这些解析器的
xla_tpu_*flag 表面