如何对存储在 32 位寄存器对中的 64 位整数求反?

2023-12-19

我已经存储了一个64-bit中的整数EDX:EAX 寄存器对。 我怎么能够正确地 negate号码?

例如:123456789123-123456789123.


向编译器询问想法:编译int64_t neg(int64_t a) { return -a; }在 32 位模式下。当然,询问编译器的不同方式将在内存中、编译器选择的寄存器中或已经在 EDX:EAX 中获得起始值。查看全部三种方式在 Godbolt 编译器资源管理器上 https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,source:'%23include+%3Cstdint.h%3E%0A%0Aint64_t+neg_value_from_mem(int64_t+a)+%7B%0A+++++return+-a%3B%0A%7D%0A%0Aint64_t+neg_value_in_regs(int64_t+a)+%7B%0A++++//+The+OR+makes+the+compiler+load%2BOR+first%0A++++//+but+it+can+choose+regs+to+set+up+for+the+negate%0A++++int64_t+reg+%3D+a+%7C+0x1111111111LL%3B%0A++++//+clang+chooses+mov+reg,mem+++/+or+reg,imm8+when+possible,%0A++++//+otherwise+++++mov+reg,imm32+/+or+reg,mem.++Neat+:)%0A++++return+-reg%3B%0A%7D%0A%0A//int64_t+__attribute__((noinline))+foo()%7Breturn+0%3B%7D++//+in+case+you+want+to+compile+to+a+linked+binary%0Aint64_t+foo()%3B%0Aint64_t+neg_value_in_place(void)%0A%7B+++//+foo!'s+return+value+will+be+in+edx:eax%0A++++int64_t+a+%3D+foo()%3B%0A++++return+-a%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:36.372406215515106,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:g63,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+gcc+6.3+(Editor+%231,+Compiler+%231)',t:'0')),k:30.29426045115158,l:'4',m:69.94100486990892,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cl19_32,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-Ox',source:1),l:'5',n:'0',o:'x86+CL+19+2017+RTW+(Editor+%231,+Compiler+%233)',t:'0')),l:'4',m:30.05899513009107,n:'0',o:'',s:0,t:'0')),k:30.29426045115158,l:'3',n:'0',o:'',t:'0'),(g:!((h:compiler,i:(compiler:clang391,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+clang+3.9.1+(Editor+%231,+Compiler+%232)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4,带有 gcc、clang 和 MSVC(又名 CL)的 asm 输出。

当然有很多方法可以实现这一点,但任何可能的序列在某个时刻都需要某种从低到高的进位,因此没有有效的方法来避免 SBB 或 ADC。


如果该值在内存中开始,或者您想保留寄存器中的原始值,对目标进行异或清零并使用 SUB/SBB。 SysV x86-32 ABI 在堆栈上传递参数并在 EDX:EAX 中返回 64 位整数。这是什么铿锵3.9.1-m32 -O3 does https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,source:'%23include+%3Cstdint.h%3E%0A%0Aint64_t+neg_value_from_mem(int64_t+a)+%7B%0A+++++return+-a%3B%0A%7D%0A%0Aint64_t+neg_value_in_regs(int64_t+a)+%7B%0A++++//+The+OR+makes+the+compiler+load%2BOR+first%0A++++//+but+it+can+choose+regs+to+set+up+for+the+negate%0A++++int64_t+reg+%3D+a+%7C+0x1111111111LL%3B%0A++++//+clang+chooses+mov+reg,mem+++/+or+reg,imm8+when+possible,%0A++++//+otherwise+++++mov+reg,imm32+/+or+reg,mem.++Neat+:)%0A++++return+-reg%3B%0A%7D%0A%0A//int64_t+__attribute__((noinline))+foo()%7Breturn+0%3B%7D++//+in+case+you+want+to+compile+to+a+linked+binary%0Aint64_t+foo()%3B%0Aint64_t+neg_value_in_place(void)%0A%7B+++//+foo!'s+return+value+will+be+in+edx:eax%0A++++int64_t+a+%3D+foo()%3B%0A++++return+-a%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:36.372406215515106,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:g63,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+gcc+6.3+(Editor+%231,+Compiler+%231)',t:'0')),k:30.29426045115158,l:'4',m:69.94100486990892,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cl19_32,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-Ox',source:1),l:'5',n:'0',o:'x86+CL+19+2017+RTW+(Editor+%231,+Compiler+%233)',t:'0')),l:'4',m:30.05899513009107,n:'0',o:'',s:0,t:'0')),k:30.29426045115158,l:'3',n:'0',o:'',t:'0'),(g:!((h:compiler,i:(compiler:clang391,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+clang+3.9.1+(Editor+%231,+Compiler+%232)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4, for neg_value_from_mem:

    ; optimal for data coming from memory: just subtract from zero
    xor     eax, eax
    xor     edx, edx
    sub     eax, dword ptr [esp + 4]
    sbb     edx, dword ptr [esp + 8]

如果寄存器中有值并且不需要就地结果, 您可以使用NEG http://www.felixcloutier.com/x86/NEG.html将寄存器设置为 0 - 本身,当且仅当输入非零时设置 CF。即与 SUB 相同的方式。注意异或归零很便宜 https://stackoverflow.com/a/33668295/224132,而不是延迟关键路径的一部分,因此这绝对比 gcc 的 3 指令序列(如下)更好。

    ;; partially in-place: input in ecx:eax
    xor     edx, edx
    neg     eax         ; eax = 0-eax, setting flags appropriately
    sbb     edx, ecx    ;; result in edx:eax

即使对于就地情况,Clang 也会这样做,尽管这会花费额外的费用mov ecx,edx。这对于具有零延迟 mov reg,reg (Intel IvB+ 和 AMD Zen)的现代 CPU 上的延迟来说是最佳的,但对于融合域 uops(前端吞吐量)或代码大小的数量来说不是最佳的。


gcc 的序列很有趣,但并不完全明显。对于就地情况,与 clang 相比,它节省了一条指令,但否则情况会更糟。

    ; gcc's in-place sequence, only good for in-place use
    neg     eax
    adc     edx, 0
    neg     edx
       ; disadvantage: higher latency for the upper half than subtract-from-zero
       ; advantage: result in edx:eax with no extra registers used

不幸的是,gcc 和 MSVC 都总是使用这个,即使 xor-zero + sub/sbb 会更好。


要更完整地了解编译器的功能,请查看这些函数的输出(在上帝螺栓上 https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,source:'%23include+%3Cstdint.h%3E%0A%0Aint64_t+neg_value_from_mem(int64_t+a)+%7B%0A+++++return+-a%3B%0A%7D%0A%0Aint64_t+neg_value_in_regs(int64_t+a)+%7B%0A++++//+The+OR+makes+the+compiler+load%2BOR+first%0A++++//+but+it+can+choose+regs+to+set+up+for+the+negate%0A++++int64_t+reg+%3D+a+%7C+0x1111111111LL%3B%0A++++//+clang+chooses+mov+reg,mem+++/+or+reg,imm8+when+possible,%0A++++//+otherwise+++++mov+reg,imm32+/+or+reg,mem.++Neat+:)%0A++++return+-reg%3B%0A%7D%0A%0A//int64_t+__attribute__((noinline))+foo()%7Breturn+0%3B%7D++//+in+case+you+want+to+compile+to+a+linked+binary%0Aint64_t+foo()%3B%0Aint64_t+neg_value_in_place(void)%0A%7B+++//+foo!'s+return+value+will+be+in+edx:eax%0A++++int64_t+a+%3D+foo()%3B%0A++++return+-a%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:36.372406215515106,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:g63,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+gcc+6.3+(Editor+%231,+Compiler+%231)',t:'0')),k:30.29426045115158,l:'4',m:69.94100486990892,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cl19_32,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-Ox',source:1),l:'5',n:'0',o:'x86+CL+19+2017+RTW+(Editor+%231,+Compiler+%233)',t:'0')),l:'4',m:30.05899513009107,n:'0',o:'',s:0,t:'0')),k:30.29426045115158,l:'3',n:'0',o:'',t:'0'),(g:!((h:compiler,i:(compiler:clang391,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'-xc+-m32+-Wall+-Wextra+-O3++-mtune%3Dhaswell+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+clang+3.9.1+(Editor+%231,+Compiler+%232)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4)

#include <stdint.h>

int64_t neg_value_from_mem(int64_t a) {
     return -a;
}

int64_t neg_value_in_regs(int64_t a) {
    // The OR makes the compiler load+OR first
    // but it can choose regs to set up for the negate
    int64_t reg = a | 0x1111111111LL;
    // clang chooses mov reg,mem   / or reg,imm8 when possible,
    // otherwise     mov reg,imm32 / or reg,mem.  Nice :)
    return -reg;
}

int64_t foo();
int64_t neg_value_in_place(int64_t a) {
    // foo's return value will be in edx:eax
    return -foo();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何对存储在 32 位寄存器对中的 64 位整数求反? 的相关文章

  • 汇编器8086将32位数字除以16位数字

    我尝试将 32 位数字除以 16 位数字 例如 10000000h 除以 2000h 根据我尝试做的设计除以 右 4 位数字除以除数 然后左 4 位数字除以除数 这是我的代码 DATA num dd 10000000h divisor dw
  • 无法识别的仿真模式:MinGW32 上的 elf_i386

    我正在尝试制作内核 但无法链接C与程序集一起输出 这ld 我收到错误 无法识别的仿真模式 elf i386 我正在使用 Windows 10 专业版以及 MinGW32 和 MSYS 我正在使用的代码 link ld link ld OUT
  • Intel:序列化指令和分支预测

    英特尔架构开发人员手册 http www intel com content www us en architecture and technology 64 ia 32 architectures software developer v
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化
  • CISC 机器 - 它们不只是将复杂指令转换为 RISC 吗?

    也许我在架构上存在误解 但如果机器有 比如说 乘法指令 该指令是否未转换为更小的指令 或者过于复杂以至于最终与等效的 RISC 指令具有相同的速度 乘法是一个不好的例子 它在两种体系结构中都是一条指令 将上面的 乘法 替换为 CISC 中更
  • PAE(物理地址扩展)如何实现大于4GB的地址空间?

    维基百科文章的摘录物理地址扩展 http en wikipedia org wiki Physical Address Extension x86 处理器硬件架构通过用于选择附加内存的附加地址线进行了增强 因此物理地址大小从 32 位增加到
  • 将 C 代码转换为 x86-64 汇编

    我正在尝试将 C 代码转换为 x86 64 我的目标是反转链表 传入的两个参数是 head ptr 和 offset to 以获取指针字段的地址 即指向列表中下一个节点的指针 据我了解 head ptr是通过rdi寄存器传入的 offset
  • Polygot 包含 nasm/yasm 和 C 的文件

    我有一堆幻数 我想将它们包含在由 nasm 或 yasm 编译的 C 程序和汇编文件中 在纯 C 语言中 该文件看起来像是一系列定义 例如 define BLESS 55378008 define ANSWER 42 在 nasm 或 ya
  • long double(GCC 特定)和 __float128

    我正在寻找有关的详细信息long double and float128在 GCC x86 中 更多是出于好奇而不是因为实际问题 可能很少有人需要这些 我只是有史以来第一次 truly需要一个double 但我想知道你的工具箱里有什么以及它
  • 从 DX:AX 寄存器转移到单个 32 位寄存器

    我在添加 16 位乘法的乘积时遇到问题 我想将一年 例如 2015 年 乘以 365 为此 我 mov dx 0 to clear the register mov ax cx cx holds the year such as 2015
  • ICC 中的 -O3 会扰乱内在函数,使用 -O1 或 -O2 或相应的手动汇编即可

    这是后续这个问题 http stackoverflow com questions 49791664 o2 in icc messes up assembler fine with o1 in icc and all optimizatio
  • 比“add esp, 4”更小的指令

    又是我 我的程序中有很多 add esp 4 我正在尝试减小它的大小 是否有任何更小的指令可以替代 add esp 4 pop edx 或者您不介意破坏的任何其他整数寄存器 这就是现代编译器实际上所做的 https stackoverflo
  • 为什么 mov %ax, %ds 汇编+反汇编为 mov %eax,%ds,与原来不一致?

    test S text global start start xor ax ax mov ax ds mov ax ss mov ax es mov ax fs mov ax gs 我通过这样做得到了反汇编代码文件 x86 64 elf g
  • gcc 删除内联汇编代码

    看起来 gcc 4 6 2 删除了它认为函数中未使用的代码 test c int main void goto exit handler asm volatile jmp 0x0 exit return 0 拆解main 0x0804840
  • x86 asm 图形设置的分辨率高于 640x480?

    我刚刚开始使用汇编语言 感觉像学习新东西 并且遇到了一些问题 到目前为止 我一直在浏览的所有教程都没有回答 或者太旧而无法知道 1 我尝试了一些搜索 也许我只是不知道正确的关键字 但我找不到用于更改屏幕分辨率等的图形模式的更新列表 我发现的
  • 如何在汇编中使用 ReadString?

    mov edx offset Prompt1 call WriteString mov ecx 32 mov edx offset String1 call ReadString 现在 我该如何访问String1 如何将其移入寄存器以便对其
  • 在共享库中不使用 PLT 的情况下调用另一个目标文件中的函数?

    我有两个汇编代码 code1 s and code2 s我想从这两个构建一个可重定位 使用 fPIC 开关 共享库 I want code2 s调用一个函数 名为myfun1 其定义在code1 s 当我使用call myfun1 PLT
  • 32位进程在64位操作系统上可以访问多少内存?

    在 Windows 上 正常情况下 32 位进程只能访问 2GB RAM 或通过 boot ini 文件中的特殊开关访问 3GB 在 64 位操作系统上运行 32 位进程时 有多少可用内存 是否有任何特殊的开关或设置可以改变这种情况 默认
  • 微软怎么能说WinAPI中一个字的大小是16位呢?

    我刚刚开始学习WinAPI 在MSDN中 对WORD数据类型提供了以下解释 WORD16 位无符号整数 范围是十进制 0 到 65535 该类型在 WinDef h 中声明如下 typedef 无符号短 WORD 很简单 而且它与我一直在使
  • 如何阅读英特尔操作码符号

    我正在阅读一些引用的材料Intel vol 2 SDM x86 手册 https www intel com content www us en developer articles technical intel sdm html关于汇编

随机推荐