【问题标题】:__attribute__((malloc)) vs restrict__attribute__((malloc)) vs 限制
【发布时间】:2019-05-08 04:54:30
【问题描述】:

为什么 gcc 需要__attribute__((__malloc__))?不应该通过将malloc(和类似函数)声明为返回restricted 指针(void *restrict malloc(size_t))来传递相同的信息吗?

似乎这种方法会更好,因为除了不需要非标准功能之外,它还允许将其应用于通过指针“返回”的函数 (int malloc_by_arg(void *restrict*retval, size_t size);)。

【问题讨论】:

  • 不就是因为标准规定malloc的格式是void *malloc(size_t size);吗? gcc 不允许更改函数的格式,否则它将不兼容并且与某些“属性”一样不标准。
  • @Lundin 是的,尽管标准可以将malloc 指定为两个选项之一(有和没有restrict)。由于一些符合标准的编译器已经将malloc 返回的指针视为无别名,因此在我看来,如果有一天标准开始允许替代声明,这不会是一个重大变化。但我可能是错的。
  • @TheodorosChatzigiannakis 是的,返回的指针根据定义没有别名,因为它没有“有效类型”。我想这可以看作是语言缺陷;他们没有理由不 restrict 限定返回的指针。

标签: c gcc malloc restrict


【解决方案1】:

即使非常相似,当添加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__)) 不是标准的?我不知道,但 IMO,从定义的角度来看,这些相似之处和从行为角度来看的差异无助于以清晰、差异化和通用的方式将两者整合到标准中。

【讨论】:

  • 虽然你的答案是正确的,但我认为它没有回答问题的本质:为什么不能通过__attribute__((__malloc__)) 实现的优化通过restrict 实现,考虑到restrict是标记非别名指针(例如分配器返回的指针)的标准方法吗?
  • 我明白你的意思,我的回答更侧重于试图展示差异。我已经编辑了我的答案以添加一个更清晰的结论,即使它根本没有用。
【解决方案2】:

在我的测试中,即使基于没有属性的函数,它也可以使用命令优化代码:~/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

【讨论】:

    猜你喜欢
    • 2013-08-31
    • 2013-02-19
    • 2014-06-10
    • 2010-09-07
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 2013-03-09
    相关资源
    最近更新 更多