【问题标题】:Inline assembly in compiler design编译器设计中的内联汇编
【发布时间】:2023-03-08 02:50:02
【问题描述】:

我正在为自己的类 C 语言 (x86-64) 制作自己的编译器。但我很困惑如何编译另一种语言的 sn-p,即 x86-64 程序集,例如:

int main() {
   __asm {
       mov rcx, rsp
       call func
   }
}

一旦遇到 __asm,它必须以某种方式将标记更改为程序集标记,如果我在 __asm 块之外有一个名为 rcx 的变量怎么办?将其合并到类 C 编译器设计中的好方法是什么?您将如何对其进行标记并以将其与类 C 代码分开的方式对其进行解析? __asm 块将首先在解析器级别上被识别,但如果没有对其进行标记,您将无法达到该级别....

【问题讨论】:

  • 既然您知道这有多么棘手,请重新考虑支持内联汇编 :) 对于超低级别的东西,他们总是可以 link 与在别处定义的汇编函数。
  • 您应该创建一个集成/统一两种语言的语法。如果你不知道这两种语言是什么,你将无法解析。
  • 您可以将汇编代码放在一个字符串中,然后将其传递给汇编器进行解析。
  • 分词器能否将整个区块视为单个分词? prl 关于放入字符串文字的建议具有类似的效果,因为字符串是单个标记。 gcc 会这样做。
  • 由于 MSVC 确实以这种方式实现 asm,您是否调查过当您有本地(或更糟糕的全局)变量时会发生什么情况?或clang -fasm-blocks。我现在有点好奇他们是如何处理那个角落的。在解析级别,我们只知道它以某种方式工作(也许按照 Nate 的建议跳到结尾的 }),但是在实际使用寄存器与 C var 方面发生的事情可能很有趣。

标签: parsing compiler-construction x86-64 inline-assembly language-design


【解决方案1】:

一种选择是做现代 MSVC 所做的事情,并为每条指令提供内在函数,包括像 invlpg 这样的特权指令。 (因为 MSVC 不支持针对 32 位 x86 以外的目标的内联汇编)。这就是 MS 仍然能够使用它来开发 Windows 内核的方式。

但是,如果您不掌握您关心的所有目标 ISA 中的未来指令集扩展,那将无法正常工作。


我真的建议使用GNU C's Extended inline asm syntax,其中操作数约束向编译器描述了 asm 模板字符串。编译器本身根本不需要理解它,只需像printf 寻找%conversion 一样将字符串代入其中。 (见What is the difference between 'asm', '__asm' and '__asm__'?

使用不依赖于 asm 语法的固定语法指定要访问的 C var 名称。此外,asm 在 "" 中作为 C 语法级别的字符串文字,因此块作用域解析看不到 ARM push {r4, lr} 之类的内容。请参阅https://stackoverflow.com/tags/inline-assembly/info 了解更多关于 GNU C 内联汇编如何工作的文档/指南。另请注意,它的模板/操作数约束语法(几乎?)与 GCC 在其机器定义文件中内部使用的语法相同,这些文件教导编译器针对不同目标的可用指令。

这将问题推给程序员编写所有的clobber声明,以告诉编译器call到任意函数可以修改的每个寄存器,假设它遵循标准调用约定。

这还可以让您编写诸如asm("blsi %1, %0" : "=r"(dst) : "r"(src) ) 之类的内容,编译器可以在其中选择实际使用的寄存器。 (仅输出寄存器操作数,仅输入寄存器操作数)。这让编译器可以尽可能高效地围绕黑盒(asm 语句)进行寄存器分配。它可以为输入和输出选择相同的寄存器,也可以不选择,因为源没有使用“早期破坏者”("=&r"),所以它可以假设在写入任何输出之前读取所有输入。

它非常适合包装单个指令,但可用于包装多个指令并访问指向的内存,例如通过"memory"clobber。


您展示的 MSVC 样式语法必须解析块以检测损坏的寄存器,并提及 var 名称。这要困难得多。

现代 clang 确实支持带有命令行选项的 asm{} 块,但它很难有效地使用(就像在 MSVC 中一样);它们不能用寄存器代替变量名,因此输入/输出必须通过内存反弹。

MSVC 不支持除 32 位 x86 之外的目标的 asm 块,可能是因为它们处理 asm{} 的编译器内部结构如此混乱,以至于对于具有寄存器 args 的函数不安全。这使得它无法用于现代调用约定。这不是语法问题,只是编译器技术债务问题。

但是在将数据输入/输出asm{} 块时不可避免的低效率是语法/设计问题。不要犯与 MSVC 相同的错误。 或者,如果您确实想让用户提及 var 名称,请在您的文档中明确说明它们可以被寄存器或内存替换,如果您愿意,请保持该选项处于打开状态认为你可以让它在你的优化后端工作。

【讨论】:

    猜你喜欢
    • 2013-07-23
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    • 2012-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多