为什么这个 LLVM IR 代码会产生意想不到的结果?
Why does this LLVM IR code produce unexpected results?
我真的很沮丧,因为这个问题已经困扰我好几天了,所以我很感激所有可能的帮助。
我目前正在制作我自己的编程语言,目前正在尝试实现枚举和匹配语句,将值与枚举案例匹配并运行相应的语句,但我得到了意想不到的结果和段错误。
这是我的语言的一段代码,运行 (lli) 但产生意外结果有时(出于某种原因打印 1,而不是 3):
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:
; 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[=11=]A[=11=]", 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 应用 debugify pass 产生错误的源代码行(所有不同的偏移量)这也使得 NO SENSE (顺便说一句,我使用的是 llvm 8 但是我之前使用的 llvm 6.0.1 显示了相同的结果。
然后,如果我将 r 变量的定义向上移动到 match 语句之前,突然我得到一个段错误,由于我之前提到的偏移 ir 源代码行,我无法确定其位置。
这是相应的代码和 ir:
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[=13=]A[=13=]", 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
}
我知道这类问题真的很糟糕,我可能只是将我的代码扔到这里而违反了一些规则,但如果有人愿意牺牲一些时间来帮助一些非常沮丧和接近放弃的人, 将不胜感激
这看起来确实很棘手。我想我已经找到了你问题的答案。
段错误是在您尝试打印 %tmpgetfldld 时引起的。如果您尝试使用 clang 进行编译然后执行它,您将不会遇到段错误。这并不是说这是 lli 的错,因为即使这样你也会得到错误的输出,因为你正在访问无效内存 space。让我解释一下这是怎么发生的。
%tmpgetfldld(无效)是一个i32,最初是从%n指向的内存地址提取的,3上面的行:
%tmpld_cls = load %test.Node*, %test.Node** %n
如果%tmpgetfldld的值无效,则表示%11被存储到 %n 无效。原因是这条指令:
%9 = bitcast %test.OptionalNode* %s to %test.OptionalNode.some*
在程序开始时,您分配给指针 %s 的大小等于 %test.OptionalNode[=63= 的大小] 对象,它是 9 个字节(1 个字节,另外 8 个字节用于数组)。然后,您将 %9 的 %s 的位转换分配给寄存器 %test.OptionalNode.some.此类型的总大小为 1 + 4 + 1 + 8*1 = 14 个字节。此时您的程序还没有任何错误,%9 指向相同的地址 %s,但您只将其视为 %test.OptionalNode.some.但是,在该内存 space 处,您已经分配了 9 个字节,现在通过 'getelementptr' 或 'extractvalue' 指令,您可以访问 14 个字节。在第 9 个字节之后读取,会导致段错误。确实,通过这些说明:
%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.
时
要解决段错误,您需要以某种方式警告编译器在分配 %s 或任何其他 %test.OptionalNode[=63 时=],你有 至少 9 个字节,但你可能需要更多,例如,如果你位转换为一个更大的结构。实际上,这正是 LLVM 对待虚拟 classes 和多态性的方式,当子 classes 具有可变大小的成员时,但仍然必须以某种方式位播到父 class。因此,如果您将 %test.OptionalNode 结构声明更改为此,您将解决段错误:
%test.OptionalNode = type { i8, [8 x i8], i8(...)** }
最后一个类型是一个函数指针,指示您期望可变数量的 i8(字节)。也在这里检查:LLVM what does i32 (...)** mean in type define?
如果你做了这个改变,你就摆脱了段错误,但你会注意到你还没有完全解决你的问题。有时你可能会得到一个 3 作为输出,有时是其他东西,一种未定义的行为。这是因为,即使您声明了一个 i8(...)** 来解释位转换结构类型的额外字节,但两种结构类型之间公共内存区域中的数据类型并没有很好地对齐。您会注意到它们的区别从第二个字节开始:在 %test.OptionalNode 中,i8 数组开始,而在 %test.OptionalNode.some,有一个i32,然后是一个i8,然后就是i8的同一个数组。要解决这个问题,您必须将结构定义更改为:
%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,您的问题消失了。我认为此解决方案也涵盖了您之前的问题 (How to fix segmentation fault in genrated llvm bytecode?)。
抱歉回答太长了。希望对你有帮助。
我真的很沮丧,因为这个问题已经困扰我好几天了,所以我很感激所有可能的帮助。
我目前正在制作我自己的编程语言,目前正在尝试实现枚举和匹配语句,将值与枚举案例匹配并运行相应的语句,但我得到了意想不到的结果和段错误。
这是我的语言的一段代码,运行 (lli) 但产生意外结果有时(出于某种原因打印 1,而不是 3):
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:
; 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[=11=]A[=11=]", 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 应用 debugify pass 产生错误的源代码行(所有不同的偏移量)这也使得 NO SENSE (顺便说一句,我使用的是 llvm 8 但是我之前使用的 llvm 6.0.1 显示了相同的结果。
然后,如果我将 r 变量的定义向上移动到 match 语句之前,突然我得到一个段错误,由于我之前提到的偏移 ir 源代码行,我无法确定其位置。
这是相应的代码和 ir:
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[=13=]A[=13=]", 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
}
我知道这类问题真的很糟糕,我可能只是将我的代码扔到这里而违反了一些规则,但如果有人愿意牺牲一些时间来帮助一些非常沮丧和接近放弃的人, 将不胜感激
这看起来确实很棘手。我想我已经找到了你问题的答案。
段错误是在您尝试打印 %tmpgetfldld 时引起的。如果您尝试使用 clang 进行编译然后执行它,您将不会遇到段错误。这并不是说这是 lli 的错,因为即使这样你也会得到错误的输出,因为你正在访问无效内存 space。让我解释一下这是怎么发生的。
%tmpgetfldld(无效)是一个i32,最初是从%n指向的内存地址提取的,3上面的行:
%tmpld_cls = load %test.Node*, %test.Node** %n
如果%tmpgetfldld的值无效,则表示%11被存储到 %n 无效。原因是这条指令:
%9 = bitcast %test.OptionalNode* %s to %test.OptionalNode.some*
在程序开始时,您分配给指针 %s 的大小等于 %test.OptionalNode[=63= 的大小] 对象,它是 9 个字节(1 个字节,另外 8 个字节用于数组)。然后,您将 %9 的 %s 的位转换分配给寄存器 %test.OptionalNode.some.此类型的总大小为 1 + 4 + 1 + 8*1 = 14 个字节。此时您的程序还没有任何错误,%9 指向相同的地址 %s,但您只将其视为 %test.OptionalNode.some.但是,在该内存 space 处,您已经分配了 9 个字节,现在通过 'getelementptr' 或 'extractvalue' 指令,您可以访问 14 个字节。在第 9 个字节之后读取,会导致段错误。确实,通过这些说明:
%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.
时要解决段错误,您需要以某种方式警告编译器在分配 %s 或任何其他 %test.OptionalNode[=63 时=],你有 至少 9 个字节,但你可能需要更多,例如,如果你位转换为一个更大的结构。实际上,这正是 LLVM 对待虚拟 classes 和多态性的方式,当子 classes 具有可变大小的成员时,但仍然必须以某种方式位播到父 class。因此,如果您将 %test.OptionalNode 结构声明更改为此,您将解决段错误:
%test.OptionalNode = type { i8, [8 x i8], i8(...)** }
最后一个类型是一个函数指针,指示您期望可变数量的 i8(字节)。也在这里检查:LLVM what does i32 (...)** mean in type define?
如果你做了这个改变,你就摆脱了段错误,但你会注意到你还没有完全解决你的问题。有时你可能会得到一个 3 作为输出,有时是其他东西,一种未定义的行为。这是因为,即使您声明了一个 i8(...)** 来解释位转换结构类型的额外字节,但两种结构类型之间公共内存区域中的数据类型并没有很好地对齐。您会注意到它们的区别从第二个字节开始:在 %test.OptionalNode 中,i8 数组开始,而在 %test.OptionalNode.some,有一个i32,然后是一个i8,然后就是i8的同一个数组。要解决这个问题,您必须将结构定义更改为:
%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,您的问题消失了。我认为此解决方案也涵盖了您之前的问题 (How to fix segmentation fault in genrated llvm bytecode?)。
抱歉回答太长了。希望对你有帮助。