函数属性位于模块末尾的 attributes #n = { ... } 块中,并通过函数定义末尾的 #n 引用;参数属性则显式写在 define 的参数类型后,如 ptr nocapture,须带 byval、nocapture 等关键字。

直接看 attributes 块和函数签名里的 #N 引用,别猜;参数属性只出现在函数定义的参数列表里,且必须带 byval、nocapture 等显式关键字。
怎么定位函数属性(Function Attributes)
函数属性不写在函数体内部,而是独立出现在模块末尾的 attributes #N = { ... } 块中,再通过函数定义末尾的 #N 编号引用。例如:
define dso_local i32 @sum(i32 %0, i32 %1) #0 {
...
}
attributes #0 = { noinline nounwind optnone uwtable }
这里 #0 是索引,实际含义要跳转到 attributes #0 = { ... } 行去查。常见属性含义:
-
noinline:禁止内联,即使优化开启也强制保持函数调用 -
nounwind:声明该函数不会抛出 C++ 异常(影响栈展开代码生成) -
optnone:绕过所有中端优化,常用于-O0输出 -
uwtable:为异常处理生成完整的 unwind 表(影响二进制大小)
注意:attributes 块可能被复用——多个函数共用同一个 #N,所以不能只看单个函数定义就断言其属性。
参数属性(Parameter Attributes)在哪写、怎么读
参数属性只出现在函数定义(define)的参数列表中,且必须显式写出,例如:
declare i32 @puts(ptr nocapture readonly)
这里的 nocapture 和 readonly 是作用于第一个参数(ptr 类型)的参数属性。关键点:
- 它们紧贴参数类型之后,中间无逗号,如
ptr nocapture,不是ptr, nocapture - 常见参数属性包括:
nocapture(函数不存储该指针)、readonly(只读内存访问)、byval(按值传递结构体)、noalias(与其他指针无重叠) - 如果参数没写任何属性,就代表“无约束”,编译器不能做相应假设
-
byval必须跟align,如%struct.T* byval align 8,漏掉align是常见解析错误
容易误读的三个地方
很多初学者把以下内容当成属性,其实不是:
-
dso_local是链接类型(Linkage Type),属于全局符号修饰,和函数行为无关 -
unnamed_addr是全局变量的地址属性,只对@xxx = ...生效,不适用于函数参数 - 函数名后的
@sum开头的@是全局标识符标记,不是属性语法的一部分
真正算“属性”的,只有明确出现在 attributes #N = { ... } 里的内容,或紧跟在参数类型后的关键字。其他都是修饰符、类型或链接信息。
用工具快速验证属性是否生效
手动读容易漏,建议用 llvm-dis + grep 快速确认:
clang -emit-llvm -S -O2 test.c -o test.ll grep -A5 "attributes #" test.ll grep "declare.*@puts" test.ll
如果你在 test.ll 里找不到 nocapture,但源码用了 __attribute__((nocapture)),大概率是前端没透传——Clang 对某些属性支持有限,nocapture 在 C/C++ 中需配合 restrict 或特定 ABI 才会落地到 IR。











