Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clang是否误解了“const”指针说明符?_C_Clang_Compiler Optimization_Strict Aliasing_Restrict Qualifier - Fatal编程技术网

Clang是否误解了“const”指针说明符?

Clang是否误解了“const”指针说明符?,c,clang,compiler-optimization,strict-aliasing,restrict-qualifier,C,Clang,Compiler Optimization,Strict Aliasing,Restrict Qualifier,在下面的代码中,我看到如果没有隐式限制指针说明符,clang无法执行更好的优化: #include <stdint.h> #include <stdlib.h> #include <stdbool.h> typedef struct { uint32_t event_type; uintptr_t param; } event_t; typedef struct { event_t

在下面的代码中,我看到如果没有隐式限制指针说明符,clang无法执行更好的优化:

#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
    uint32_t        event_type;
    uintptr_t       param;
} event_t;

typedef struct
{
    event_t                     *queue;
    size_t                      size;
    uint16_t                    num_of_items;
    uint8_t                     rd_idx;
    uint8_t                     wr_idx;
} queue_t;

static bool queue_is_full(const queue_t *const queue_ptr)
{
    return queue_ptr->num_of_items == queue_ptr->size;
}

static size_t queue_get_size_mask(const queue_t *const queue_ptr)
{
    return queue_ptr->size - 1;
}

int queue_enqueue(queue_t *const queue_ptr, const event_t *const event_ptr)
{
    if(queue_is_full(queue_ptr))
    {
        return 1;
    }

    queue_ptr->queue[queue_ptr->wr_idx++] = *event_ptr;
    queue_ptr->num_of_items++;
    queue_ptr->wr_idx &= queue_get_size_mask(queue_ptr);

    return 0;
}
在反汇编文件中,我看到生成的代码重新读取内存:

_queue_enqueue:
        .cfi_startproc
