获取比较指令的值

2024-04-25

据我了解, cmp 指令将设置标志寄存器中的一些位。然后,您可以使用 jle、jnp 等指令基于这些指令进行分支。

我想知道如何从比较中恢复整数值。

示例:以下是有效的 c 语法

y = x[a >= 13];

因此,a 与 13 进行比较,得到 true 或 false,分别解释为 1 或 0。但是,1 或 0 必须作为整数输入到数组访问中。编译器会做什么?

我能想到的一些事情是:

进行比较,然后分支到 x[0] 或 x[1]

进行比较,然后分支执行 tmp = 0 或 tmp = 1,然后执行 x[tmp]

也许对标志做一些奇特的逻辑(不确定是否有直接访问标志的指令)

我试图查看 gcc 为该代码示例吐出的内容,但不可能从它抛出的所有额外垃圾中找出逻辑。

我正在开发一个编译器,因此任何建议将不胜感激。


基本上可以通过三种方法来完成此操作。我会一次一个地回顾它们。

一种方法基本上就是您在问题中所描述的:进行比较,然后分支到分别实现两种可能性的代码。例如:

    cmp  [a], 13                     ; compare 'a' to 13, setting flags like subtraction
    jge  GreaterThanOrEqual          ; jump if 'a' >= 13, otherwise fall through

    mov  eax, [x * 0 * sizeof(x[0])] ; EAX = x[0]
    jmp  Next                        ; EAX now loaded with value, so do unconditional jump

GreaterThanOrEqual:
    mov  eax, [x * 1 * sizeof(x[0])] ; EAX = x[1]
                                     ; EAX now loaded with value; fall through

Next:
    mov  [y], eax                    ; store value of EAX in 'y'

通常,编译器会尝试在寄存器中保留更多值,但这应该让您了解基本逻辑。它进行比较,然后分支到读取/加载的指令x[1]或落入读取/加载的指令x[0]。然后,它转移到一条指令,将该值存储到y.

您应该能够看到,由于需要所有分支,因此效率相对较低。因此,优化编译器不会生成这样的代码,尤其在简单的情况下,你有一个基本的三元表达式:

(a >= 13) ? 1 : 0

or even:

(a >= 13) ? 125 : -8

有一些位操作技巧可用于进行此比较并获取相应的整数without曾经不得不做一个分支。

这给我们带来了第二种方法,即使用SETcc操作说明。这cc部分代表“条件代码”,所有条件代码与条件跳转指令的条件代码相同。 (事实上​​,您可以将所有条件跳转指令写为Jcc。) 例如,jge表示“大于或等于则跳转”;相似地,setge意思是“如果大于或等于则设置”。简单的。

诀窍是关于SETcc是它设置了一个字节大小的寄存器,这基本上意味着AL, CL, DL, or BL(还有更多选择;您could设置这些寄存器之一的高字节,和/或在 64 位长模式下有更多选项,但这些是操作数的基本选择)。

以下是实现此策略的代码示例:

xor   edx, edx        ; clear EDX
cmp   [a], 13         ; compare 'a' to 13, setting flags like subtraction
setge dl              ; set DL to 1 if greater-than-or-equal, or 0 otherwise
mov   eax, [x * edx * sizeof(x[0])]
mov   [y], eax

很酷,对吧?支行被淘汰。需要的0或1直接加载到DL,然后将其用作负载的一部分(MOV操作说明)。

这里唯一有点令人困惑的是你需要知道DL是全32位的低字节EDX登记。这就是为什么我们需要预先清除完整的EDX, since setge dl只影响低字节,但我们想要完整的EDX为 0 或 1。事实证明,对整个寄存器进行预清零是在所有处理器上执行此操作的最佳方法 https://stackoverflow.com/questions/33666617/what-is-the-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and,但还有其他方法,例如使用MOVZX after the SETcc操作说明。链接的答案对此进行了非常详细的说明,因此我不会在这里进行详细说明。关键点就在于SETcc仅设置寄存器的低字节,但后续指令需要entire32位寄存器要有值,所以需要消除高字节中的垃圾。

不管怎样,当你编写类似的代码时,编译器 99% 的情况下都会生成这样的代码y = x[a >= 13]. The SETcc指令为您提供了一种根据一个或多个标志的状态设置字节的方法,就像您可以在标志上进行分支一样。这基本上就是您所想到的允许直接访问标志的指令。

这实现了以下逻辑

