为什么此LLVM IR代码会产生意外结果? [英] Why does this LLVM IR code produce unexpected results?

查看:109
本文介绍了为什么此LLVM IR代码会产生意外结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我真的很沮丧,因为这个问题困扰了我好几天,所以我将不胜感激.

I'm getting really frustrated, since this problem has been bugging me for days, so I'd appreciate every help possible.

我目前正在使用自己的编程语言,并且目前正在尝试实现将值与枚举大小写匹配的枚举和匹配语句,并运行相应的语句,但是到处都是意外的结果和段错误.

I'm currently making my own programming language and am currently trying to implement enums and match statements that match a value to an enum case and runs the corresponding statement but I'm getting unexpected results and segfaults here and there.

这是我的一种语言,可以运行(lli),但有时会产生意想不到的结果 (出于某些原因,打印1,而不是3):

Here's one piece of code of my language that runs (lli) but produces unexpected results sometimes (prints 1, not 3 for some reason):

class Node {
    fld value: int;
    fld next: OptionalNode;

    new(_value: int, _next: OptionalNode) {
        value = _value;
        next = _next;
    }
}

enum OptionalNode {
    val nil;
    val some(Node);
}

fun main(): int {
    var s: OptionalNode = OptionalNode.some(new Node(3, OptionalNode.nil));

    match s {
        OptionalNode.some(n) => print n.value;
    }

    var r: int = 0;

    ret r;
}

这是我的编译器生成的相应的LLVM IR:

This is the corresponding LLVM IR that my compiler generates:

; ModuleID = 'test.bc'
source_filename = "test"

%test.Node = type { i32, %test.OptionalNode }
%test.OptionalNode = type { i8, [8 x i8] }
%test.OptionalNode.nil = type { i8 }
%test.OptionalNode.some = type { i8, %test.Node* }

@str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1

declare i32 @printf(i8*, ...)

define void @"test.Node.!ctor$[test.Node]i[test.OptionalNode]"(%test.Node* %this, i32 %_value, %test.OptionalNode %_next) {
entry:
  %arg0 = alloca %test.Node*, align 8
  store %test.Node* %this, %test.Node** %arg0
  %arg1 = alloca i32, align 4
  store i32 %_value, i32* %arg1
  %arg2 = alloca %test.OptionalNode, align 16
  store %test.OptionalNode %_next, %test.OptionalNode* %arg2
  %ldarg1 = load i32, i32* %arg1
  %tmpld_cls = load %test.Node*, %test.Node** %arg0
  %tmpfld = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls, i32 0, i32 0
  store i32 %ldarg1, i32* %tmpfld
  %ldarg2 = load %test.OptionalNode, %test.OptionalNode* %arg2
  %tmpld_cls1 = load %test.Node*, %test.Node** %arg0
  %tmpfld2 = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls1, i32 0, i32 1
  store %test.OptionalNode %ldarg2, %test.OptionalNode* %tmpfld2
  ret void
}

define i32 @"test.main$v"() {
entry:
  %s = alloca %test.OptionalNode, align 16
  %enm = alloca %test.OptionalNode
  %0 = bitcast %test.OptionalNode* %enm to %test.OptionalNode.nil*
  %1 = getelementptr inbounds %test.OptionalNode.nil, %test.OptionalNode.nil* %0, i32 0, i32 0
  store i8 0, i8* %1
  %2 = load %test.OptionalNode, %test.OptionalNode* %enm
  %tmpalloc = alloca %test.Node
  call void @"test.Node.!ctor$[test.Node]i[test.OptionalNode]"(%test.Node* %tmpalloc, i32 3, %test.OptionalNode %2)
  %enm1 = alloca %test.OptionalNode
  %3 = bitcast %test.OptionalNode* %enm1 to %test.OptionalNode.some*
  %4 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %3, i32 0, i32 0
  store i8 1, i8* %4
  %5 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %3, i32 0, i32 1
  store %test.Node* %tmpalloc, %test.Node** %5
  %6 = load %test.OptionalNode, %test.OptionalNode* %enm1
  store %test.OptionalNode %6, %test.OptionalNode* %s
  %7 = getelementptr inbounds %test.OptionalNode, %test.OptionalNode* %s, i32 0, i32 0
  %8 = load i8, i8* %7
  switch i8 %8, label %match_end [
    i8 1, label %case1
  ]

case1:                                            ; preds = %entry
  %n = alloca %test.Node*, align 8
  %9 = bitcast %test.OptionalNode* %s to %test.OptionalNode.some*
  %10 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %9, i32 0, i32 1
  %11 = load %test.Node*, %test.Node** %10
  store %test.Node* %11, %test.Node** %n
  %tmpld_cls = load %test.Node*, %test.Node** %n
  %tmpgetfldgep = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls, i32 0, i32 0
  %tmpgetfldld = load i32, i32* %tmpgetfldgep
  %print_i = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @str, i32 0, i32 0), i32 %tmpgetfldld)
  br label %match_end

