- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一些代码来将屏幕清除为特定颜色。 C++代码:
void clear_screen(unsigned int color, void *memory, int height, int width) {
unsigned int *pixel = (unsigned int *)memory;
for (auto y = 0; y < height; y++)
for (auto x = 0; x < width; x++)
*pixel++ = color;
}
我用g++和objconv生成了相应的程序集。这就是我得到的,并且我已经评论了我认为某些行也是如此。
renderer_clear_screen:
push r13
push r12
push rbp
push rdi
push rsi
push rbx
mov r11d, ecx ; move the color into r11d
mov ebx, r8d ; move the height into ebx
mov rcx, rdx ; 000E _ 48: 89. D1st
test r8d, r8d ;
jle _cls_return ; basically, return if width or height is 0
test r9d, r9d ; ( window minimized )
jle _cls_return ;
mov r8d, r9d ; height = width
mov esi, r9d ; esi = width
mov edi, r9d ; edi = width
xor r10d, r10d ; r10d = 0
shr esi, 2 ; esi = width / 2
movd xmm1, r11d ; move the lower 32-bits of the color into xmm1
lea r12d, [r9-1] ; r12d = width - 1
shl rsi, 4 ; 003F _ 48: C1. E6, 04
mov ebp, r8d ; 0043 _ 44: 89. C5
shl rdi, 2 ; 0046 _ 48: C1. E7, 02
pshufd xmm0, xmm1, 0 ; 004A _ 66: 0F 70. C1, 00
shl rbp, 2 ; 004F _ 48: C1. E5, 02
ALIGN 8
?_001: cmp r12d, 2
jbe ?_006 ; if (width - 1 <= 2) { ?_006 }
mov rax, rcx ; 005E _ 48: 89. C8
lea rdx, [rcx+rsi] ; 0061 _ 48: 8D. 14 31
ALIGN 8
?_002: movups oword [rax], xmm0 ; 0068 _ 0F 11. 00
add rax, 16 ; 006B _ 48: 83. C0, 10
cmp rdx, rax ; 006F _ 48: 39. C2
jnz ?_002 ; 0072 _ 75, F4
lea rdx, [rcx+rbp] ; 0074 _ 48: 8D. 14 29
mov eax, r8d ; 0078 _ 44: 89. C0
cmp r9d, r8d ; 007B _ 45: 39. C1
jz ?_004 ; 007E _ 74, 1C
?_003: lea r13d, [rax+1H] ; 0080 _ 44: 8D. 68, 01
mov dword [rdx], r11d ; 0084 _ 44: 89. 1A
cmp r13d, r9d ; 0087 _ 45: 39. CD
jge ?_004 ; 008A _ 7D, 10
add eax, 2 ; 008C _ 83. C0, 02
mov dword [rdx+4H], r11d ; 008F _ 44: 89. 5A, 04
cmp r9d, eax ; 0093 _ 41: 39. C1
jle ?_004 ; 0096 _ 7E, 04
mov dword [rdx+8H], r11d ; 0098 _ 44: 89. 5A, 08
?_004: add r10d, 1 ; 009C _ 41: 83. C2, 01
add rcx, rdi ; 00A0 _ 48: 01. F9
cmp ebx, r10d ; 00A3 _ 44: 39. D3
jnz ?_001 ; 00A6 _ 75, B0
_cls_return:
pop rbx ;
pop rsi ;
pop rdi ;
pop rbp ;
pop r12 ;
pop r13 ; pop all the saved registers
ret ;
?_006: ; Local function
mov rdx, rcx ; 00B1 _ 48: 89. CA
xor eax, eax ; 00B4 _ 31. C0
jmp ?_003 ; 00B6 _ EB, C8
现在,在 ?_001
中,编译器将 width - 1
与 2 进行比较,这与比较 是一回事宽度
到3。我的问题是,对于 -O3
,为什么编译器选择了两个而不是三个,并浪费了一个 lea
(将 width - 1
移动到r12d
).
对我来说唯一有意义的是,两个的幂在某种程度上比较快。还是编译器的怪癖?
最佳答案
GCC 调整比较常量的通常原因是创建更小的立即数,这有助于它适应任何宽度的立即数。 Understanding gcc output for if (a>=3)/GCC seems to prefer small immediate values in comparisons. Is there a way to avoid that? (它总是这样做,而不是检查目标 ISA 上的这个常量是否真的有用。)这种启发式方法适用于大多数 ISA,但有时不适用于 AArch64 或 ARM Thumb,它们可以将一些立即数编码为位范围/位-pattern,所以并不总是数字越小越好。
width-1
不是其中的一部分。 -1
是 range check 的一部分跳过自动矢量化循环(使用 movups
一次 16 个字节)并直接进行清理,1..3 标量存储。
好像是在检查width >= 1 && width <= 3
,即需要清理但总大小小于完整 vector 宽度。它不等同于已签名或未签名的 width <= 3
对于 width=0
.注意无符号比较:0 - 1
在2U
之上,因为 -1U
是 UINT_MAX。
但它已经排除了width <= 0
与 test r9d, r9d
/jle _cls_return
, 所以 GCC 最好只检查 width <= 3U
而不是做额外的工作来从范围检查中排除零。 (一个 lea
,并保存/恢复 R12 未被使用!)
(清理也可能看起来过于复杂,例如,如果需要超过 1 个单位,则使用 movq [rdx], xmm0
,并且在各种情况下使用一些奇怪的分支。甚至更好,如果总大小 >= 4 个单位,只需再做一个 movups
,它在范围的末尾结束,可能与之前的商店重叠。)
是的,这是一个错过的优化,你可以在https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc上报告它(现在您知道这是一个错过的优化;最好先在这里询问,而不是在没有先弄清楚是否可以避免该指令的情况下提交错误。)
The only thing which makes sense to me is that powers of two are somehow faster to compare.
不,它并没有更快; cmp
性能根本不依赖于数据。 (没有整数指令,除了有时 [i]div
。在 Zen3 之前的 AMD CPU 上, pext
/pdep
。但无论如何,不是简单的整数加法/比较/移位的东西。见 https://uops.info/ )。
顺便说一句,我们可以重现您的 GCC asm output on Godbolt告诉它这个函数是__attribute__((ms_abi))
,或者有一个命令行选项来设置调用约定默认值。 (它实际上只对查看 asm 有用;它仍在使用 GNU/Linux header 和 x86-64 System V 类型宽度,如 64 位 long
。只有合适的 MinGW(交叉)编译器才能向您展示 GCC 的真正用途在面向 Windows 时执行。)
这是煤气 .intel_syntax noprefix
,它类似于 MASM,而不是 NASM,但只有在涉及全局变量的寻址模式下,差异才会很明显。
关于c++ - 为什么 GCC 减去 1 并比较 <= 2? cmp 在汇编中使用 2 的幂会更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72482054/
我试图在图形模式下打印一个字符。通常当我打印我正在做的一个字符时: mov ah,14 ; ah=14 mov al,'x' int 10h ; print the character 这
我试图通过更改其中的一个字节来修改存储在内存中的字符串。我为此使用了 movb,但由于某种原因,给定内存位置的字节没有改变。 在 gdb 调试器上: 14 movb %al, (%r10) # nex
我一直在阅读一些汇编代码,并且开始发现调用指令实际上是与程序计数器相关的。 但是,每当我使用 Visual Studio 或 Windbg 进行调试时,它总是显示 call 0xFFFFFF ...这
我最近一直在使用 Visual C++ 中的内联汇编,我想知道是否可以直接向堆栈上的局部变量添加值,例如: push 5 add [esp], 7 这样做可以吗?我问这个问题是因为我在执行此操作时随机
我有下一个代码: mov al, -5 add al, 132 add al, 1 据我检查,溢出标志和进位标志将在第一个操作中设置,而在第二个操作中,仅设置溢出。 但我不明白为什么: 在无符号数中,
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
以下两者有什么区别: mov eax, [eax+4] 和 add eax, 4 mov eax, [eax] 如果不是,那么汇编器是否会选择哪个来进行某种优化? 最佳答案 这
看《The Shellcoder's Handbook》中的一些汇编和反汇编代码,发现一条指令的序列操作数是不一样的。 例如,在 assembly 上: mov ebx,0 并且,在反汇编时: mov
我有这个非常简单的汇编代码: start: add ax, 100 ; if ax overflow add to bx 1 jmp start 但我不知道如何检测 ax 寄存器溢出,有人可以帮
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
我正在学习一些关于操作系统开发的教程,我发现了一篇关于多重引导 header 。这些是您必须定义的一些“神奇”值才能使用GRUB2。这些是命令: # Declare constants used f
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
我正在尝试从内存中复制一些单词并使用汇编将其保存到另一个内存地址。我正在尝试为其编写代码,但我不确定其中的某些部分。我将简要描述我想要做什么。 源地址、目标地址和要复制的字数是函数的输入参数。 最佳答
当我们想要像这样创建一个初始化变量时: name db 'zara ali' 我们创建了一个字节大小变量,但我们在其中存储了一个字符串 这怎么可能?? 当我们使用这条指令时: MOV ecx, nam
我还是汇编的新手,我还不知道汇编中的许多命令代码。我想在 16 位寄存器中进行除法。我想打印它的内容。我知道我需要将寄存器的内容转换为 ASCII 进行打印,但同样,我的问题是除法。请帮我。 比如cx
使用有什么区别: c.eq.s $1, $2 bc1t L2 并使用: beq $1, $2, L2 如果他们做同样的事情,为什么有两种分支方式?如果它们不同,那么它们各自的好处是什么
源代码: int main() { int i; for(i=0, i : push rbp 2. 0x000055555555463b :
我是一名优秀的程序员,十分优秀!