为什么海湾合作委员会需要
__attribute__((__malloc__))
?难道不应该通过声明 malloc
(和类似的函数)作为返回 restrict
ed 指针(void *restrict malloc(size_t)
)来传达相同的信息吗?
这种方法似乎会更好,因为除了不需要非标准功能之外,它还允许将其应用于通过指针“返回”的函数(
int malloc_by_arg(void *restrict*retval, size_t size);
)。
即使非常相似,当添加
restrict
或 __attribute__((malloc))
时,相同的函数会产生不同的优化。考虑这个例子(包括here作为__attribute__((malloc))
的一个很好的例子的参考):
#include <stdlib.h>
#include <stdio.h>
int a;
void* my_malloc(int size) __attribute__ ((__malloc__))
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
还有这个(相同的代码,没有属性):
void* my_malloc(int size);
int a;
void* my_malloc(int size)
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
正如我们所期望的,带有 malloc 属性的代码比没有它的代码(都带有
-O3
)得到了更好的优化。让我仅包括差异:
无属性:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
具有属性:
[...]
call ___main
movl $4, (%esp)
call _my_malloc
movl $0, _a
xorl %eax, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
[...]
尽管如此,在这种情况下使用
restrict
是没有价值的,因为它不会优化生成的代码。如果我们修改原始代码以与 restrict
一起使用:
void *restrict my_malloc(int size);
int a;
void *restrict my_malloc(int size)
{
void *restrict p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable and discarded \
during compilation process\n");
return 0;
}
asm 代码与生成的没有 malloc 属性的代码完全相同:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
因此,对于类似 malloc/calloc 的函数,使用
__attribute__((__malloc__))
看起来比 restrict
更有用。
__attribute__((__malloc__))
和 restrict
有不同的行为来优化代码,即使它们的定义非常相似。这让我认为没有必要“合并”它们,因为编译器通过不同的方式实现不同的优化。即使同时使用两者,生成的代码也不会比仅使用其中之一的最优化代码更优化(__attribute__((__malloc__))
或restrict
,取决于情况)。程序员的选择也是如此,根据他/她的代码知道哪一个更适合。
为什么
__attribute__((__malloc__))
不是标准的? 我不知道,但在我看来,这些从定义角度来看的相似之处,以及从行为角度来看的差异,无助于将两者整合到标准中,以清晰、有区别和通用的方式进行表达。
在我的测试中,即使基于没有属性的函数,它也可以通过命令优化代码:~/gcc11.1.0-install/bin/aarch64-linux-gnu-gcc test2.c -O3 -S
main:
.LFB23:
.cfi_startproc
stp x29, x30, [sp, -16]!
.cfi_def_cfa_offset 16
.cfi_offset 29, -16
.cfi_offset 30, -8
mov w0, 4
mov x29, sp
bl my_malloc
adrp x1, .LANCHOR0
mov w0, 0
ldp x29, x30, [sp], 16
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa_offset 0
str wzr, [x1, #:lo12:.LANCHOR0]
ret
.cfi_endproc
.LFE23:
.size main, .-main