【问题标题】:Are "functions" a fundamental part of the x86 hardware?“功能”是 x86 硬件的基本组成部分吗?
【发布时间】:2013-06-27 23:04:01
【问题描述】:

在流行的 x86 架构上,可以使用jmp 类型指令之一以及call 指令来控制执行流程。但两者都是基本的,还是一种“语法糖”是另一种?

例如,push 指令是语法糖:push eax 等价于mov [esp], eax,后跟sub esp, 4。所以mov是唯一的基本数据操作。

但是call 也是这样吗?是否有其他一些指令可以达到相同的效果?

在伪代码中,如果我可以访问 指令指针:

bar:
    push eip    ;; pseudo-code!              ;; call foo
    jmp foo

foo:
    pop eax                                  ;; ret
    jmp [eax]

这不是有效的 x86,因为不能直接读取指令指针寄存器 eip。到目前为止,我看到的所有获得eip 值的“技巧”都使用call。或者call 实际上是在 x86 上读取指令指针的规范方式?作为一个额外的问题,是否每个可以实现 C 的架构都需要一种读取指令指针的方法,以便可以实现函数指针?

int main(int argc, char * argv[])
{
    int (*p)() = strtoul(argv[1]);

    return p();                     // how to implement without
                                    // accessing the instruction pointer?
}

[这不是一个深奥的问题;知道函数不仅是高级编程语言引入的抽象,而且实际上是硬件固有的,我只是觉得在情感上令人满意。]

【问题讨论】:

  • push 指令不是语法糖——是的,它与更长的指令序列具有相同的净效果,但执行所需的时间不同。实际涉及到不同的指令位序列。语法糖一般是指会生成完全相同的机器码位模式的东西,比如nop指令,和xchg eax,eax是一样的
  • @ChrisDodd:非常有趣,谢谢!随意在答案中扩展它!
  • 顺便说一句,lea reg, [rip] 在 64 位模式下。实际上push eax 通常不是作为mov [esp], eax \ sup esp, 4 实现的,而是通过特殊的技巧使 esp 调整不需要时间(但是,当您以正常方式使用esp 时,它确实需要一个特殊的重新同步操作)。通常无论如何。
  • @harold:是的,我注意到 x86_64 似乎允许这样做——为什么那是合法的?这是一个奇怪的小变化......
  • Only one instruction is fundamental。任何计算都可以使用一个减法和分支如果负指令来实现。

标签: c assembly x86


【解决方案1】:

机器指令集可能很简单;见Wolfram's 2-state, 3 symbol Turing machine. 问题是这样的机器很难编程,也很难快速运行。

你可以用其他指令完全模拟调用返回:

    ;  Simulate "call abc":
    push    offset next_location
    jmp     abc
 next_location:

    ; Simulate "ret"
    pop    eax
    jmp    eax

您显然可以使用 mov 指令模拟推送/弹出,并通过添加您在问题中提到的常量来调整寄存器。

人们将指令添加到指令集中以提高通用序列的效率。因此,“调用”的存在是因为它执行了一项通常有用的任务(并且“调用/调用”对可以通过硬件使用返回地址的内部堆栈进行优化,以避免指令流水线中断;这是真正的性能优势)。

所以,是的,您可以使用计算机提供的最小指令集来实现您的汇编语言目的。但是正确的附加说明在实践中确实很有帮助。 (您可以善意地添加您认为可能有用的说明。它们通常无济于事,这是source of the original RISC/CISC debate)。

Goofball 最小机器:

  • 1970年代生产的真正的小型机(“GRI”)只有一条指令:MOV LOC1 to LOC2;目标位置很神奇,这就是实际工作的完成方式。通过将某些东西移动到位置 33(组成),一个副作用是该值被添加到可以从位置 32 读取的累加器中。它有很多位置可以做有趣、方便的事情,所以本质上添加了一个新的有趣寄存器只是添加有用指令的奇怪变体。我怀疑 JMP 指令包括将常量移动到充当程序计数器的位置。 (如果您觉得这很奇怪,那么您还没有遇到过 PDP-11)。

  • 理论机器是具有单指令“SUB LOC1 from LOC2 jmp to LOC3 ifnegative”的机器。这台机器是通用的,但在您遇到第一个玩具问题后编程并不好玩。

有趣的是,这两个指令集都没有操作码位或寻址模式位。指令解码器确实很简单。

【讨论】:

  • 顺便说一句,似乎有“通用序列”的说明enter/leave,但GCC似乎没有使用它们。 (顺便说一句,我对理论机器有点熟悉。好点。)
  • "enter"/"leave" 原来是那些“哎呀,这似乎很有用”之一,但在实践中却没有那么有用。一个无用 CISC 的完美示例,但现在永久内置于英特尔指令集中,永久使用一点额外的力量。
  • 不使用进入/离开(由 GCC)的一个原因是它们支持具有嵌套词法范围的语言.. C 和 C++ 不这样做,IIRC。这本身并不会使它们变坏。我的公司构建了一个确实具有词法作用域的编译器,这些指令最初看起来很有吸引力。事实证明,它们并没有完全完全完成我们的编译器需要的操作,因此我们生成了足够快的其他内容。因此,它们无法在需要的情况下发挥作用;这是复杂指令的常见故障,也是不添加它们的原因。
  • 进入/离开在当前处理器中也相当慢。过去有时有时会使用它们,但现在它们并不是非常有用(但必须保留以保持兼容性)。
  • @BrianKnoblauch:它们是否比等效的汇编指令慢?如果是这样,它们已经退化了。我希望它们受到内存访问的限制,因为 enter 是关于复制(密集)显示(指向外部词法范围块的指针数组)。我们不使用它们的一个原因是,实际上,您不必为许多词法范围的过程复制整个显示,并且您不复制的内容减少了函数调用开销。
猜你喜欢
  • 1970-01-01
  • 2010-11-30
  • 2020-09-28
  • 2020-12-10
  • 1970-01-01
  • 2018-07-16
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多