【发布时间】:2020-10-01 11:15:30
【问题描述】:
正如我所见,llvm 支持以 null 结尾的字符串,包括任何字符(0x01 到 0xff)作为 llvm-IR 变量和汇编符号的有效名称。在我看来,这样的决定可能会导致一些问题。
- 当名称包含“特殊”(不可打印)字符时,很难使用文本编辑器(Vim、Kate 等)在 llvm-IR 和汇编程序中编辑程序
- LLvm 和汇编程序支持使用双引号转义,例如
"A B"是一个带有空格字符的名称。在特殊字符的编码中期望类似 printf 的风格是合乎逻辑的。我的意思是"\n"、"\t"、"\xAB",但 llvm-IR 和汇编程序不支持这种名称样式(但 llvm 支持 \KL 用于初始化程序)。
- 一方面
"A\n"产生的不是"A"和newline character,而是对象elf 文件中包含所有3 个字节的名称 - 另一方面,
"A\n"和"A\\n"为 llvm 生成相同的名称
(所以看起来即使是 llvm 也不支持以任何适当的方式进行特殊命名。)
@"A\n" = internal constant i32 1
@"A\\n" = internal constant i32 2
$ clang-9 test.ll -S
test.ll:3:1: error: redefinition of global '@A\n'
@"A\\n" = internal constant i32 2
-
@GOTOFF或@plt是怎么回事?如何区分包括@GOTOFF在内的名称与汇编程序重定位规范?为什么"A B@GOTOFF"可以组装,而"A B"@GOTOFF不行? -
Bug https://sourceware.org/bugzilla/show_bug.cgi?id=18581 于 2015 年开放,但即使是现在,gas 也不支持名称中的某些字符,而 llvm 支持。例如
"A,B"和"A\B"不能用气体组装。 所以llvm创建了汇编方言,不能通过gas进行组装。
编程语言(C/C++、Rust、Go、Python、Java 等)仅支持标识符中的 letters、digits、'_'、'$' 字符。前端也使用 '.'、'$'、'#' 字符,但无论如何它们会生成在汇编程序中有效的名称(没有任何双引号转义)。
可能只有 llvm 优化会生成带有特殊字符的名称。但是这些名称仅为具有内部(C 术语中的静态)链接的全局变量创建。那么为什么不对这些全局变量使用像 "__llvm_internal_global_Id_*" 这样的特殊模式(某些名称在所有情况下都是保留的)?
那么使用这种命名策略的原因是什么?使用一组小而简单的有效字符进行命名会更好吗?
【问题讨论】:
-
这里有一些不好的假设。您对java identifiers 的描述完全错误,到目前为止,我还没有看到任何创建非ascii 标识符的通行证(您称之为优化)。如果煤气不起作用,为什么还要费心呢?现在所有酷孩子都编译为目标代码。
-
嗯。我的措辞可能是错误的。我不知道,但我现在已经在一个 bug 上花费了六个小时,这往往会影响我的行为方式,所以...抱歉。
-
我在大约 3 周的时间里只调试了一个错误(它是来自 Firefox 的 SpiderMonkey JIT 中 mips 解释器的更快版本)。这很“有趣”,所以没问题。现在我不知道哪个 pass(es) 创建了“内部常量全局变量”,但似乎字符串常量的优化创建了一个名称等于值的全局变量。因此,字符串中的所有字符都成为名称的字符。就是这样(我认为是这样)。我在 clang、rust、python3/numba/llvmlite 中看到了这样的情况。
-
基于我阅读的 2003-2004 年提交,这是故意的。这是有道理的,因为 LLVM 的目标之一是简化前端的编写。所以它的标识符允许大字符集并且没有长度限制。还有其他类似的决定:对行长没有限制,对结构中的字段数没有限制。生成的代码经常会产生丑陋的结果,我见过丑陋的名字、丑陋的类型和丑陋的线条。
-
使用少量但简单的有效字符集进行命名会更好吗? - 我希望大多数前端都会这样做,尤其是当 asm 符号名称时来自高级语言标识符。在我看来,LLVM 不会浪费代码对名称进行一些限制,这似乎很正常。对于某些可能的(未来?)前端来说,这会更慢并且可能不方便。相反,实际可用的限制来自 LLVM-IR 文本解析器,以及常识/人类可用性。 (至少这是我对如何明智地看待这种情况的猜测)。
标签: assembly llvm llvm-ir gnu-assembler