Crystal-lang:如果由 Crystal 或 clang 生成,为什么 LLVM "hello.bc" 不一样?
Crystal-lang: why is the LLVM "hello.bc" not the same if generated by Crystal or by clang?
这是我的第一个 Whosebug 问题:-)
我的背景:
- 2年Python经验
- 2 个月 crystal-lang 经验(网站 运行 Amber 框架)
- 1 个月学习 C、C++、汇编
事实:
- crystal-lang 正在编译并且 运行 没有任何问题
- 运行宁 x86_64
请多多关照,我的底层语言知识还不多。
根据我的理解,当我们使用 LLVM 编译和 运行 一个基本的 hello.c 文件时,它如下所示:
hello.c :
#include
int main() {
printf("hello world\n");
return 0;
}
shell :
$ clang -O3 -emit-llvm hello.c -c -o hello.bc
$ llc hello.bc -o hello.s
$ gcc hello.s -o hello.native
$ ./hello.native
这来自 LLVM 示例)
我的观点是,我们可以生成一个非常短的 hello.bc 文件(128 行),可以 运行 使用以下方法以较慢的方式使用:
$ lli hello.bc
但是当我尝试从 hello.cr 文件和 运行 生成类似的 hello.bc 时,就像我对 hello.c 文件所做的那样:
hello.cr :
puts "hello world"
shell :
$ crystal build hello.cr --emit llvm-bc --release
$ llc hello.bc -o hello.s
我注意到了什么:
- 这个 hello.bc 文件比从 c 文件生成的文件大得多(43'624 行)
这个 hello.bc 不能 运行 使用 "lli" 因为它会生成一个:
"LLVM 错误:程序使用了无法解析的外部函数 'pcre_malloc'!
我什至无法从 hello.s 编译到 hello.native
- 同样的问题,如果我尝试使用生成和 hello.ll 文件
据我所知,LLVM 是可移植的,所有前端语言都会生成一个中间 *.bc,然后可以将其编译为任何架构。
我的问题是:
- 为什么 hello.bc 两种情况都不相似?
- 我在 crystal 过程中做错了什么吗?
谢谢!
一切都如其所愿。 Crystal 有一个 运行 时间库,即使您没有包含任何内容,它也始终存在。这是 运行 Crystal 程序所必需的。
C 示例除了对 printf
的系统调用外几乎没有包含任何其他内容。这就是编译后的 ASM 也非常小的原因。
Crystal 简单的 puts
调用背后有更多内容。它基于用于处理异步 IO、并发、信号处理、垃圾收集等的库。其中一些库完全在 Crystal 标准库中实现,一些使用直接嵌入到二进制文件中的其他库 (libgc
) 或仍然需要来自系统的动态库 (libpcre
, libpthread
).
任何 Crystal 程序都默认带有此 运行 时间库。甚至是一个空程序。这通常完全没有引起注意,因为较大的程序无论如何最终都会需要这些东西,并且 运行time 库的编译二进制大小小于 500 KB(在发布模式下)。
像你这样的小程序并不真的需要所有这些来打印一个字符串。但是 Crystal 运行 时间需要这些库。
注意:您可以编译 Crystal 程序 而无需 这些默认库。但这意味着您不能使用 Crystal stdlib 中的 anything 并且您基本上必须使用 Crystal 语法编写 C 代码(或实现您自己的 stdlib):
require "lib_c"
require "c/stdio"
LibC.printf pointerof("hello world".@c)
这可以用 --prelude=empty
选项编译,它将生成一个小得多的 ASM,大致类似于 C 示例。
这是我的第一个 Whosebug 问题:-)
我的背景:
- 2年Python经验
- 2 个月 crystal-lang 经验(网站 运行 Amber 框架)
- 1 个月学习 C、C++、汇编
事实: - crystal-lang 正在编译并且 运行 没有任何问题 - 运行宁 x86_64
请多多关照,我的底层语言知识还不多。
根据我的理解,当我们使用 LLVM 编译和 运行 一个基本的 hello.c 文件时,它如下所示:
hello.c :
#include
int main() {
printf("hello world\n");
return 0;
}
shell :
$ clang -O3 -emit-llvm hello.c -c -o hello.bc
$ llc hello.bc -o hello.s
$ gcc hello.s -o hello.native
$ ./hello.native
这来自 LLVM 示例)
我的观点是,我们可以生成一个非常短的 hello.bc 文件(128 行),可以 运行 使用以下方法以较慢的方式使用:
$ lli hello.bc
但是当我尝试从 hello.cr 文件和 运行 生成类似的 hello.bc 时,就像我对 hello.c 文件所做的那样:
hello.cr :
puts "hello world"
shell :
$ crystal build hello.cr --emit llvm-bc --release
$ llc hello.bc -o hello.s
我注意到了什么:
- 这个 hello.bc 文件比从 c 文件生成的文件大得多(43'624 行)
这个 hello.bc 不能 运行 使用 "lli" 因为它会生成一个:
"LLVM 错误:程序使用了无法解析的外部函数 'pcre_malloc'!
我什至无法从 hello.s 编译到 hello.native
- 同样的问题,如果我尝试使用生成和 hello.ll 文件
据我所知,LLVM 是可移植的,所有前端语言都会生成一个中间 *.bc,然后可以将其编译为任何架构。
我的问题是:
- 为什么 hello.bc 两种情况都不相似?
- 我在 crystal 过程中做错了什么吗?
谢谢!
一切都如其所愿。 Crystal 有一个 运行 时间库,即使您没有包含任何内容,它也始终存在。这是 运行 Crystal 程序所必需的。
C 示例除了对 printf
的系统调用外几乎没有包含任何其他内容。这就是编译后的 ASM 也非常小的原因。
Crystal 简单的 puts
调用背后有更多内容。它基于用于处理异步 IO、并发、信号处理、垃圾收集等的库。其中一些库完全在 Crystal 标准库中实现,一些使用直接嵌入到二进制文件中的其他库 (libgc
) 或仍然需要来自系统的动态库 (libpcre
, libpthread
).
任何 Crystal 程序都默认带有此 运行 时间库。甚至是一个空程序。这通常完全没有引起注意,因为较大的程序无论如何最终都会需要这些东西,并且 运行time 库的编译二进制大小小于 500 KB(在发布模式下)。 像你这样的小程序并不真的需要所有这些来打印一个字符串。但是 Crystal 运行 时间需要这些库。
注意:您可以编译 Crystal 程序 而无需 这些默认库。但这意味着您不能使用 Crystal stdlib 中的 anything 并且您基本上必须使用 Crystal 语法编写 C 代码(或实现您自己的 stdlib):
require "lib_c"
require "c/stdio"
LibC.printf pointerof("hello world".@c)
这可以用 --prelude=empty
选项编译,它将生成一个小得多的 ASM,大致类似于 C 示例。