(a >= 13) ? 1 : 0

但如果你想做怎么办

(a >= 13) ? 125 : -8

就像我之前提到的?嗯,你还是用SETcc指令,但之后您会进行一些花哨的位调整,以将结果 0 或 1 “修复”为您实际想要的值。例如:

xor   edx, edx
cmp   [a], 13
setge dl
dec   edx
and   dl, 123
add   edx, 125
; do whatever with EDX

这适用于几乎任何二进制选择(两个可能的值,取决于条件),并且优化编译器足够聪明来解决这个问题。仍然是无分支代码;很酷。

还有第三种方法可以实现这一点,但它在概念上与我们刚才讨论的第二种方法非常相似。它使用一个有条件的移动指令,这只是基于标志状态进行无分支设置的另一种方法。条件移动指令是CMOVcc, where cc再次引用“条件代码”,与前面的示例完全相同。这CMOVcc该指令大约在 1995 年随 Pentium Pro 引入,此后一直存在于所有处理器中(不是 Pentium MMX,而是 Pentium II 及更高版本),因此基本上是您今天看到的所有处理器。

代码非常相似,只是顾名思义,它是一个条件移动,因此需要更多的初步设置。具体来说,您需要将候选值加载到寄存器中,以便您可以选择正确的值:

xor    edx, edx    ; EDX = 0
mov    eax, 1      ; EAX = 1
cmp    [a], 13     ; compare 'a' to 13 and set flags
cmovge edx, eax    ; EDX = (a >= 13 ? EAX : EDX)
mov    eax, [x * edx * sizeof(x[0])]
mov    [y], eax

请注意,移动EAX into EDX is 有条件的- 仅当标志指示条件时才会发生ge(大于或等于)。因此,它可以计算出基本的 C 三元运算符,如指令右侧的注释中所述。如果标志表明ge, then EAX被移入EDX。否则,什么都不会移动,并且EDX保持其原始值。

请注意,尽管某些编译器(特别是英特尔的编译器,称为 ICC)更喜欢CMOV指示结束SET指令,这与我们之前看到的之前的实现相比没有任何优势SETGE。事实上,这确实是次优的。

When CMOV真正有用的是允许您消除获取除旧的 0 或 1 以外的值所需的位操作代码。例如:

mov    edx, -8     ; EDX = -8
mov    eax, 125    ; EAX = 125
cmp    [a], 13     ; compare 'a' to 13 and set flags
cmovge edx, eax    ; EDX = (a >= 13 ? EAX : EDX)
; do whatever with EDX

现在指令更少了,因为正确的值被直接移入EDX寄存器,而不是设置 0 或 1,然后必须将其操作为我们想要的值。因此编译器将使用CMOV指令(当针对支持它们的处理器时,如前所述)来实现更复杂的逻辑,例如

(a >= 13) ? 125 : -8

即使他们could使用其他方法之一来完成它们。你也need当条件两侧的操作数不是编译时常量(即它们是寄存器中的值,仅在运行时已知)时,条件移动。

这有帮助吗? :-)

我试图查看 gcc 为该代码示例吐出的内容,但不可能从它抛出的所有额外垃圾中找出逻辑。