@ %bb.0:
        ldrh    r2, [r0, #8]            ---> reads the queue_ptr->num_of_items
        ldr     r3, [r0, #4]            ---> reads the queue_ptr->size
        cmp     r3, r2
        itt     eq
        moveq   r0, #1
        bxeq    lr
        ldrb    r2, [r0, #11]           ---> reads the queue_ptr->wr_idx
        adds    r3, r2, #1
        strb    r3, [r0, #11]           ---> stores the queue_ptr->wr_idx + 1
        ldr.w   r12, [r1]
        ldr     r3, [r0]
        ldr     r1, [r1, #4]
        str.w   r12, [r3, r2, lsl #3]
        add.w   r2, r3, r2, lsl #3
        str     r1, [r2, #4]
        ldrh    r1, [r0, #8]            ---> !!! re-reads the queue_ptr->num_of_items
        adds    r1, #1
        strh    r1, [r0, #8]
        ldrb    r1, [r0, #4]            ---> !!! re-reads the queue_ptr->size (only the first byte)
        ldrb    r2, [r0, #11]           ---> !!! re-reads the queue_ptr->wr_idx
        subs    r1, #1
        ands    r1, r2
        strb    r1, [r0, #11]           ---> !!! stores the updated queue_ptr->wr_idx once again after applying the mask
        movs    r0, #0
        bx      lr
        .cfi_endproc
                                        @ -- End function
将restrict关键字添加到指针后,这些不必要的重新读取就消失了:

int queue_enqueue(queue_t * restrict const queue_ptr, const event_t * restrict const event_ptr)
我知道在clang中,默认情况下禁用严格别名。但在这种情况下,event_ptr指针被定义为const,因此其对象的内容不能被该指针修改,因此它不会影响队列_ptr指向的内容(假设对象在内存中重叠),对吗

那么,这是一个编译器优化错误,还是确实存在一些奇怪的情况,当queue_ptr所指向的对象可能受到event_ptr的影响时,假设以下声明:

int queue_enqueue(queue_t *const queue_ptr, const event_t *const event_ptr)
顺便说一下,我尝试为x86目标编译相同的代码,并检查了类似的优化问题

生成的带有restrict关键字的程序集不包含重新读取:

_queue_enqueue:
        .cfi_startproc
@ %bb.0:
        ldr     r3, [r0, #4]
        ldrh    r2, [r0, #8]
        cmp     r3, r2
        itt     eq
        moveq   r0, #1
        bxeq    lr
        push    {r4, r6, r7, lr}
        .cfi_def_cfa_offset 16
        .cfi_offset lr, -4
        .cfi_offset r7, -8
        .cfi_offset r6, -12
        .cfi_offset r4, -16
        add     r7, sp, #8
        .cfi_def_cfa r7, 8
        ldr.w   r12, [r1]
        ldr.w   lr, [r1, #4]
        ldrb    r1, [r0, #11]
        ldr     r4, [r0]
        subs    r3, #1
        str.w   r12, [r4, r1, lsl #3]
        add.w   r4, r4, r1, lsl #3
        adds    r1, #1
        ands    r1, r3
        str.w   lr, [r4, #4]
        strb    r1, [r0, #11]
        adds    r1, r2, #1
        strh    r1, [r0, #8]
        movs    r0, #0
        pop     {r4, r6, r7, pc}
        .cfi_endproc
                                        @ -- End function
int queue_enqueue(queue_t * restrict const queue_ptr, const event_t *const event_ptr)
补充:

在他的评论中与Lundin进行了一些讨论之后,我得到的印象是,可能会导致重新读取,因为编译器会假设queue_ptr->queue可能指向*queue_ptr本身。因此,我将队列结构更改为包含数组,而不是指针:

typedef struct
{
    event_t                     queue[256]; // changed from pointer to array with max size
    size_t                      size;
    uint16_t                    num_of_items;
    uint8_t                     rd_idx;
    uint8_t                     wr_idx;
} queue_t;
然而,重读仍与以前一样。我仍然无法理解是什么让编译器认为队列字段可能被修改,因此需要重新读取。。。以下声明消除了重新读取:

_queue_enqueue:
        .cfi_startproc
@ %bb.0:
        ldr     r3, [r0, #4]
        ldrh    r2, [r0, #8]
        cmp     r3, r2
        itt     eq
        moveq   r0, #1
        bxeq    lr
        push    {r4, r6, r7, lr}
        .cfi_def_cfa_offset 16
        .cfi_offset lr, -4
        .cfi_offset r7, -8
        .cfi_offset r6, -12
        .cfi_offset r4, -16
        add     r7, sp, #8
        .cfi_def_cfa r7, 8
        ldr.w   r12, [r1]
        ldr.w   lr, [r1, #4]
        ldrb    r1, [r0, #11]
        ldr     r4, [r0]
        subs    r3, #1
        str.w   r12, [r4, r1, lsl #3]
        add.w   r4, r4, r1, lsl #3
        adds    r1, #1
        ands    r1, r3
        str.w   lr, [r4, #4]
        strb    r1, [r0, #11]
        adds    r1, r2, #1
        strh    r1, [r0, #8]
        movs    r0, #0
        pop     {r4, r6, r7, pc}
        .cfi_endproc
                                        @ -- End function
int queue_enqueue(queue_t * restrict const queue_ptr, const event_t *const event_ptr)
但是为什么队列ptr必须声明为限制指针以防止重新读取,我不理解,除非它是编译器优化错误

附言


我也找不到任何链接,指向文件/报告不会导致编译器崩溃的clang问题…

据我所知,是的,在代码队列中\u ptr无法修改指针对象的内容。这是一个优化错误吗?这是一个错过的优化机会,但我不会称之为bug。它没有误解const,只是没有/没有进行必要的分析,以确定它不能针对此特定场景进行修改


作为旁注:queue_is_fullqueue_ptr可以修改*queue_ptr的内容,即使它有const queue_t*const param,因为它可以合法地丢弃constanss,因为原始对象不是const。也就是说,quueue_is_full的定义是可见的,编译器可以使用它,因此它可以确定它确实不存在。

据我所知,是的,在您的代码队列中\u ptr指针的内容无法修改。这是一个优化错误吗?这是一个错过的优化机会,但我不会称之为bug。它没有误解const,只是没有/没有进行必要的分析,以确定它不能针对此特定场景进行修改


作为旁注:queue_is_fullqueue_ptr可以修改*queue_ptr的内容,即使它有const queue_t*const param,因为它可以合法地丢弃constanss,因为原始对象不是const。也就是说,quueue_is_full的定义是可见的,编译器可以使用它,因此它可以确定它确实不存在。

队列ptr的事件成员可以指向与事件ptr相同的内存。当编译器无法排除两个指针指向同一内存时,它们往往会生成效率较低的代码。因此,限制导致更好的代码并不奇怪


常量限定符并不重要,因为它们是由函数添加的,原始类型可以在其他地方修改。特别是,*const没有添加任何内容,因为指针已经是原始指针的本地副本,所以包括调用者在内的任何人都不关心函数是否修改了该本地副本

严格别名指的是编译器可以省事的情况,比如假设uint16\u t*不能指向uint8\u t*等。但在您的情况下,有两个完全兼容的类型,其中一个仅包装在外部结构中。

队列\u ptr的事件\u t成员可以指向与事件\u ptr相同的内存。当编译器无法排除两个指针指向同一内存时,它们往往会生成效率较低的代码。因此,限制导致更好的代码并不奇怪


常量限定符并不重要,因为它们是由函数添加的,原始类型可以在其他地方修改。特别是,*const没有添加任何内容,因为指针已经是原始指针的本地副本,所以包括调用者在内的任何人都不关心函数是否修改了该本地副本


严格别名指的是编译器可以省事的情况,比如假设uint16\u t*不能指向uint8\u t*等。但在您的情况下,您有两个完全兼容的类型,其中一个仅包装在外部结构中。

如您所知,您的代码会出现 要修改数据,使常量状态无效,请执行以下操作:

如果没有restrict关键字,编译器必须假设这两种类型可能共享相同的内存空间

这在更新的示例中是必需的,因为事件\u t是队列\u t的成员,并且严格别名适用于:

。。。一种聚合或联合类型,其成员中包含上述类型之一,递归地包括子聚合或包含的联合的成员,或

在最初的示例中,有许多原因可能会将类型视为别名,从而导致相同的结果,即使用char指针,并且可能会认为类型在某些体系结构(如果不是所有体系结构)上具有足够的兼容性

因此,编译器需要在内存发生变异后重新加载内存,以避免可能的冲突

const关键字并没有真正进入这个过程,因为一个可能指向同一内存地址的指针发生了变异

编辑为方便起见,以下是有关访问变量的完整规则:

对象的存储值只能由具有以下类型之一的左值表达式访问:

-与对象的有效类型兼容的类型

-与对象的有效类型兼容的类型的限定版本

-与对象的有效类型相对应的有符号或无符号类型

-一种类型,它是与对象的有效类型的限定版本相对应的有符号或无符号类型

-在其成员中包含上述类型之一的聚合或联合类型,递归地包括子聚合或包含的联合的成员,或

-字符类型

88此列表的目的是指定对象可以或不可以使用别名的情况

附笔。 后缀由POSIX保留。您可以考虑使用不同的后缀。


通常的做法是对结构使用_,对联合使用_。

如您所知,您的代码似乎修改了数据,使常量状态无效:

如果没有restrict关键字,编译器必须假设这两种类型可能共享相同的内存空间

这在更新的示例中是必需的,因为事件\u t是队列\u t的成员,并且严格别名适用于:

。。。一种聚合或联合类型,其成员中包含上述类型之一,递归地包括子聚合或包含的联合的成员,或

在最初的示例中,有许多原因可能会将类型视为别名,从而导致相同的结果,即使用char指针,并且可能会认为类型在某些体系结构(如果不是所有体系结构)上具有足够的兼容性

因此,编译器需要在内存发生变异后重新加载内存,以避免可能的冲突

const关键字并没有真正进入这个过程,因为一个可能指向同一内存地址的指针发生了变异

编辑为方便起见,以下是有关访问变量的完整规则:

对象的存储值只能由具有以下类型之一的左值表达式访问:

-与对象的有效类型兼容的类型

-与对象的有效类型兼容的类型的限定版本

-与对象的有效类型相对应的有符号或无符号类型

-一种类型,它是与对象的有效类型的限定版本相对应的有符号或无符号类型

-在其成员中包含上述类型之一的聚合或联合类型,递归地包括子聚合或包含的联合的成员,或

-字符类型

88此列表的目的是指定对象可以或不可以使用别名的情况

附笔。 后缀由POSIX保留。您可以考虑使用不同的后缀。


通常的做法是使用_表示结构,使用_表示联合。

[谈论原始程序]

这是由于Clang生成的TBAA元数据中存在缺陷造成的

如果使用-S-emit LLVM发射LLVM IR,您将看到为简洁起见被剪断的部分:

...

  %9 = load i8, i8* %wr_idx, align 1, !tbaa !12
  %10 = trunc i32 %8 to i8
  %11 = add i8 %10, -1
  %conv4 = and i8 %11, %9
  store i8 %conv4, i8* %wr_idx, align 1, !tbaa !12
  br label %return

...

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, !"min_enum_size", i32 4}
!2 = !{!"clang version 10.0.0 (/home/chill/src/llvm-project 07da145039e1a6a688fb2ac2035b7c062cc9d47d)"}
!3 = !{!4, !9, i64 8}
!4 = !{!"queue", !5, i64 0, !8, i64 4, !9, i64 8, !6, i64 10, !6, i64 11}
!5 = !{!"any pointer", !6, i64 0}
!6 = !{!"omnipotent char", !7, i64 0}
!7 = !{!"Simple C/C++ TBAA"}
!8 = !{!"int", !6, i64 0}
!9 = !{!"short", !6, i64 0}
!10 = !{!4, !8, i64 4}
!11 = !{!4, !5, i64 0}
!12 = !{!4, !6, i64 11}

查看TBAA元数据!4:这是队列的类型描述符,顺便说一句,我在结构中添加了名称,例如typedef struct queue。。。您可能会在那里看到空字符串。描述中的每个元素都对应于结构字段,请看!5,这是事件队列字段:它是任意指针!此时,我们已经丢失了指针实际类型的所有信息,这告诉我编译器将假定通过该指针的写入可以修改任何内存对象

这就是说,TBAA元数据有了一种新的形式,更精确的形式仍然是d 不足之处,但稍后

使用-Xclang-new结构路径tbaa编译原始程序。我的确切命令行是,并且我包括stddef.h而不是stdlib.h,因为开发构建没有libc:

./bin/clang -I lib/clang/10.0.0/include/ -target armv7m-eabi -O2 -Xclang -new-struct-path-tbaa  -S queue.c
最终的组件再次出现,一些绒毛被剪断:

queue_enqueue:
    push    {r4, r5, r6, r7, lr}
    add r7, sp, #12
    str r11, [sp, #-4]!
    ldrh    r3, [r0, #8]
    ldr.w   r12, [r0, #4]
    cmp r12, r3
    bne .LBB0_2
    movs    r0, #1
    ldr r11, [sp], #4
    pop {r4, r5, r6, r7, pc}
.LBB0_2:
    ldrb    r2, [r0, #11]                   ; load `wr_idx`
    ldr.w   lr, [r0]                        ; load `queue` member
    ldrd    r6, r1, [r1]                    ; load data pointed to by `event_ptr`
    add.w   r5, lr, r2, lsl #3              ; compute address to store the event
    str r1, [r5, #4]                        ; store `param`
    adds    r1, r3, #1                      ; increment `num_of_items`
    adds    r4, r2, #1                      ; increment `wr_idx`
    str.w   r6, [lr, r2, lsl #3]            ; store `event_type`
    strh    r1, [r0, #8]                    ; store new value for `num_of_items`
    sub.w   r1, r12, #1                     ; compute size mask
    ands    r1, r4                          ; bitwise and size mask with `wr_idx`
    strb    r1, [r0, #11]                   ; store new value for `wr_idx`
    movs    r0, #0
    ldr r11, [sp], #4
    pop {r4, r5, r6, r7, pc}
看起来不错,不是吗D

我在前面提到过,新的struct路径存在一些缺陷,但正因为如此:在邮件列表上


顺便说一句,恐怕在这种情况下没有什么可以吸取的教训。原则上,向编译器提供的信息越多越好:诸如限制、强类型转换(而非无端强制转换)、类型双关等,相关函数和变量属性。。。但不是在这种情况下,原始程序已经包含了所有必要的信息。这只是编译器的缺陷,解决这些问题的最佳方法是提高意识:询问邮件列表和/或文件错误报告。

[谈论原始程序]

这是由于Clang生成的TBAA元数据中存在缺陷造成的

如果使用-S-emit LLVM发射LLVM IR,您将看到为简洁起见被剪断的部分:

...

  %9 = load i8, i8* %wr_idx, align 1, !tbaa !12
  %10 = trunc i32 %8 to i8
  %11 = add i8 %10, -1
  %conv4 = and i8 %11, %9
  store i8 %conv4, i8* %wr_idx, align 1, !tbaa !12
  br label %return

...

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, !"min_enum_size", i32 4}
!2 = !{!"clang version 10.0.0 (/home/chill/src/llvm-project 07da145039e1a6a688fb2ac2035b7c062cc9d47d)"}
!3 = !{!4, !9, i64 8}
!4 = !{!"queue", !5, i64 0, !8, i64 4, !9, i64 8, !6, i64 10, !6, i64 11}
!5 = !{!"any pointer", !6, i64 0}
!6 = !{!"omnipotent char", !7, i64 0}
!7 = !{!"Simple C/C++ TBAA"}
!8 = !{!"int", !6, i64 0}
!9 = !{!"short", !6, i64 0}
!10 = !{!4, !8, i64 4}
!11 = !{!4, !5, i64 0}
!12 = !{!4, !6, i64 11}

查看TBAA元数据!4:这是队列的类型描述符,顺便说一句,我在结构中添加了名称,例如typedef struct queue。。。您可能会在那里看到空字符串。描述中的每个元素都对应于结构字段,请看!5,这是事件队列字段:它是任意指针!此时,我们已经丢失了指针实际类型的所有信息,这告诉我编译器将假定通过该指针的写入可以修改任何内存对象

这就是说,TBAA元数据有了一种新的形式,更精确的形式仍然有不足之处,但稍后

使用-Xclang-new结构路径tbaa编译原始程序。我的确切命令行是,并且我包括stddef.h而不是stdlib.h,因为开发构建没有libc:

./bin/clang -I lib/clang/10.0.0/include/ -target armv7m-eabi -O2 -Xclang -new-struct-path-tbaa  -S queue.c
最终的组件再次出现,一些绒毛被剪断:

queue_enqueue:
    push    {r4, r5, r6, r7, lr}
    add r7, sp, #12
    str r11, [sp, #-4]!
    ldrh    r3, [r0, #8]
    ldr.w   r12, [r0, #4]
    cmp r12, r3
    bne .LBB0_2
    movs    r0, #1
    ldr r11, [sp], #4
    pop {r4, r5, r6, r7, pc}
.LBB0_2:
    ldrb    r2, [r0, #11]                   ; load `wr_idx`
    ldr.w   lr, [r0]                        ; load `queue` member
    ldrd    r6, r1, [r1]                    ; load data pointed to by `event_ptr`
    add.w   r5, lr, r2, lsl #3              ; compute address to store the event
    str r1, [r5, #4]                        ; store `param`
    adds    r1, r3, #1                      ; increment `num_of_items`
    adds    r4, r2, #1                      ; increment `wr_idx`
    str.w   r6, [lr, r2, lsl #3]            ; store `event_type`
    strh    r1, [r0, #8]                    ; store new value for `num_of_items`
    sub.w   r1, r12, #1                     ; compute size mask
    ands    r1, r4                          ; bitwise and size mask with `wr_idx`
    strb    r1, [r0, #11]                   ; store new value for `wr_idx`
    movs    r0, #0
    ldr r11, [sp], #4
    pop {r4, r5, r6, r7, pc}
看起来不错,不是吗D

我在前面提到过,新的struct路径存在一些缺陷,但正因为如此:在邮件列表上


顺便说一句,恐怕在这种情况下没有什么可以吸取的教训。原则上,向编译器提供的信息越多越好:诸如限制、强类型转换(而非无端强制转换)、类型双关等,相关函数和变量属性。。。但不是在这种情况下,原始程序已经包含了所有必要的信息。这只是编译器的缺陷,解决这些缺陷的最佳方法是提高意识:询问邮件列表和/或文件错误报告。

尽管如此,quueue_is_full的定义对编译器来说是可见的,因此编译器可以确定它确实不存在。-它不仅是可见的,而且clang还内联了它的内容。25条汇编指令中有4条是不需要的,而且ldr/str指令在arm cortex-m3上可能需要3个周期,而常规mov/add/和需要1个周期。考虑到提供了-O2,我不认为这只是错过了机会,也就是说,quueue_is_full的定义对编译器是可见的,并且是可用的,因此它可以确定它确实不存在。-它不仅是可见的,而且clang还内联了它的内容。25条汇编指令中有4条是不需要的,而且ldr/str指令在arm cortex-m3上可能需要3个周期,而常规mov/add/和需要1个周期。考虑到提供了-O2,我不认为它只是错过了机会const并不意味着该值不能更改;这意味着不能使用常量标识符更改该值。int foo;int*a=&foo;int const*b=&foo*a=42/*正常*/*b=-1/*否*/@pmg I的意思是它不能被const event_t*指针更改。我再加上这个clarification@StaceyGirl你为什么删除你的答案?@AlexLop。这不是注册压力。这在其他体系结构的汇编中是可见的。我认为您应该检查IR以了解发生了什么,因为它有显式的别名注释!tbaa X。我检查了它,发现Clang没有tbaa注释进行事件复制,tbaa注释可以解释值flush,但同时我看到不同的编译器配置可能会生成llvm.memcpy调用!tbaa元数据。我可能会稍后再检查,但无法从中写出答案。@StaceyGirl谢谢!我很感激。将等待您的输入。还请注意,在x86体系结构上复制类似的行为是可能的,而且看起来很奇怪。。。const并不意味着该值不能更改;这意味着不能使用常量标识符更改该值。int foo;int*a=&foo;int const*b=&foo*a=42/*正常*/*b=-1/*否*/@pmg I的意思是它不能被const event_t*指针更改。我再加上这个clarification@StaceyGirl你为什么删除你的答案?@AlexLop。这不是注册压力。这在其他体系结构的汇编中是可见的。我想你应该检查一下红外线

o弄清楚发生了什么,因为它有显式的别名注释!tbaa X。我检查了它,发现Clang没有tbaa注释进行事件复制,tbaa注释可以解释值flush,但同时我看到不同的编译器配置可能会生成llvm.memcpy调用!tbaa元数据。我可能会稍后再检查,但无法从中写出答案。@StaceyGirl谢谢!我很感激。将等待您的输入。还请注意,在x86体系结构上复制类似的行为是可能的,而且看起来很奇怪。。。但是,即使它们指向同一个位置,这里唯一的操作就是逐个复制dword,那么它如何混淆编译器呢?@AlexLop。因为您通常无法将对象复制到已存储的地址,或者复制重叠的对象。好吧,我可以理解,但我仍然不知道它会如何影响完全不同结构(如队列)的字段?为什么要重新读取这些字段?常量限定符并不重要,因为它们是由函数添加的,原始类型可以在其他地方修改。-即使函数只获得一个指针,在许多其他情况下也是如此。但生成的程序集并不总是读取、修改并立即将值写回内存,除非volatile@AlexLop. *const限定符是程序员而不是编译器的编码样式。编译器应该能够推断指针变量是否在函数中被修改或未被修改,而不考虑限定符。但即使它们指向同一位置,这里唯一的操作就是逐dword复制dword,那么它如何混淆编译器?@AlexLop。因为您通常无法将对象复制到已存储的地址,或者复制重叠的对象。好吧,我可以理解,但我仍然不知道它会如何影响完全不同结构(如队列)的字段?为什么要重新读取这些字段?常量限定符并不重要,因为它们是由函数添加的,原始类型可以在其他地方修改。-即使函数只获得一个指针,在许多其他情况下也是如此。但生成的程序集并不总是读取、修改并立即将值写回内存,除非volatile@AlexLop. *const限定符是程序员而不是编译器的编码样式。编译器应该能够推断指针变量是否在函数中被修改或未被修改,而不考虑限定符。但在原始示例中,事件\u t不是队列\u t的成员。@StaceyGirl-true,但还有其他原因导致此情况。我不想为这件事辩护,所以我把清单附在了答案后面。。。有人可能会争辩说,事件类型的兼容性足以考虑将uint32添加到类型双关中,从而使前两个成员保持对齐。。。其他人可能会注意到,任何字符指针或uint8_t*始终被视为可能的内存别名。在我们的例子中,queue_ptr->wr_idx指向一个未签名的字符,并发出一个别名标志。。。谁知道呢。我不太明白为什么他们可能会像讨论效果一样使用别名。我不认为这里的情况是这样的。。。例如,看看各种情况下问题的再现:-f5没有重新读取,而f6没有重新读取does@AlexLop. 您提供的示例可能不相关,因为f5中的char*没有发生变异,只有int*发生了变异,并且它没有潜在的别名问题,而f6明显存在严格的别名问题。但在原始示例中,event_t不是queue_t的成员。@StaceyGirl-true,但还有其他原因导致此问题。我不想为这件事辩护,所以我把清单附在了答案后面。。。有人可能会争辩说,事件类型的兼容性足以考虑将uint32添加到类型双关中,从而使前两个成员保持对齐。。。其他人可能会注意到,任何字符指针或uint8_t*始终被视为可能的内存别名。在我们的例子中,queue_ptr->wr_idx指向一个未签名的字符,并发出一个别名标志。。。谁知道呢。我不太明白为什么他们可能会像讨论效果一样使用别名。我不认为这里的情况是这样的。。。例如,看看各种情况下问题的再现:-f5没有重新读取,而f6没有重新读取does@AlexLop. 您提供的示例可能与此无关,因为f5中的char*没有发生变异,只有int*发生了变异,并且它没有潜在的别名问题,而f6明显存在严格的别名问题。谢谢!这看起来比之前发布的所有内容都更令人满意。那么为什么不总是使用-Xclang-new结构路径tbaa选项呢?它的优点/缺点是什么?或者它应该是一个单独的问题…?@AlexLop。如果答案能回答我认为能回答的问题,别忘了接受。否则,如果回答的得票率较低,奖金将到期,不会授予任何人。@StaceyGirl谢谢Stacy,我同意这回答了我的问题。。。没有充分考虑。我还是不明白我能从中学到什么
这是因为我的编程/优化技能,因此我可以为基础架构层实现最优化的代码。除非确实如此,否则它可以被归类为编译器问题,这将在未来的版本中得到修复,与我的编码习惯无关。@chill请您参考或在您的答案中提及以下主题,以便我可以将您的答案标记为已接受:1。如何防止将来的此类重读2。你用来消除这些重读的标志有什么不好的地方。@AlexLop。在这种情况下,恐怕没有什么可以吸取的教训。原则上,向编译器提供的信息越多越好:诸如限制、强类型转换(而非无端强制转换)、类型双关等,相关函数和变量属性。。。但不是在这种情况下,原始程序已经包含了所有必要的信息。这只是一个编译器缺陷,解决这些问题的最好方法是提高意识:在邮件列表和/或文件错误报告中询问。谢谢!这看起来比之前发布的所有内容都更令人满意。那么为什么不总是使用-Xclang-new结构路径tbaa选项呢?它的优点/缺点是什么?或者它应该是一个单独的问题…?@AlexLop。如果答案能回答我认为能回答的问题,别忘了接受。否则,如果回答的得票率较低,奖金将到期,不会授予任何人。@StaceyGirl谢谢Stacy,我同意这回答了我的问题。。。没有充分考虑。我仍然看不到我可以从中学到什么编程/优化技能,以便为基础架构层实现最优化的代码。除非确实如此,否则它可以被归类为编译器问题,这将在未来的版本中得到修复,与我的编码习惯无关。@chill请您参考或在您的答案中提及以下主题,以便我可以将您的答案标记为已接受:1。如何防止将来的此类重读2。你用来消除这些重读的标志有什么不好的地方。@AlexLop。在这种情况下,恐怕没有什么可以吸取的教训。原则上,向编译器提供的信息越多越好:诸如限制、强类型转换(而非无端强制转换)、类型双关等,相关函数和变量属性。。。但不是在这种情况下,原始程序已经包含了所有必要的信息。这只是一个编译器缺陷,解决这些问题的最佳方法是提高意识:询问邮件列表和/或提交错误报告。