llvm-size可直接查看.o或.bc文件的段分布,支持ELF、Mach-O和bitcode格式,能准确解析LLVM IR中的section属性;加-A显示节偏移,--common包含COMMON符号。

直接用 llvm-size 查目标文件或 bitcode 文件的段大小,比看链接后二进制更早、更准;想定位函数级空间开销,得结合 llvm-objdump -t 和 opt -print-module-sizes。
怎么用 llvm-size 看 .o 或 .bc 的段分布
llvm-size 是 LLVM 自带的轻量工具,专为未链接的中间产物设计,支持 ELF、Mach-O 和 bitcode(.bc)格式。它不依赖系统 size,能正确解析 LLVM IR 中的全局变量布局和 section 属性。
- 对编译后的 bitcode:
clang -c -emit-llvm foo.cpp -o foo.bc,然后llvm-size foo.bc—— 会显示.text(函数体)、.data(初始化全局)、.bss(未初始化全局)等逻辑段大小 - 对普通目标文件:
llvm-size foo.o,输出和 GNUsize类似,但能识别__LLVM_STACKMAPS、.llvm_swift_reflection这类 LLVM 特有节 - 加
-A参数可显示每个节的绝对地址偏移,方便判断是否因对齐膨胀;加--common可包含 COMMON 符号(如未定义的弱全局)
怎么查单个函数/全局变量占多少字节
llvm-size 只给段汇总,没法下钻到符号粒度。真要定位“哪个函数拖垮了代码体积”,得换两套方法:
- 用
llvm-objdump -t foo.o | sort -k3 -n:列出所有符号及其 size 字段(第三列),按大小倒序,一眼揪出巨无霸函数或静态表 - 对 IR 文件启用模块级尺寸打印:
opt -print-module-sizes foo.bc 2>&1 | grep -E '^[a-zA-Z_].*:'—— 它会按 function、global variable、metadata 分组打印占用,且包含内联展开后的估算值 - 注意:IR 中的
StringLiteral常被忽略,实际编译后可能膨胀成大块.rodata,建议额外用llvm-strings foo.bc | wc -l扫描字符串常量密度
为什么不能直接用 strip 后的可执行文件看
strip 会删掉符号表和调试段,导致 size 或 llvm-size 失去函数粒度信息;更重要的是,链接器(lld 或 ld)在合并段时会重排、对齐、填充,甚至折叠相同内容(如 .rodata dedup),使得最终二进制的段大小 ≠ 源头各模块贡献之和。
- 例如:多个
.cpp文件里定义相同的const char[] = "hello",链接时可能只保留一份,llvm-size算出的总和会明显大于size a.out - 又比如:
lld默认开启--icf=safe(identical code folding),会让语义等价的函数体共享指令内存,这层优化在 IR 阶段完全不可见 - 所以,空间归因必须分层——bitcode 层看 IR 贡献,.o 层看符号分布,a.out 层只用于验证最终效果,不能反推原因
最易被忽略的一点:LLVM IR 里的 alloca 不占代码空间,但会抬高栈帧,间接影响 cache line 占用和函数内联决策;而 SmallVector 这类 ADT 在编译期展开后可能生成大量 inline 指令,opt -print-module-sizes 会把它算进函数体,但 llvm-objdump -t 看不到——这种“隐形体积”只能靠 IR 级分析捕获。

