是的。我有一些提示给你:

  1. 将您的代码缩减为很简单功能是only做你想学习的事情。您需要将输入作为参数(以便优化器不能简单地折叠常量),并且您需要返回函数的输出。例如:

    int Foo(int a)
    {
        return a >= 13;
    }
    

    返回一个bool也会在这里工作。如果您使用条件运算符返回 0 或 1 以外的值,则需要返回int, 当然。

    不管怎样,现在你可以看到exactly编译器生成什么汇编指令来实现这一点,没有任何其他噪音。确保您已启用优化;查看调试代码并没有什么指导意义very noisy.

  2. 确保您要求 GCC 使用 Intel/MASM 格式生成汇编列表,即much比它的默认格式 GAS/AT&T 语法更容易阅读(至少在我看来)。上面的所有汇编代码示例都是使用 Intel 语法编写的。所需的咒语是:

    gcc -S -masm=intel MyFile.c
    

    where -S生成输入源代码文件的汇编列表,并且-masm=intel将程序集列表语法格式切换为 Intel 样式。

  3. 使用像这样的好工具Godbolt 编译器资源管理器 https://gcc.godbolt.org,它可以自动执行所有这些操作,从而大大缩短周转时间。另一个好处是,它对汇编指令进行颜色编码,以与原始源代码中的 C 代码行相匹配。

    这是您用来研究此内容的示例 https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,source:'bool+Boolean(int+a)%0A%7B%0A++++return+a+%3E%3D+13%3B%0A%7D%0A%0Aint+OneOrZero(int+a)%0A%7B%0A++++return+a+%3E%3D+13%3B%0A%7D%0A%0Aint+ComplexTernary(int+a)%0A%7B%0A++++return+a+%3E%3D+13+%3F+125+:+-8%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),header:(),k:25.109860278204515,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:g71,filters:(___0:(),b:'0',commentOnly:'0',directives:'0',intel:'0',jquery:'3.2.1',length:1,prevObject:(___0:(),length:1,prevObject:(___0:(jQuery3210076433803653344511:(display:'')),length:1))),options:'-m32+-O3+-march%3Dhaswell',source:1),l:'5',n:'0',o:'x86-64+gcc+7.1+(Editor+%231,+Compiler+%231)',t:'0')),header:(),k:32.88792518846509,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:g71,filters:(___0:(),b:'0',commentOnly:'0',directives:'0',intel:'0',jquery:'3.2.1',length:1,prevObject:(___0:(),length:1,prevObject:(___0:(jQuery3210076433803653344511:(display:'')),length:1))),options:'-m32+-O3+-march%3Dpentium',source:1),l:'5',n:'0',o:'x86-64+gcc+7.1+(Editor+%231,+Compiler+%232)',t:'0')),k:42.00221453333042,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4。原始来源位于最左侧。中间窗格显示了现代处理器的 GCC 7.1 汇编输出,它支持CMOV指示。最右边的窗格显示了 GCC 7.1 对于一个非常旧的处理器的汇编输出,它确实not支持CMOV指示。很酷,对吧?您可以轻松操作编译器开关并观察输出如何变化。例如,如果你这样做-m64(64 位)而不是-m32(32位),然后你会看到参数是在寄存器中传递的(EDI),而不是在堆栈上传递并必须作为函数中的第一条指令加载到寄存器中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

