gpt4 book ai didi

c++ - LLVM JIT 教程代码因简单的参数化函数而崩溃。为什么?

转载 作者:可可西里 更新时间:2023-11-01 17:56:14 25 4
gpt4 key购买 nike

我正在尝试了解 LLVM 基础架构。我已经在 MinGW 安装上安装了适用于 Windows 的 LLVM 二进制文件。

我正在学习在 LLVM 网站上找到的关于所谓的 Kaleidoscope 语言的教程。我有一个源文件 完全 代码 list at the end of this page .

此外,如果它有任何重要性,我将使用以下标志进行构建(提前通过 llvm-config 获得,因为 Windows shell 没有非常舒适的替换语法):

clang++ -g -O3 kaleido.cpp -o kaleido.exe -IC:/MinGW/include -DNDEBUG -D__NO_CTYPE_INLINE -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -LC:/MinGW/lib -lLLVMCore -lLLVMSupport - lpthread -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMMCParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMJIT -lLLVMRuntimeDyld -lLLVMExecutionEngine -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMTarget -lLLVMMC -lLLVMObject -lLLVMCore -lLLVMSupport - lm -limagehlp -lpsapi

使用在链接代码中实现的建议语言,我正在测试一些顶级表达式。首先,一个有文字的:

ready> 5 + 3;
ready> Read top-level expression:
define double @0() {
entry:
ret double 8.000000e+00
}

Evaluated to 8.000000

...按预期工作。然后是具有常量结果的函数定义:

ready> def f(x) 12;
ready> Read function definition:
define double @f(double %x) {
entry:
ret double 1.200000e+01
}

...再次,按预期工作。为任何输入调用它会给出一个固定的结果:

ready> f(5);
ready> Read top-level expression:
define double @1() {
entry:
%calltmp = call double @f(double 5.000000e+00)
ret double %calltmp
}

Evaluated to 12.000000

...不足为奇。然后,一个用参数做某事的函数定义:

ready> def g(x) x + 1;
ready> Read function definition:
define double @g(double %x) {
entry:
%addtmp = fadd double 1.000000e+00, %x
ret double %addtmp
}

...看起来没问题,字节码生成了。现在,调用它:

ready> g(5);
ready> Read top-level expression:
define double @2() {
entry:
%calltmp = call double @g(double 5.000000e+00)
ret double %calltmp
}

0x00D400A4 (0x0000000A 0x00000000 0x0028FF28 0x00D40087) <unknown module>
0x00C7A5E0 (0x01078A28 0x010CF040 0x0028FEF0 0x40280000)
0x004023F1 (0x00000001 0x01072FD0 0x01071B10 0xFFFFFFFF)
0x004010B9 (0x00000001 0x00000000 0x00000000 0x00000000)
0x00401284 (0x7EFDE000 0x0028FFD4 0x77E59F42 0x7EFDE000)
0x75693677 (0x7EFDE000 0x7B3361A2 0x00000000 0x00000000), BaseThreadInitThunk() + 0x12 bytes(s)
0x77E59F42 (0x0040126C 0x7EFDE000 0x00000000 0x00000000), RtlInitializeExceptionChain() + 0x63 bytes(s)
0x77E59F15 (0x0040126C 0x7EFDE000 0x00000000 0x78746341), RtlInitializeExceptionChain() + 0x36 bytes(s)

...崩溃。

通过一些初步的调试,我开始相信所涉及的代码片段,即用于顶级表达式的代码片段(调用参数为 5 的 g(x) ) 和被调用函数的函数都成功地进行了 JIT 编译。我相信是这种情况,因为我在崩溃之前得到了函数指针(并且我假设执行引擎仅在之后成功编译了函数返回一个函数指针).更准确地说,崩溃恰好发生在函数指针运行的位置,这意味着我的源文件中的这一行(在 HandleTopLevelExpression() 中):

  fprintf(stderr, "Evaluated to %f\n", FP());

很可能该行本身是无辜的,因为它成功地运行了其他功能。罪魁祸首可能在上面最后一个示例中由 FP 指向的函数中的某处,但由于该代码是运行时生成的,所以我的 cpp 中没有它文件。

关于为什么它可能会在特定情况下崩溃的任何想法?


更新#1:通过 gdb 运行进程在崩溃点显示:

Program received signal SIGILL, Illegal instruction.

还有一条没有告诉我任何信息的痕迹:

0x00ee0044 in ?? ()

更新 #2:为了进一步阐明这一点,这里是崩溃前后的集会:

00D70068   55               PUSH EBP
00D70069 89E5 MOV EBP,ESP
00D7006B 81E4 F8FFFFFF AND ESP,FFFFFFF8
00D70071 83EC 08 SUB ESP,8
00D70074 C5FB LDS EDI,EBX ; Here! ; Illegal use of register
00D70076 1045 08 ADC BYTE PTR SS:[EBP+8],AL
00D70079 C5FB LDS EDI,EBX ; Illegal use of register
00D7007B 58 POP EAX
00D7007C 05 6000D700 ADD EAX,0D70060
00D70081 C5FB LDS EDI,EBX ; Illegal use of register
00D70083 110424 ADC DWORD PTR SS:[ESP],EAX
00D70086 DD0424 FLD QWORD PTR SS:[ESP]
00D70089 89EC MOV ESP,EBP
00D7008B 5D POP EBP
00D7008C C3 RETN

崩溃发生在 00D70074,指令是 LDS EDI,EBX。它比 FP 指向的地址高几个地址(这让我相信这可能是 JIT 发出的代码,但请对这个结论持保留态度,因为我已经结束了我的头在这里)。

如您所见,反汇编器还在该行和下一个类似的行上添加了注释,称这是对寄存器的非法使用。老实说,我不知道为什么这个特定的扩展寄存器对对于这条指令来说是非法的,但是如果它是非法的,那它到底为什么存在,我们如何才能使编译器产生合法代码?

最佳答案

显然 LLVM 正在为您生成以 VEX 为前缀的 AVX 指令,但您的处理器不支持该指令集(您的反汇编程序也不支持)。

JIT 字节的 AVX 感知解码给出以下有效代码:

   0:   55                      push   ebp
1: 89 e5 mov ebp,esp
3: 81 e4 f8 ff ff ff and esp,0xfffffff8
9: 83 ec 08 sub esp,0x8
c: c5 fb 10 45 08 vmovsd xmm0,QWORD PTR [ebp+0x8]
11: c5 fb 58 05 60 00 d7 vaddsd xmm0,xmm0,QWORD PTR ds:0xd70060
18: 00
19: c5 fb 11 04 24 vmovsd QWORD PTR [esp],xmm0
1e: dd 04 24 fld QWORD PTR [esp]
21: 89 ec mov esp,ebp
23: 5d pop ebp
24: c3 ret

如果 LLVM 错误检测了您的原生架构,或者如果您只是想覆盖它,您可以更改示例代码中使用的 EngineBuilder,例如:

TheExecutionEngine = EngineBuilder(TheModule).setErrorStr(&ErrStr).setMCPU("i386").create();

您还可以设置架构或提供属性。

关于c++ - LLVM JIT 教程代码因简单的参数化函数而崩溃。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16389207/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com