Crystal-lang: 为什么由Crystal或clang生成的LLVM“hello.bc”不同?

5

这是我的第一个Stackoverflow问题 :-)

我的背景:

  • 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行)
  • 无法使用 "lli" 运行此 hello.bc,因为它会生成以下错误:

    "LLVM ERROR: Program used external function 'pcre_malloc' which could not be resolved!

  • 即使我尝试使用 hello.s 生成 hello.native 文件也遇到相同的问题

  • 如果我尝试使用 generate 和 hello.ll 文件,也会出现相同的问题

据我理解,LLVM是可移植的,并且所有前端语言都会产生一个中间文件 *.bc,然后可以编译成任何架构。

我的问题是:

  • 为什么两种情况下的 hello.bc 不相似?
  • 我在 crystal 过程中做错了什么吗?

谢谢!

1个回答

6
一切都如预期一样。即使您没有包含任何内容,Crystal也有一个始终存在的运行时库。这是运行Crystal程序所必需的。
C示例几乎没有包含除了对printf系统调用之外的任何内容。这就是为什么编译后的ASM非常小的原因。
Crystal的简单puts调用背后有更多的东西。它基于用于处理异步IO、并发、信号处理、垃圾回收等的库。其中一些库完全实现在Crystal标准库中,一些使用其他库,这些库要么直接嵌入到二进制文件中(libgc),要么仍需要来自系统的动态库(libpcre、libpthread)。
任何Crystal程序默认都带有此运行时库,即使是空程序。这通常完全不会被注意到,因为较大的程序最终也需要这些东西,并且运行时库的编译二进制文件大小在发布模式下小于500 KB。
像您这样的小程序实际上并不需要所有这些东西来打印字符串。但是这些库对于Crystal运行时是必需的。
注意:您可以编译不使用这些默认库的Crystal程序。但这意味着您无法使用任何Crystal stdlib中的内容,而必须使用Crystal语法编写C代码(或实现自己的stdlib)。
require "lib_c"
require "c/stdio"

LibC.printf pointerof("hello world".@c)

这可以使用 --prelude=empty 选项进行编译,它将生成一个相对较小的ASM文件,大致类似于C示例。

了解如何使用“lli”启动IR并绕过关于“pcre_malloc”的错误仍然是有趣的。也许您也知道答案。 - Oleh Prypin
我对LLVM工具一无所知。但是你可能需要告诉LLVM解释器要使用哪些动态库(使用-load选项)。 - Johannes Müller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接