GCC 调整比较常量的通常原因是创建更小的立即数,这有助于它适合任何宽度的立即数。理解 if (a>=3) 的 gcc 输出 https://stackoverflow.com/q/46412381 / 在比较中,GCC 似乎更喜欢小的立即值。有办法避免这种情况吗? https://stackoverflow.com/q/59117603(它总是这样做,而不是检查这个常量在目标 ISA 上是否真的有用。)这种启发式方法适用于大多数 ISA,但有时不适用于 AArch64 或 ARM Thumb,它们可以将一些立即数编码为位范围/位-pattern,因此并不总是数值越小越好。
The width-1
is not其中一部分。这-1
是一个的一部分范围检查 https://stackoverflow.com/questions/5196527/double-condition-checking-in-assembly跳过自动矢量化循环(一次 16 个字节)movups
)并直接进行清理,1..3 标量存储。
似乎正在检查width >= 1 && width <= 3
,即需要清理,但总大小小于完整的向量宽度。它不等于已签名或未签名width <= 3
for width=0
。注意无符号比较:0 - 1
上面是2U
, 因为-1U
是 UINT_MAX。
但已经排除了width <= 0
with test r9d, r9d
/ jle _cls_return
,所以 GCC 最好只检查width <= 3U
而不是做额外的工作来从范围检查中排除零。 (一个lea
,并保存/恢复未使用的 R12!)
(清理也可能看起来过于复杂,例如使用movq [rdx], xmm0
如果需要超过 1 个 uint,并且针对各种情况进行一些奇怪的分支。更好的是,如果总大小 >= 4 uints,只需再做一次movups
结束于范围末尾,可能与之前的商店重叠。)
是的,这是一个错过的优化,您可以报告它https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc(现在您知道这是一个错过的优化;最好先在这里询问,而不是在没有首先弄清楚是否可以避免该指令的情况下提交错误。)
对我来说唯一有意义的是,比较两个的幂在某种程度上更快。
不,不是更快,而是更快。cmp
性能根本不依赖于数据。 (没有整数指令,除非有时[i]div
。而在 Zen3 之前的 AMD CPU 上,pext
/ pdep
。但无论如何,这不是简单的整数加/比较/移位的东西。看https://uops.info/ https://uops.info/).
顺便说一句,我们可以重现您的Godbolt 上的 GCC asm 输出 https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,selection:(endColumn:22,endLineNumber:2,positionColumn:22,positionLineNumber:2,selectionStartColumn:22,selectionStartLineNumber:2,startColumn:22,startLineNumber:2),source:%27%0A__attribute__((ms_abi))%0Avoid+clear_screen(unsigned+int+color,+void+*memory,+int+height,+int+width)+%7B%0A++unsigned+int+*pixel+%3D+(unsigned+int+*)memory%3B%0A++for+(auto+y+%3D+0%3B+y+%3C+height%3B+y%2B%2B)%0A++++for+(auto+x+%3D+0%3B+x+%3C+width%3B+x%2B%2B)%0A++++++*pixel%2B%2B+%3D+color%3B%0A%7D%0A%27),l:%275%27,n:%270%27,o:%27C%2B%2B+source+%231%27,t:%270%27)),k:35.58012044257613,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((h:compiler,i:(compiler:g121,filters:(b:%270%27,binary:%271%27,commentOnly:%270%27,demangle:%271%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%270%27,trim:%271%27),flagsViewOpen:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,libs:!(),options:%27-O3%27,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1,tree:%271%27),l:%275%27,n:%270%27,o:%27x86-64+gcc+12.1+(C%2B%2B,+Editor+%231,+Compiler+%231)%27,t:%270%27)),k:34.351129485231034,l:%274%27,m:100,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((h:compiler,i:(compiler:clang1400,filters:(b:%270%27,binary:%271%27,commentOnly:%270%27,demangle:%271%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%270%27,trim:%271%27),flagsViewOpen:%271%27,fontScale:14,fontUsePx:%270%27,j:2,lang:c%2B%2B,libs:!(),options:%27-O3%27,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1,tree:%271%27),l:%275%27,n:%270%27,o:%27x86-64+clang+14.0.0+(C%2B%2B,+Editor+%231,+Compiler+%232)%27,t:%270%27)),header:(),k:30.06875007219285,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27)),l:%272%27,n:%270%27,o:%27%27,t:%270%27)),version:4告诉它这个函数是__attribute__((ms_abi))
,或者有一个命令行选项来设置调用约定默认值。 (它实际上只对查看 asm 有用;它仍然使用 GNU/Linux 标头和 x86-64 System V 类型宽度,例如 64 位long
。只有合适的 MinGW(交叉)编译器才能向您展示 GCC 在针对 Windows 时真正会做什么。)
这是气体.intel_syntax noprefix
,类似于 MASM,而不是 NASM,但只有在涉及全局变量的寻址模式中,差异才会明显。