一句话开场
如果说 v0.7.0 是给 Kylix 装上"网页灵魂",那 v0.7.1(2026-09-10)就是给它装上"Windows 心跳"。
这个版本的核心命题只有一句:让 Windows 不再是"尽力而为",而是一等公民。三个字:真、打、通。
-
• 真 ---
net在 Windows 上有真 Winsock 实现(不再是 stub) -
• 打 ---
--target windows在 macOS/Linux 上能交叉打 PE32+ .exe -
• 通 --- CI 上有真 Windows runner(windows-latest)跑通整条链路
把视野拉到整个项目,你会看到 Kylix 的两条主线一直在合流:
v0.5.x 自举编译器 (kylix_self 编译自己)
│
v0.6.x bootstrap 无 Go 闭环 · IR 不动点
│
v0.7.0 web 页面 + web 框架
│
v0.7.1 ★ Windows 一等公民
│
v0.7.2 CI 全绿 + 稳定性还债(同日发)
│
v0.8.0 自举 stdlib(真自包含)...
注意一个细节:v0.7.1 和 v0.7.2 是同一天(09-10)发的。这种"日更式小步快跑"在 Kylix 是头一回------说明 CI 门禁和测试体系已经把质量托住了,可以放心做小颗粒发布。
上承:v0.7.0 留下的"作业"
要理解 v0.7.1,先得理解 v0.7.0 给它铺了哪些路。
v0.7.0(2026-09-06)干了一件大事:让 Kylix 从"命令行玩具"变成了"能写网页的语言"。五个 P:
| 编号 | 内容 | 留给 v0.7.1 的作业 |
|---|---|---|
| P0 | error 类型 + 多返回 (T, error) |
错误处理语义 |
| P1 | 纯 Kylix 模板引擎(~1080 行) | 三端同源范本 |
| P2 | res.HTML / req.Form / static/ |
HTTP server 雏形 |
| P3 | res.Redirect + 自定义 404/500 |
BootRun HTTP server |
| P5 | example60 真实 BootRun E2E | 教程 51 → 54 |
但 v0.7.0 也"埋"了几个没说出口的债:
-
- BootRun 底层还是 unix 的 TcpDial/TcpListen/TcpAccept --- 在 Windows 上等于半残
-
- 正则走 Go 的 text/template 包装 --- Windows 链接不上,正则没戏
-
- CI 上 Windows 一直"装不上 LLVM 就跳过" --- 真 Windows 验证等于零
这三个坑,正好是 v0.7.1 的 P1、P0、P3。上承完完整整地转化为下启。
v0.7.1 五大里程碑(从浅到深)
按"对用户感知的近远"组织,从最直观的功能到最底层的工程债。
P0 --- 纯 Kylix 正则引擎
决定"Kylix 程序到底能干什么"。以前正则能力几乎为零;现在直接给你一个完整的小型正则引擎。
三个关键决策:
-
- 砍掉 POSIX
<regex.h>--- UCRT(Windows 的 C 标准库)没有它。继续依赖 = Windows 永远不支持。六个Is*验证器改成纯手写字符类(icmp + range + or 链),零 libc 调用。
- 砍掉 POSIX
-
- 不烘焙(No baking) --- 之前的 stdlib 走"先 host 编译 → 烘焙 IR → bootstrap 复用"路线。regex 引擎直接用普通 Kylix unit ,多文件构建引用。设计哲学的转向:普通 unit 比烘焙数据更自由,三端零成本同源。
-
- 回溯 VM + 9 字节定长指令 ---
stdlib/regex_engine.klx~870 行,支持字符类、* + ? {n} {n,} {n,m}、lazy、锚点、分组、alternation。显式栈 + visited memo 防(a*)*空宽循环死递归。
- 回溯 VM + 9 字节定长指令 ---
五个 API:
RegexMatch → bool (是否匹配)
RegexFind → (String, error) (首个匹配)
RegexFindAll → array of String (所有匹配)
RegexReplace → String (替换)
RegexSplit → array of String (切分)
注意 RegexFind 用了 v0.7.0 的 error 多返回 ------ 新特性立刻被新 stdlib 用上,这就是特性成熟度的标志。
验收:教程 example61(33 场景)--- Go 55/55 · LLVM 55/55 · bootstrap 53 PASS + 2 SKIP,IR 不动点保持(227k 行逐字节一致)。
💡 语义对齐 Go RE2(leftmost-first + 空匹配跳过规则),文档化偏差。这种"知其然知其所以然"的兼容性设计,是把语言当产品做的态度。
P1 --- net Winsock 真实现
如果说 P0 是"补能力",那 P1 是"补平台"。决定 Windows 程序的真实可用性。
wrapper 架构示意:
┌────────────────────────────────────────────┐
│ Kylix 用户代码: TcpDial("kylix.top:80") │
└──────────────────────┬─────────────────────┘
▼
┌────────────────────────────────────────────┐
│ 公开 TCP 函数 (OS 无关, handle 统一 i64) │
│ TcpDial/Write/Read/Close/Listen/Accept ... │
└──────────────────────┬─────────────────────┘
┌──────────────┴──────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ 9 个 __kylix_ │ │ 9 个 __kylix_ │
│ net_* 原语 │ │ net_* 原语 │
│ (Linux/macOS) │ │ (Windows) │
│ │ │ │
│ socket/connect │ │ WSAStartup-once │
│ bind/listen │ │ socket/connect │
│ accept/send │ │ bind/listen │
│ recv/closeh │ │ accept/send │
│ reuse │ │ recv/closesocket │
│ │ │ + ioctlsocket │
│ │ │ + setsockopt │
└─────────────────┘ └─────────────────────┘
四个关键设计点:
-
- handle 统一 i64(8 字节堆 cell) --- 底层不管是
int fd还是SOCKET UINT_PTR,对外都是一个 64 位整数。SOCKET_ERROR=-1 永远保号(i64→i32 trunc/sext)。
- handle 统一 i64(8 字节堆 cell) --- 底层不管是
-
- WSAStartup-once 全局守卫 ---
@__kylix_net_wsa_done+MAKEWORD(2,2)=514+ 512 字节 WSADATA,进程级只调一次。
- WSAStartup-once 全局守卫 ---
-
- ioctlsocket 强制阻塞模式 ---
FIONBIO=0x8004667e,避免 Winsock 默认非阻塞带来的复杂性。
- ioctlsocket 强制阻塞模式 ---
-
- 跨模块引用入队 --- 修了一个隐藏回归:boot HTTP server 引用
@__kylix_net_accept但没入队的 bug(example60 修复后才跑通)。
- 跨模块引用入队 --- 修了一个隐藏回归:boot HTTP server 引用
顺手破案的潜伏 bug:
| 平台 | SOL_SOCKET | SO_REUSEADDR |
|---|---|---|
| Linux | 1 | 2 |
| macOS / BSD / Windows | 0xffff | 4 |
旧代码统一用 1/2,macOS 上 setsockopt 静默无效 --- 双进程 echo 测出来 TIME_WAIT 30s 内 rebind 必报 EADDRINUSE。真根因查到了,按 targetOS 分支选常量。
💡 教程从未在 LLVM 端裸跑过 Tcp*,所以这个 bug 藏了 5 个版本。真正考验平台的永远是 E2E 测试,不是单元测试。
顺带还破了一个 ASLR 触发的非确定性段错误(~35% 崩溃率):
-
•
EmitCall参数表达式嵌套调用时共享字段被重入重置 -
•
EmitPlainCallcoerce 检查用复合and(bootstrap 不短路) -
• 垃圾值能否解引用取决于 ASLR → 非确定性 strcmp BUS
调试手法堪称艺术:ASAN 版 BOOT 定位 EmitPlainCall+0xdd4 strcmp 野指针 → 反汇编 + IR 结构体偏移表锁死语句。修复后 30/30 稳定,IR 不动点保持。
P2 --- --target windows 交叉链接
让"在 macOS/Linux 上给 Windows 编程序"成为现实。
核心组件:FindMingwSysroot() 搜索顺序:
1. $KYLIX_MINGW_ROOT (用户最明确)
2. 可执行文件旁的 llvm-mingw/
3. PATH 上 mingw driver 的祖父目录
4. ~/llvm-mingw
5. /opt/llvm-mingw
6. /usr/local/llvm-mingw
三个关键修复:
-
-
tripleForwindows/amd64:msvc → mingw triple-
• msvc triple 下 llc 发 MSVC 栈探针
__chkstk(mingw 无此符号) -
• gnu triple 发
___chkstk_ms(compiler-rt 内建) -
• net.klx 交叉链接
__chkstkundefined 的真根因
-
-
-
链接器路径显式指定
-
• 旧代码用
-fuse-ld=lld按 PATH 找 → homebrew-only 安装必挂 -
• 新代码:
--ld-path=<sysroot>/bin/ld.lld
-
-
-
exec.Command的 realloc 坑 (最有意思的一个)// 错的写法(v0.7.0 的 windows 分支)
cmd := exec.Command(linker, args...)
args = append(args, "-lws2_32") // append 后 realloc
// cmd 已经持有旧切片!新参数丢失
坑的本质:
exec.Command在调用时冻结 args 切片的引用,append 后底层数组换地址 → 链接报 Winsock 符号全部 undefined。 -
端到端验证:
KYLIX_MINGW_ROOT=~/llvm-mingw kylix build \
--backend=llvm --target=windows/amd64 hello.klx
# 产出: hello.exe (PE32+ executable console x86-64)
KYLIX_MINGW_ROOT=~/llvm-mingw kylix build \
--backend=llvm --target=windows/amd64 net.klx
# 产出: net.exe
# objdump 确认: 导入 WS2_32.dll + UCRT + KERNEL32
⚠️ macOS 隐藏坑(血泪提示): 下载解包的 llvm-mingw 带 com.apple.quarantine 隔离属性,Apple Silicon 上无签名二进制被 Gatekeeper 直接 SIGKILL(signal: killed,无任何输出)。解决:xattr -dr com.apple.quarantine <目录>
P3 --- CI llvm-windows job 真跑
没有 CI 验证的平台支持都是自欺欺人。 这一段把 Windows 拉进了真机验证体系。
三个反常识设计:
-
-
不装 LLVM,装 LLVM
-
• 历史:windows-latest runner 用 choco/winget/官方 exe 装 LLVM --- 10 次 10 次装不上
-
• 解决:解压 mstorsjo llvm-mingw ucrt-x86_64 portable zip(固定版本 20260826)
-
• 优势:一份 zip 两个角色 --- 既当 IR 工具链(llc/clang/opt),又当链接 sysroot
-
-
-
四段验收 (不只是"能编译"):
(1) 4 个代表教程(hello/if_else/类方法/stdlib core)
(2) example61 regex 引擎(多文件 + 0 个 ^F + 含 done)
(3) net 双进程 Winsock echo
(WSAStartup-once + listen/accept/dial 全链路)
(4) llvm-mingw 兼做 IR 工具链 + 链接 sysroot
-
-
- CI 程序预验证
双进程 echo + example61 先在 macOS 本地用同一份源码跑通,CI 失败只可能来自平台差异 --- 把"CI 红了但本地复现不了"的扯皮成本压到 0。
P4 --- 工程债快赢
这一段不性感,但是工程师最爱的部分 --- 修两个天天烦人的小坑。
1. 增量缓存指纹加入编译器二进制自哈希
场景重现:
# 修改了 emitter 的代码
go build -o kylix cmd/kylix/main.go
kylix build hello.klx
# 编译跑的是旧 emitter!缓存没失效!
# 你以为在测新代码,其实跑的是旧版本生成的 IR
修复:codegenHash() 对运行中的 kylix 可执行文件算 SHA-256,写入 CacheEntry.CodegenHash。编译器一重编,全部缓存自动失效。exe 读取失败时退回旧 mtime+size 行为(保护异常环境)。
2. "三处名单"一致性单测
历史教训:v0.7.0 的 undefined: BootNotFoundPage bug 类(host Go 端 stdlib 模块启发式名单漏加)。
修复:解析源文本(不靠反射未导出 map)交叉校验 Boot* API 名单:
-
•
TestBootNameLists_Converged:host Go 与 LLVM 名单必须集合相等 -
•
TestBootNames_Dispatchable:每个 Boot* 必须在emitBootCall有 case
💡 测试"名单漂移"这种软性 bug 是最难写的(没运行时异常,只有人类发现),源文本解析 + 对称差报警是优雅解法。
v0.7.1 总览:一张表
| 编号 | 内容 | 用户感知 | 技术深度 | 关键数字 |
|---|---|---|---|---|
| P0 | 纯 Kylix 正则引擎 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 870 行 · 33 场景 · 5 API |
| P1 | net Winsock 真实现 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 700 行重写 · 7+9 函数 |
| P2 | --target windows 交叉链接 |
⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 3 大修复 · realloc 坑 |
| P3 | CI llvm-windows job 真跑 | ⭐⭐⭐ | ⭐⭐⭐ | 4 段验收 · portable |
| P4 | 增量缓存自哈希 + 名单单测 | ⭐⭐ | ⭐⭐⭐ | SHA-256 + 对称差 |
整体验收门槛: 16 包全绿 · Go 55/55 · LLVM 55/55 · bootstrap 53 PASS + 2 SKIP · IR 不动点 gen1 ≡ gen2 保持(~227k 行)
下启:v0.7.2 已经来了
v0.7.1 是 09-10 发的,v0.7.2 也是 09-10 发的。这种"日更式连发"在 Kylix 的开发节奏里是第一次。
v0.7.2 的主题是 "CI 全绿 + 稳定性还债"------本质上是 v0.7.1 留下的"附属任务"。
三件最值得说的事:
-
- CI selfrepro fixpoint job 改走
--emit-llvm链
旧 job 走 Go 自繁殖链,但自 v0.6.9 起这条路径已经腐化 (
StdIrInitundefined + 多态 gate 7 处不一致)。新 job 验证 v0.6.9 无 Go 闭环的真不动点:gen1 ≡ gen2 逐字节一致。 - CI selfrepro fixpoint job 改走
-
-
bootstrap Go-codegen 路径显式退役
src/main.klx非--emit-llvm调用从「静默输出编译不过的 main.go」改为显式报错 :error: the bootstrap Go-codegen path is retired; use --emit-llvm
Linux 上字符串常量损坏(
fmt.Println("Hello")→@p[ :=)的排查债务随路径退役一并关闭。 -
-
-
LLVM 端类方法多返回支持
-
(解除 v0.7.0 起的绕行限制)
-
• vtable 槽聚合返回:
%__ret_<Class>_<Method>命名 struct -
•
result := (a, b)+(q, r) := obj.M()两形式解构 -
• 模板引擎 RenderString/ErrorMsg 拆方法的绕行模式可退役
-
一句话总结 v0.7.1 + v0.7.2: v0.7.1 把 Windows 拉进了一等公民,v0.7.2 把"v0.7.1 顺手发现的债 + 长期红的 CI"一次性还清。这是 Kylix 第一次连续两个版本在同一天发布 ------ 也是工程纪律成熟的标志。
写给读者的话
Kylix 的故事走到这里,有三个我觉得最值得圈点的事:
1. "工具链即文档"的文化
v0.7.1 的 xattr -dr com.apple.quarantine 提示、exec.Command realloc 坑、SO_REUSEADDR 跨平台常量表 ------ 这些都直接写进了 CHANGELOG 和代码注释。坑被看见 = 坑被修掉。这种"把血泪写成路标"的工程文化,是项目能持续迭代的根本。
2. "同源三端"的设计哲学
模板引擎走烘焙 IR 数据,regex 引擎走普通 unit ------ 两种路线并存,根据模块特性选择最合适的。设计不是教条,是权衡。
3. "CI 即合同"的纪律
v0.7.1 把 Windows CI 从"装不上就跳过"升级为"必须真跑",v0.7.2 把长期红的 fixpoint job 修了。不再允许"假装支持" ------ 这是从"个人项目"到"产品项目"的临界点。
展望 v0.8.0:自举 stdlib(真自包含)。届时 template_engine.klx 和 regex_engine.klx 这样的范本会扩展到 encoding/jsonutil/字符串工具等更多模块。Kylix 真正"自举"的那一天,stdlib 本身就是用 Kylix 写的 ------ 这是这门语言从"转译器"变成"自包含生态"的最后一步。
我们下一篇(第六十一篇)再见 🚀