Skip to content

AutoOr 解析语法

地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so。其他版本会有所不同。

摘要

当用户设置 XLA_FLAGS=--xla_tpu_<knob>=auto(或 true0.542MODE_NAME)时,libtpu 不会把原始字符串交给通用 flag 解析器。该 token 会经过 AutoOr<T> 特有的两阶段路径。首先,一个以 FieldDescriptor 为键的 arm 选择器,即 ParseAutoOrFromString<30> @ 0x1d7504c0,尾调用到 <25> @ 0x1d7eac40,通过 CommandLineFlag 的类型标签查找该 flag 注册的 C++ 类型,并分派到该类型唯一的 AutoOr<T>::ParseFlag 实例。真正的语法在这个按类型划分的解析器里:30 个实例都以完全相同的 "auto" 哨兵检查开头;如果不匹配,则委托给 T 的标准 absl/proto2 原语(SimpleAtobsafe_strto32_baseSimpleAtodEnumDescriptor 名称查找、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-static std::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 @ 0x1d6ba160SimpleAtob;打包 val | 0x100
int32 解析器AutoOr<int>::ParseFlag @ 0x1d744080safe_strto32_base;打包 val | 0x100000000
int64 解析器AutoOr<long>::ParseFlag @ 0x1d743480safe_strto64_base;打包 {value, has@+8}
enum 解析器例如 AutoOr<ScavengingMode_Value>::ParseFlag @ 0x1d748280AbslParseFlagImpl 名称查找
错误字符串"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 值如何到达

text
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 …
c
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>`。重新实现者可以把它折叠成行为相同的单个 30switch;两个函数的拆分是模板实例化产物(同一个泛型中有两个 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 ok

kAuto 字符串是惰性构造的函数局部 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/floatvmovups %xmm0,(out)(清零 16 B){value=0, has=0}
stringout+0x18 = 0xff; out+0x20 = 0absent 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。

按类型解析器和打包

类型ParseFlagabsl 被调用方(strip-WS → core)OK 打包
bool0x1d6ba160AbslParseFlag(bool*) 0x21112840SimpleAtob 0x211716c0val | 0x100
int320x1d744080AbslParseFlag(int*) 0x21112a40safe_strto32_base 0x21173ce0val | 0x100000000
uint320x1d743e60AbslParseFlag(uint*) 0x21112b60safe_strtou32_base 0x211742a0val | 0x100000000
int640x1d743480AbslParseFlag(long*) 0x21112c80safe_strto64_base 0x21173e20{value@+0, has=1@+8}
uint640x1d7ed180AbslParseFlag(ulong*) 0x21112da0safe_strtou64_base 0x21174360{value@+0, has=1@+8}
double0x1d744be0AbslParseFlag(double*) 0x21112ee0SimpleAtod 0x21171580{dbl@+0, has=1@+8}
float(via NormalizeFieldType)AbslParseFlag(float*) 0x21112ec0SimpleAtof 0x21171440{flt@+0, has=1@+8}
string0x1d7437e0AbslParseFlag(string*) 0x21112f00(原样复制){body, idx@+0x18=0, has@+0x20=1}

bool token 集

SimpleAtob @ 0x211716c0 会把剥离空白后的输入以大小写不敏感方式(通过 EqualsIgnoreCase 0x211711c0memcasecmp 0x21171120)按顺序与两个固定集合比较:

text
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=offnot;test 0x101;seteAUTO=onand 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() 被调用方:

c
function AutoOr<EnumT_Value>::ParseFlag(value, value_len, AutoOr<EnumT>* out):  // 0x1d748280
"auto" sentinel3); 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 typeParseFlag分派器
ScavengingMode_Value0x1d748280<25>
Bf16EmissionMode_Value0x1d7488e0<25>
MlirVerifierOptions_Value0x1d7486c0<25>
CollectiveMatmulModeProto_Value0x1d748060<25>
ExpandedScopedAlternateMemoryMode_Value0x1d747760<25>
ExecutionOptions_EffortLevel0x1d747360<25>
RematerializationOptions_RematerializationAlgorithm0x1d7484a0<25>
PrecisionTracerModeProto_Value0x1d7ecbc0<25>
SkipConfigTypeProto_Value0x1d7eccc0<25>
BufferAssignmentAlgorithmProto_Value0x1d74a900<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 @ 0x12fcfd00ParseFlag 实例,但没有被任何选择器分派)。"auto" 哨兵之上的语法是 key=value 读取器,而不是 scalar 原语;解析出的 sub-message 与 has-byte 一起保存在 variant 中。

算法

AutoOr<CostModelFlagOptions>::ParseFlag @ 0x1d744f80 为例:

c
function AutoOr<MsgT>::ParseFlag(value, value_len, AutoOr<MsgT>* out):   // 0x1d744f80
"auto" sentinel3); 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 按分隔符(ByChar Splitter)拆分,并通过 *0x10 按字段分派,但分隔符字符、语法是标准 proto TextFormat 还是自定义 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."0xa0307afAbslParseFlagImpl(名称由 ", " 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 @ 0x20ec6cc0enum 名称查找引擎(lower/upper 重试 + numeric fallback)
absl::SimpleAtob @ 0x211716c0大小写不敏感的 bool token 集
NormalizeFieldType<AutoOr<T>>将已解析的 AutoOr<T> 折叠进 20 备选项 TCE field variant

交叉引用