match_end:                                        ; preds = %case1, %entry
  %r = alloca i32, align 4
  store i32 0, i32* %r
  %tmploadlocal = load i32, i32* %r
  ret i32 %tmploadlocal
}

define i32 @main() {
entry:
  %call = tail call i32 @"test.main$v"()
  ret i32 %call
}

现在,正如我所说的那样,它可以完全编译并运行,但是由于某种原因,它有时会打印1而不是3,而我对此一点都不理解.我不知道如何调试llvm ir代码,并使用opt进行debug传递会产生错误的源代码行(所有偏移量都不同),这也会导致无意义(我使用的是llvm 8 btw,但使用llvm 6.0.我之前使用的1显示了相同的结果.

Now, as I said this compiles and runs completely, but for some reason it sometimes prints 1 instead 3 which I don't understand at all. I have no idea how to debug llvm ir code and applying the debugify pass with opt produces wrong source lines (all varying offset) which also makes NO SENSE (I'm using llvm 8 btw but llvm 6.0.1 which i was using before showed the same results).

然后,如果我将r变量的定义上移到match语句之前,突然出现段错误,由于前面提到的源代码行偏移,我无法精确定位该段错误.

Then, if I move the definition of the r variable up before the match statement, suddenly I get a segfault the position of which I cannot pinpoint because of the offset ir source lines that I mentioned before.

这是相应的代码和ir:

Here's the corresponding code and ir for that:

class Node {
    fld value: int;
    fld next: OptionalNode;

    new(_value: int, _next: OptionalNode) {
        value = _value;
        next = _next;
    }
}

enum OptionalNode {
    val nil;
    val some(Node);
}

fun main(): int {
    var s: OptionalNode = OptionalNode.some(new Node(3, OptionalNode.nil));

    var r: int = 0;

    match s {
        OptionalNode.some(n) => print n.value;
    }

    ret r;
}

; ModuleID = 'test.bc'
source_filename = "test"

%test.Node = type { i32, %test.OptionalNode }
%test.OptionalNode = type { i8, [8 x i8] }
%test.OptionalNode.nil = type { i8 }
%test.OptionalNode.some = type { i8, %test.Node* }

@str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1

declare i32 @printf(i8*, ...)

define void @"test.Node.!ctor$[test.Node]i[test.OptionalNode]"(%test.Node* %this, i32 %_value, %test.OptionalNode %_next) {
entry:
  %arg0 = alloca %test.Node*, align 8
  store %test.Node* %this, %test.Node** %arg0
  %arg1 = alloca i32, align 4
  store i32 %_value, i32* %arg1
  %arg2 = alloca %test.OptionalNode, align 16
  store %test.OptionalNode %_next, %test.OptionalNode* %arg2
  %ldarg1 = load i32, i32* %arg1
  %tmpld_cls = load %test.Node*, %test.Node** %arg0
  %tmpfld = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls, i32 0, i32 0
  store i32 %ldarg1, i32* %tmpfld
  %ldarg2 = load %test.OptionalNode, %test.OptionalNode* %arg2
  %tmpld_cls1 = load %test.Node*, %test.Node** %arg0
  %tmpfld2 = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls1, i32 0, i32 1
  store %test.OptionalNode %ldarg2, %test.OptionalNode* %tmpfld2
  ret void
}

