ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

结构体传参、TLS与原子变量:chibicc 中3个高级C特性实现全解析

结构体传参、TLS与原子变量:chibicc 中3个高级C特性实现全解析 结构体传参、TLS与原子变量chibicc 中3个高级C特性实现全解析【免费下载链接】chibiccA small C compiler项目地址: https://gitcode.com/gh_mirrors/ch/chibiccchibicc 是一个小巧但功能强大的 C11 编译器它能正确编译 Git、SQLite、libpng 等真实项目。对想学编译器原理的新手来说chibicc 是最好的教材之一——它用极简洁的代码实现了三个公认高级的 C 特性结构体传参x86-64 ABI 寄存器传参规则、线程局部变量 TLS和原子变量_Atomic。本文带你逐一拆解 chibicc 如何实现这三者无需任何汇编基础也能看懂。一、为什么 chibicc 适合学高级C特性实现在深入源码之前先了解 chibicc 的整体架构。整个编译器分四个阶段每个阶段对应一个源文件阶段作用源文件Tokenize把源码切成词法单元tokenize.cPreprocess宏展开与预处理指令preprocess.cParse递归下降解析 类型标注parse.cCodegen生成 x86-64 汇编codegen.c 关键设计哲学chibicc 的作者刻意不过于聪明宁可保留重复代码也要保证可读性。这正是它成为学习编译器首选的原因——每一个 C 特性的实现往往只集中在一个文件或一段代码里。二、结构体传参16字节是分水岭核心规则来自 x86-64 SystemV ABI普通小类型传参很简单整数放 RDI/RSI/RDX/RCX/R8/R9但结构体传参是编译器里出了名的复杂点。chibicc 在 codegen.c 中的注释把规则讲得非常清楚≤ 16 字节用最多两个寄存器传递前 8 字节一个寄存器后 8 字节另一个 16 字节通过栈传递每 8 字节块内如果全是浮点成员用 XMM 浮点寄存器否则用通用寄存器chibicc 的精巧设计两遍扫描实现见 codegen.c 的push_args函数。它先做一遍模拟分配统计寄存器是否够用不够用就把该参数打上pass_by_stack标记走栈传。然后push_args2被调用两次分别生成栈传和寄存器传两种代码实现寄存器用满自动降级到栈的行为has_flonum函数codegen.c递归检查结构体某字节区间是否全为浮点成员决定用 XMM 还是 RAX大结构体返回值更特殊调用方负责分配 16 字节以上的缓冲区把指针当隐式第一参数传给被调函数见 codegen.c小结构体返回值则由copy_struct_reg把内存数据逐字节拼回RAX/RDX 两个寄存器 对新手来说最值得学的是push_struct函数codegen.c它用最朴素的逐字节mov循环完成结构体入栈没有一行花哨技巧却完全正确。三、TLS 线程局部变量一个 fs 段的魔法语法识别在 parse.c_Thread_local/__thread声明在 parse.c 的declspec函数中被识别与static、extern互斥最终把类型/变量标记为is_tlschibicc.h 中定义。地址计算两种模式TLS 的难点在于同一段代码不同线程运行时变量地址不同。chibicc 在 codegen.c 生成了两种模式非 PIC 模式简单直接读取线程指针mov %fs:0, %rax再加上tpoff偏移量即可拿到变量地址PIC 模式兼容动态链接通过__tls_get_addr函数动态查询 TLS 槽位再调用系统提供的data16 lea nametlsgd(%rip), %rdi定位变量落在哪个段普通全局变量放.data/.bss而 TLS 变量在 codegen.c 中被分到专用段有初始值 →.tdata段无初始值 →.tbss段这就是thread_local变量与普通全局变量的本质区别每个线程拥有独立副本链接器为每个线程复制一份 tdata/tbss 内容。想验证效果可以运行测试 test/tls.c。四、原子变量把读-改-写变成硬件级安全操作头文件几乎全是宏看 include/stdatomic.h 会发现惊喜atomic_load就是(*(addr))atomic_store就是(*(addr) (val))。因为 chibicc 生成的代码默认单线程无优化普通读写已经是原子的。真正的重头戏在_Atomic类型上。两条实现路径路径一解析器改写复合赋值parse.cx 1这种操作天然不可原子化读、加、写三步可被打断。chibicc 在解析阶段直接把原子类型上的op改写成 do-while 比较交换循环的 ASTA op B → 循环 { new old op B; } while (CAS失败)路径二codegen 生成 lock 前缀指令codegen.cND_CAS节点生成lock cmpxchg——这是 x86 实现原子操作的核心指令配合sete记录成功与否ND_EXCH节点生成xchg天然原子__builtin_atomic_exchange也走这条路径 完整的多线程压力测试在 test/atomic.c它开多个线程反复做 CAS 递增验证没有任何自增丢失。五、总结从 chibicc 学到什么特性核心实现位置关键技巧结构体传参codegen.c16 字节分界 两遍扫描分配寄存器TLScodegen.c、codegen.cfs 段线程指针 tdata/tbss 专用段原子变量parse.c、codegen.c解析期改写oplock cmpxchg三个特性恰好代表了编译器处理的三个层次传参约定ABI 层、运行时环境线程模型、并发正确性指令级原子性。chibicc 用不到 1000 行可读代码覆盖全部细节是理解C 语言高级特性背后到底发生了什么的最佳入口。 动手建议先用 Makefile 构建 chibicc然后拿 test/struct.c、test/tls.c、test/atomic.c 作为输入对比生成的汇编与本文讲解学习效果最佳。【免费下载链接】chibiccA small C compiler项目地址: https://gitcode.com/gh_mirrors/ch/chibicc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表