获取比较指令的值 的相关文章

  • 比较 2 个单独的 csv 文件并将差异写入新的 csv 文件 - Python 2.7

    我正在尝试比较 python 中的两个 csv 文件 并将差异保存到 python 2 7 中的第三个 csv 文件中 import csv f1 open olddata file1 csv oldFile1 csv reader f1
  • SSE加载和添加

    假设我有两个由两个类型数组表示的向量double 每个尺寸为2 我想添加相应的位置 所以假设向量i0 and i1 我想补充一下i0 0 i1 0 and i0 1 i1 1 一起 由于类型是double 我需要两个寄存器 诀窍是把i0 0
  • 计算 Amd Zen 2 处理器上的 L3 缓存访问事件

    我试图找出与 perf stat 命令一起使用的事件来计算 AMD Zen 2 处理器上的 L3 缓存访问次数 根据 PPR http developer amd com wordpress media 2017 11 54945 PPR
  • 比较堆栈中的两个值? [复制]

    这个问题在这里已经有答案了 我卡住了 在我的汇编代码中 我想比较两个值 堆 x86 语法 AT T cmpl 4 ebp 4 ebp 错误 cmp 的内存引用太多 我认为不可能根据乘数和 ebp 来比较两个值 有什么建议 您可以使用 CMP
  • 链接描述文件未按预期跳过字节

    因此 我有这个汇编文件 我使用 GNU as 进行汇编 并使用链接器脚本与 GNU ld 进行链接 链接描述文件 boot ld INPUT boot o OUTPUT boot out ENTRY boot start SECTIONS
  • 比较两个不同的音频文件不起作用

    我想比较两个音频文件 例如 mp3 和 wav 我用musicg https code google com p musicg 通过指纹进行比较 Wave record1 new Wave music1 toString Wave reco
  • x86 汇编中 cmove 指令的用途?

    反汇编可执行文件时我遇到了cmove操作说明 我已经在互联网上搜索过 但我只发现这是一个有条件的移动 如果源和目的地相等mov发生 我还不明白为什么我需要它 因为它不会改变操作数 它的目的是什么 The CMOVcc指令不比较源和目标 它们
  • Pandas 将值与前一行与过滤条件进行比较

    我有一个包含员工工资信息的数据框 大约有 900000 多行 Sample table num name salary 0 001234 John Johnson 1200 1 001234 John Johnson 1000 2 0012
  • 使用 xslt 比较两个 xml 文件?

    我有 2 个 xml 文件 如何使用 xslt 比较两个文件是否相等 如果不等于意味着第二个 xml 中发生了更改 在 XPath 2 0 中你可以简单地使用fn deep equal http www w3 org TR 2005 CR
  • 如何在汇编程序中使用 C 库?

    我想知道如何用汇编语言编写文本编辑器 但现代操作系统需要 C 库 特别是对于它们的窗口系统 我找到了这个page http pengu1n is programmer com posts 8304 html 这对我有很大帮助 但我想知道是否
  • 分配内存空间的宏

    我需要让一个汇编程序员来计算帕斯卡三角形 https en wikipedia org wiki Pascal 27s triangle 这样帕斯卡三角形的每一行都与其他行分开存储在内存中 我想做一个 但我不知道如何使用宏在汇编中做到这一点
  • 编写 AMD64 SysV 程序集时使用哪些寄存器作为临时寄存器?

    我正在使用实现一个功能cpuid根据 AMD64 SysV ABI 进行组装 我需要在函数本身中使用 2 个临时寄存器 第一个用于累积返回值 第二个用作计数器 我的功能目前如下所示 zero argument function some c
  • 预取双类成员需要转换为 char*?

    我有一个正在使用的课程 mm prefetch 预先请求包含 double 类型的类成员的缓存行 class MyClass double getDouble return dbl other members double dbl othe
  • 这段汇编语言代码是什么意思?

    我是一名学生 刚刚开始学习汇编语言 为了更好地理解它 我只是用 C 写了一个简短的代码并将其转换为汇编语言 奇怪的是我有点听不懂 代码是 include
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • 内联 asm 中不支持的指令“mov”将控制寄存器移动到 uint32_t

    我在 C 函数中使用汇编代码 但海湾合作委员会给出unsupported instruction mov 以下代码的错误 uint32 t faulting address asm volatile mov cr2 0 r faulting
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • 在 x86 ASM 中测试零通常哪个更快:“TEST EAX, EAX”与“TEST AL, AL”?

    测试 AL 中的字节是否为零 非零通常哪个更快 TEST EAX EAX TEST AL AL 假设之前有一个 MOVZX EAX BYTE PTR ESP 4 指令加载了一个带有零扩展的字节参数到 EAX 的其余部分 防止了我已经知道的组
  • mysql 如何比较 dd-mon-yy 格式的日期

    如何比较格式中的日期dd mon yy 例如 2014 年 11 月 10 日 gt 2013 年 10 月 7 日 select expiration date from grocery where expiration date lt
  • 用于预乘 ARGB 的 SSE alpha 混合

    我正在尝试编写一个支持 SSE 的 alpha 合成器 这就是我想出的 首先 混合两个 4 像素向量的代码 alpha blend two 128 bit 16 byte SSE vectors containing 4 pre multi