define i32 @"test.main$v"() {
entry:
  %s = alloca %test.OptionalNode, align 16
  %enm = alloca %test.OptionalNode
  %0 = bitcast %test.OptionalNode* %enm to %test.OptionalNode.nil*
  %1 = getelementptr inbounds %test.OptionalNode.nil, %test.OptionalNode.nil* %0, i32 0, i32 0
  store i8 0, i8* %1
  %2 = load %test.OptionalNode, %test.OptionalNode* %enm
  %tmpalloc = alloca %test.Node
  call void @"test.Node.!ctor$[test.Node]i[test.OptionalNode]"(%test.Node* %tmpalloc, i32 3, %test.OptionalNode %2)
  %enm1 = alloca %test.OptionalNode
  %3 = bitcast %test.OptionalNode* %enm1 to %test.OptionalNode.some*
  %4 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %3, i32 0, i32 0
  store i8 1, i8* %4
  %5 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %3, i32 0, i32 1
  store %test.Node* %tmpalloc, %test.Node** %5
  %6 = load %test.OptionalNode, %test.OptionalNode* %enm1
  store %test.OptionalNode %6, %test.OptionalNode* %s
  %r = alloca i32, align 4
  store i32 0, i32* %r
  %7 = getelementptr inbounds %test.OptionalNode, %test.OptionalNode* %s, i32 0, i32 0
  %8 = load i8, i8* %7
  switch i8 %8, label %match_end [
    i8 1, label %case1
  ]

case1:                                            ; preds = %entry
  %n = alloca %test.Node*, align 8
  %9 = bitcast %test.OptionalNode* %s to %test.OptionalNode.some*
  %10 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %9, i32 0, i32 1
  %11 = load %test.Node*, %test.Node** %10
  store %test.Node* %11, %test.Node** %n
  %tmpld_cls = load %test.Node*, %test.Node** %n
  %tmpgetfldgep = getelementptr inbounds %test.Node, %test.Node* %tmpld_cls, i32 0, i32 0
  %tmpgetfldld = load i32, i32* %tmpgetfldgep
  %print_i = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @str, i32 0, i32 0), i32 %tmpgetfldld)
  br label %match_end

match_end:                                        ; preds = %case1, %entry
  %tmploadlocal = load i32, i32* %r
  ret i32 %tmploadlocal
}

define i32 @main() {
entry:
  %call = tail call i32 @"test.main$v"()
  ret i32 %call
}

我知道这些问题确实很糟糕,我可能只是通过将我的代码放到这里而破坏了一些规则,但是如果有人会牺牲一些时间来帮助一些真正沮丧并且接近放弃的家伙,我真的会很感激.

I know that these kinds of questions are really bad and I'm probably breaking some rules by just throwing my code into here but if someone would sacrifice some of their time to help some really frustrated and close to giving up guy, I'd be really grateful.

推荐答案

它看起来确实很棘手.我想我已经回答了你的问题.

It certainly looks tricky. I think I have the answer to your question.

当您尝试打印%tmpgetfldld 时,将导致段错误.如果尝试使用clang进行编译然后执行,则不会出现segfault.这并不是说这是lli的错,因为即使这样,您也会得到错误的输出,因为您正在访问无效的内存空间.让我解释一下这是怎么发生的.

The segfault is caused when you try to print %tmpgetfldld. If you try compiling with clang and then execute it, you will not get a segfault. That is not to say that it is lli's fault because even this way you will get a wrong output, because you are accessing invalid memory space. Let me explain how this happens.

%tmpgetfldld (无效)是i32,它最初是从%n 所指向的内存地址中提取的,该地址位于上面3行:

%tmpgetfldld (which is invalid) is an i32, which originallly was extracted from the memory address pointed by %n, 3 lines above:

%tmpld_cls = load %test.Node*, %test.Node** %n

如果%tmpgetfldld 的值无效,则意味着存储到%n %11 无效.原因是此指令:

If the value of %tmpgetfldld is invalid, then it means that %11, who was stored to %n is invalid. The reason is this instruction:

%9 = bitcast %test.OptionalNode* %s to %test.OptionalNode.some*

在程序开始时,您已分配给指针%s 大小,该大小等于%test.OptionalNode 对象的大小,即9个字节(1个字节)和另外8个字节的数组).然后,您指定要注册%9 %s 的位播,以键入%test.OptionalNode.some .此类型的总大小为1 + 4 + 1 + 8 * 1 = 14字节.在程序的这一点上,没有什么错,并且%9 指向与%s 相同的地址,但是您仅将其视为%test.OptionalNode .some .但是,在该内存空间中,您分配了9个字节,现在通过"getelementptr"或"extractvalue"指令可以访问14个字节.在第9个字节之后读取,将导致段错误.实际上,通过以下说明:

