为x86处理器生成程序集

时间:2010-03-01 23:53:32

标签: compiler-construction assembly code-generation x86

我目前正在通过安德鲁·阿佩尔的Java现代编译器实现工作,我正处于构建低级中间表示的地步。

最初,我决定瞄准JVM并忽略所有低级机器的东西,但为了学习我不太了解的事情,我已经改变了主意。这会改变我的IR,因为定位JVM允许我(或多或少)挥手示意调用或构建对象。

Appel书没有详细介绍任何特定的机器架构,所以我想知道在哪里可以找到我需要知道的更远的东西。

我现在知道的事情我需要知道:

  • 使用哪个指令集。我有两台可以开发的笔记本电脑;两者都有Core 2 Duo处理器。我目前的理解是x86处理器大多使用相同的指令集,但它们并非完全相同。

  • 操作系统是否影响编译的代码生成步骤,或者它是否完全依赖于处理器。例如,我知道生成在32位与64位平台上运行的代码有所不同。

  • 如何组织堆栈帧等。何时使用寄存器与在堆栈上放置参数,调用者保存与被调用者保存,所有这些。我曾经想过这将与指令集一起描述,但到目前为止我还没有在任何地方看到过这个特定的信息。也许我在这里误解了什么?

完全欢迎链接到资源以代替答案。

3 个答案:

答案 0 :(得分:5)

大多数x86指令集对所有处理器都是通用的 - 这是一个相当安全的选择,你的处理器都有相同的指令集,除了可能的SIMD指令,在实现一个简单的操作时可能对你没用编译器(这些指令通常用于使多媒体应用程序等更快)。指令集列在Intel's manuals - 2A和2B中,特别是有完整的说明及其行为列表,但其他卷值得一看。

在生成用户空间代码时,操作系统的选择在系统调用方面很重要。例如,如果您希望程序在64位Linux上向终端输出内容,则需要通过以下方式进行系统调用:

  • 将值1加载到注册表rax中,表示这是write系统调用。
  • 将值1加载到寄存器rdi中以指示应使用stdout(1是stdout的文件描述符)
  • 将您要打印的内容的起始地址加载到注册rsi
  • 将要打印的内容的长度加载到注册rdx
  • 一旦设置了寄存器(和存储器),就执行syscall指令。

write的返回值存储在rax

不同的操作系统可能具有write的不同系统调用号,可能有不同的传递参数的方式(x86-64 Linux系统调用始终使用rdirsi参数的顺序为rdxr10r8r9,系统调用号为rax),可能有不同的系统调用共

Linux上普通函数调用的约定类似 - 寄存器的顺序为rdirsirdxrcxr8,和r9(所有相同,除了使用rcx而不是r10),在堆栈上有更多参数和rax中的返回值。根据{{​​3}},应在函数调用之间保留注册rbprbxr12r15。当然,你可以自由地编写自己的约定(除非进行系统调用),但是这使得调用从其他人生成或编写的代码调用变得更加困难。

答案 1 :(得分:3)

  

如何堆叠帧等   举办。何时使用寄存器vs.   将参数放在堆栈上,   所有的调用者保存与被调用者保存   那。我想过会这样   与...一起描述   指令集但到目前为止我还没有   在任何地方看到这个特殊信   也许我误会了什么   这里吗?

一般来说,这些问题没有正确答案。你可以使用你想要的任何调用约定......除非你想与其他人的代码互操作。对于互操作性,编译器对应用程序二进制接口进行标准化。我的理解是Itanium C++ ABI近年来已成为一种流行的标准。尝试从那里开始。

答案 2 :(得分:1)

我不能回答你们所有的问题;但

  • 基本的x86指令集是 兼容x86系列 处理器。你不打算 实施任何特定扩展, 你呢?
  • 我不认为你的操作系统 或架构对代码很重要 代
  • 默认答案 任何编译器相关的是 Dragon book。你看过了吗? 它呢?