随机推荐

  • 如何检测Vista UAC是否启用?

    我需要我的应用程序根据是否启用 Vista UAC 来表现不同 我的应用程序如何检测用户计算机上的 UAC 状态 该注册表项应该告诉您 HKLM SOFTWARE Microsoft Windows CurrentVersion Polic
  • 定制 iPhone 键盘

    我需要 即客户要求 提供自定义键盘 供用户在文本字段和区域中输入文本 我已经有一些可以执行键盘操作并将测试附加到文本字段的东西 但是我想让它更通用并让它像标准的 iphone 键盘一样工作 即当用户选择可编辑文本控件时出现 目前我的控制器知
  • 使用 BrokeredMessage 从 Azure 服务总线队列 (v1) 反序列化强类型对象

    无论出于何种原因 我似乎无法弄清楚如何将我的对象从队列中取出并将其反序列化回它放入其中的内容 An账户事件DTO Azure函数成功将对象放入队列 FunctionName AccountCreatedHook public static
  • 从groupby中的列获取模式[重复]

    这个问题在这里已经有答案了 我试图获取 groupby 对象中列的模式 但出现此错误 incompatible index of inserted column with frame index 这是我遇到的问题 我不知道如何解决它 任何帮
  • 哪个类应该存储查找表? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 世界上有不同地点的代理 但任何地点都只有一个代理 每个特工都知道他在哪里 但我还需要快速检查给定位置是否有特工 因此 我还维护了一张从位置到代理
  • 对角度数据表中括号内的数字进行排序

    我在用角度数据表 http l lin github io angular datatables 根据网络服务响应填充我的表 我的网络服务返回一个如下所示的 json id 1 name abc count 20 id 2 name abc
  • 计算和合并行

    我有一个表 通过将国家 地区代码添加到每行的 国家 地区代码 列来记录 Web 应用程序中的用户位置 每行代表对特定区域的访问 所以我有一些数据 比如 COL1 COL2 COL3 countrycode asd asd asd NZ as
  • 使用 Kafka Streams 在输出中设置时间戳无法进行转换

    假设我们有一个变压器 用 Scala 编写 new Transformer String V String V var context ProcessorContext override def init context Processor
  • 如何在弹性搜索(aws)中存储日期范围数据并搜索范围?

    我正在尝试在弹性搜索中存储酒店房间可用性 然后我需要 搜索从某个日期到另一个日期可用的房间 我想出了 存储数据以确保可用性的两种方式 如下 这里可用性字典存储了所有日期 每个日期键的值是 true 或 false 代表其可用 那天与否 id
  • R:有没有一种简单有效的方法来获取分块对角矩阵的构建分块矩阵列表?

    我正在寻找一个 内置 函数 它通过以下方式有效地返回块对角矩阵的构建块列表 而不是迭代插槽以手动获取列表 construct bdiag matrix library Matrix listElems lt list matrix 1 4
  • 生成 Java JNI 标头

    我想做的就是使用 JDK Javah exe 程序生成 JNI 头文件 但无论我尝试什么 我都会收到错误消息 错误 找不到 ddg ndkTest NativeLib 的类文件 其中 ddg ndkTest 是命名空间 NativeLib
  • AWS S3 StringLike条件阻止对存储桶的请求

    我有以下 s3 IAM 政策 它的目的是允许我从存储桶内的 temp prod tests 位置复制文件或将文件放入下面的存储桶中 在策略中 我添加了 StringLike 条件 我希望该条件允许策略中的权限允许在对象前缀包含时进行复制和放
  • 使用多个参数多次调用傀儡定义的资源

    我有一个简单的木偶定义的资源 如下所示 define mything number device otherthing file place number ensure gt directory mount place number dev
  • 使用vuex时如何在typescript语法中使用mapState函数?

    我在与 vuex 集成的 vuejs 项目中使用 typescript 语法 我想使用 ts 文件中计算的 mapState 方法 但出现语法错误 目前我正在使用文档建议的计算函数语法 我的意思是 get counter return th
  • 为什么这段Java代码会触发ConcurrentModificationException?

    在循环的第一行中 我收到错误 但我不明白为什么 根据我的阅读 只有当我迭代一个集合并尝试同时修改它时 才会发生这种情况 但事实并非如此 在代码中 list属于类型ArrayList
  • 在 Spring 中,为什么 POJO 上的 CGLib 的行号会丢失?

    Spring MVC Web 应用程序 我有一个不带行号的堆栈跟踪 显示在底部 我认为这是由于 CGLib 在控制器上运行所致 但这对我来说很奇怪 实际的异常发生在ServerBatchRemoteRequestAcceptor 一个没有注
  • 使用 RCurl 登录 WordPress

    我想使用 R 的 RCurl 包登录 WordPress 网站 以便安装 WordPress 插件 可能在 WordPress 的某些选项页面上使用 postForm 由于该网站受密码保护 我请求您帮助如何验证我的 R 会话 我发现以下三个
  • AND 运算符不能与布尔值和字符串一起使用

    我有一个 LINQ2SQL 语句 其中使用两个条件 var query1 from r in dt Test where r ID 92 r Status I select r ID r Status I 但它给了我一个错误 即 AND 运
  • UITableView 具有不同的可选部分?

    我正在寻找一种 好 的方法来解决一些特殊要求 我有一个包含不同部分的 UITableView 例如 基础数据 关于我 兴趣 Images 基本数据始终包含值 但仍然存在可变行数 所有其他 类别 可以包含行 也可以为空 如果没有数据 则不应显
  • 获取比较指令的值

    据我了解 cmp 指令将设置标志寄存器中的一些位 然后 您可以使用 jle jnp 等指令基于这些指令进行分支 我想知道如何从比较中恢复整数值 示例 以下是有效的 c 语法 y x a gt 13 因此 a 与 13 进行比较 得到 tru