首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么C++使用32位寄存器来存储8位值

为什么C++使用32位寄存器来存储8位值
EN

Stack Overflow用户
提问于 2020-07-09 14:28:22
回答 2查看 142关注 0票数 2

我已经尝试了以下C++代码:

代码语言:javascript
复制
void foo( ) {
    char c = 'a';
    c = c + 1;
}

获得了以下结果x86-64 gcc 10.1 default flags

代码语言:javascript
复制
    mov     BYTE PTR [rbp-1], 97
    movzx   eax, BYTE PTR [rbp-1]  ; EAX here
    add     eax, 1
    mov     BYTE PTR [rbp-1], al

但!获得了以下结果x86-64 djgpp 7.2.0 default flags

代码语言:javascript
复制
    mov     BYTE PTR [ebp-1], 97
    mov     al, BYTE PTR [ebp-1] ; AL here
    inc     eax
    mov     BYTE PTR [ebp-1], al

为什么GCC不使用AL而使用EAX

为什么djgpp只使用AL

是性能问题吗?

如果是这样的话,使用32位寄存器作为8位值背后的性能问题是什么?

EN

回答 2

Stack Overflow用户

发布于 2020-07-10 02:13:41

在AMD和最新的Intel处理器上,加载部分寄存器需要整个寄存器的先前值,以便将其与加载的值相结合,以生成新的寄存器值。

如果写入全部寄存器,则不需要旧值,因此,通过寄存器重命名,可以在寄存器的前一次写入之前完成。

票数 2
EN

Stack Overflow用户

发布于 2020-07-09 19:26:58

代码语言:javascript
复制
unsigned char fun ( unsigned char a, unsigned char b )
{
    return(a+b);
}

Disassembly of section .text:

0000000000000000 <fun>:
   0:   8d 04 3e                lea    (%rsi,%rdi,1),%eax
   3:   c3                      retq  

Disassembly of section .text:

00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e20000ff    and r0, r0, #255    ; 0xff
   8:   e12fff1e    bx  lr


Disassembly of section .text:

00000000 <fun>:
   0:   1840        adds    r0, r0, r1
   2:   b2c0        uxtb    r0, r0
   4:   4770        bx  lr

Disassembly of section .text:

00000000 <fun>:
   0:   952e                    add x10,x10,x11
   2:   0ff57513            andi    x10,x10,255
   6:   8082                    ret

不同的目标都来自于gcc。

这是一个编译器选择,所以您需要与编译器作者讨论它,而不是Stack Overflow。编译器需要在功能上实现高级语言,所以在所有这些都有32位GPRs的情况下,选择是屏蔽每个操作,或者至少在值留下来供以后使用之前,还是假设寄存器是脏的,在使用它之前需要屏蔽它,或者你是否有像eax这样的架构功能,可以在较小的部分ax,al中访问,并围绕它进行设计?只要它在功能上工作,任何解决方案都是完全没有问题的。

一个编译器可能选择使用al进行8位操作,另一个编译器可能选择eax (从性能角度来看,这可能更有效,在这一主题上有一些东西可以阅读),在这两种情况下,您都必须为rax/eax/ax寄存器中的剩余位进行设计,并且以后不会对其进行oops,而是使用更大的寄存器。

如果你没有这个部分寄存器访问的选项,你很需要在功能上实现代码,而最简单的方法是做掩码的事情。这将与本例中的C代码相匹配,有人可能会认为x86代码有错误,因为它使用了eax,但没有裁剪,所以不会返回无符号字符。

不过,请签名:

代码语言:javascript
复制
signed char fun ( signed char a, signed char b )
{
    return(a+b);
}

Disassembly of section .text:

0000000000000000 <fun>:
   0:   8d 04 3e                lea    (%rsi,%rdi,1),%eax
   3:   c3                      retq  

Disassembly of section .text:

00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e1a00c00    lsl r0, r0, #24
   8:   e1a00c40    asr r0, r0, #24
   c:   e12fff1e    bx  lr

同样的故事,一种编译器设计显然会以一种方式处理可变大小,然后以另一种方式处理。

强制它处理此函数中的大小

代码语言:javascript
复制
signed char fun ( signed char a, signed char b )
{
    if((a+b)>200) return(1);
    return(0);
}

Disassembly of section .text:

0000000000000000 <fun>:
   0:   40 0f be f6             movsbl %sil,%esi
   4:   40 0f be ff             movsbl %dil,%edi
   8:   01 f7                   add    %esi,%edi
   a:   81 ff c8 00 00 00       cmp    $0xc8,%edi
  10:   0f 9f c0                setg   %al
  13:   c3                      retq 

Disassembly of section .text:

00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e35000c8    cmp r0, #200    ; 0xc8
   8:   d3a00000    movle   r0, #0
   c:   c3a00001    movgt   r0, #1
  10:   e12fff1e    bx  lr

因为arm设计知道传入的值已经被裁剪了,这比他们选择不裁剪它要大,可能是因为我把它留为有符号的。但是,在x86的情况下,因为它们不会在退出的过程中进行裁剪,所以它们会在进入操作的过程中进行裁剪。

代码语言:javascript
复制
unsigned char fun ( unsigned char a, unsigned char b )
{
    if((a+b)>200) return(1);
    return(0);
}

Disassembly of section .text:

00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e35000c8    cmp r0, #200    ; 0xc8
   8:   d3a00000    movle   r0, #0
   c:   c3a00001    movgt   r0, #1
  10:   e12fff1e    bx  lr

现在我不同意,因为例如0xFF + 0x01 = 0x00,它不大于200,但这段代码将它作为大于200传递。他们还在无符号比较中使用了带符号的小于和大于。

代码语言:javascript
复制
unsigned char fun ( unsigned char a, unsigned char b )
{
    if(((unsigned char)(a+b))>200) return(1);
    return(0);
}
00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e20000ff    and r0, r0, #255    ; 0xff
   8:   e35000c8    cmp r0, #200    ; 0xc8
   c:   93a00000    movls   r0, #0
  10:   83a00001    movhi   r0, #1
  14:   e12fff1e    bx  lr

啊,这就是C语言推广的事了。(就像float f;f=f+1.0;vs f=f+1.0F;)

这也改变了x86的结果

代码语言:javascript
复制
Disassembly of section .text:

0000000000000000 <fun>:
   0:   01 fe                   add    %edi,%esi
   2:   40 80 fe c8             cmp    $0xc8,%sil
   6:   0f 97 c0                seta   %al
   9:   c3                      retq 

为什么GCC使用EAX而不是AL?

为什么djgpp只使用AL?

是性能问题吗?

这些是编译器设计的选择,不是问题,也不是必须的性能,而是关于如何用目标指令集实现高级语言的总体编译器设计。每个编译器都可以随心所欲地做这件事,没有理由期望gcc、clang和djgpp等人有相同的设计选择,也没有理由期望gcc版本x.x.x和y.y.y有相同的设计选择,所以如果你追溯到足够远的地方,也许它是不同的,也可能不是(如果他们有,那么也许提交解释了“为什么”的问题,那时的开发小组电子邮件将会涵盖它)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62808748

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档