At the beggining of your program you have allocated to pointer %s size equal to the size of a %test.OptionalNode object, which is 9 bytes (1 byte and another 8 bytes for the array). Then you assign to register %9 the bitcast of %s to type %test.OptionalNode.some. This type has a total size of 1 + 4 + 1 + 8*1 = 14 bytes. At this point of your program nothing is wrong yet and %9 points to the same address %s did, but you only treat it as a %test.OptionalNode.some. However, at that memory space you had allocated 9 bytes and now through 'getelementptr' or 'extractvalue' instructions you have access to 14 bytes. Reading after the 9th byte, would cause a segfault. Indeed, through these instructions:

%10 = getelementptr inbounds %test.OptionalNode.some, %test.OptionalNode.some* %9, i32 0, i32 1
%11 = load %test.Node*, %test.Node** %10

您将获得一个指向字节1到13(从索引0开始计数)的指针.然后,该指针存储在下面并再次加载,并且只有在尝试访问该值时才会出现段错误,这在访问%tmpgetfldld 时会发生.

You get a pointer pointing to bytes 1 to 13 (counting from index 0). This pointer is then stored below and loaded again, and you will get the segfault only when you try to access the value, which happens when accessing %tmpgetfldld.

要解决段错误,您需要以某种方式警告编译器,在分配%s 或任何其他%test.OptionalNode 时,您至少具有 9个字节,但是例如,如果您将其位播到具有更大大小的结构中,则可能会需要更多字节.实际上,当子类具有可变大小的成员,但仍然必须以某种方式将其位播到父类时,这正是LLVM处理虚拟类和多态性的方式.因此,如果将%test.OptionalNode 结构声明更改为此,则可以解决segfault:

To solve the segfault, you need to warn the compiler somehow that when allocating %s, or any other %test.OptionalNode, you have at least 9 bytes, but you could expect to need more, if for example you bitcasted to a struct with bigger size. Actually this is exactly how LLVM treats virtual classes and polymorphism, when subclasses have variable sized members, but still have to be somehow bitcasted to the parent class. So if you change your %test.OptionalNode struct declaration to this, you solve the segfault:

%test.OptionalNode = type { i8, [8 x i8], i8(...)** }

最后一种类型是函数指针,指示您期望可变数目的i8(字节).也请在此处检查: LLVM i32(...)**是什么意思在类型定义中?

The last type is a function pointer indicating that you expect variable number of i8 (bytes). Check also here: LLVM what does i32 (...)** mean in type define?

如果进行此更改,您将摆脱段错误,但您会注意到您尚未完全解决问题.有时您可能会得到3作为输出,有时则是某种未定义的行为.这是因为,即使您声明了i8(...)**来解释位广播结构类型的额外字节,两种结构类型之间的公共内存区域中的数据类型也没有很好地对齐.您会注意到它们的差异从第二个字节开始:在%test.OptionalNode 中,一个i8数组开始,而在%test.OptionalNode.some 中,一个i32,然后是i8,然后是相同的i8数组.为了解决这个问题,您必须将结构定义更改为以下任一形式:

If you do this change, you get rid of the segfault, but you will notice that you haven't fully solved your problem. Sometimes you might get a 3 as an output, sometimes something else, sort of an undefined behavior. This is because, even though you declared an i8(...)** to explain the extra bytes of the bitcasted struct type, the data types that are in common memory areas between the two struct types, are not well aligned. You will notice that their difference starts on the second byte: In %test.OptionalNode an i8 array starts, whereas in %test.OptionalNode.some, there is an i32, then an i8, and then the same array of i8. To solve this you have to change your struct definitions to either this:

%test.OptionalNode = type { i8, [8 x i8], i8(...)** }
%test.OptionalNode.some = type { i8, [8 x i8], %test.Node* }

或者:

%test.OptionalNode = type { i8, i8(...)** }
%test.OptionalNode.some = type { i8, %test.Node* }

取决于您的设计是否需要[8 x i8]阵列.现在,您的输出始终为3,问题就消失了.我认为该解决方案也涵盖了您之前的问题(如何修复生成的llvm字节码中存在分段错误?).

Depends on whether you need that [8 x i8] array or not on your design. Now, your output is consistently 3 and your problem is gone. I think this solution covers your previous question as well (How to fix segmentation fault in genrated llvm bytecode?).

很抱歉,答案很长.希望对您有帮助.

Sorry for the long answer. I hope it helps.

这篇关于为什么此LLVM IR代码会产生意外结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