LLVM IR嵌套phi指令

时间:2018-08-20 00:59:24

标签: compiler-construction llvm llvm-ir

我正在使用自己的编程语言。我目前正在LLVM IR中生成代码。我对使用phi的嵌套If语句有疑问。所以可以说我有我的语言:

  if n < 0 then
        print("n < 0")
    else
        if 100 < n then
            print("100")
        else
            print("\n")

我是在llvm ir中生成的:

; If
; ObjectIdentifier
%15 = load %struct.Main*, %struct.Main** %2
%16 = getelementptr inbounds %struct.Main, %struct.Main* %15, i32 0, i32 1
%17 = load i32, i32* %16

; VarValue
%18 = alloca i32
store i32 0, i32* %18
%19 = load i32, i32* %18

; Lower
%20 = icmp slt i32 %17, %19

br i1 %20, label %condIf1, label %condElse1 

condIf1:
    ; Call Method
    %21 = load %struct.Main*, %struct.Main** %2
    %22 = getelementptr inbounds %struct.Main, %struct.Main* %21, i32 0, i32 0
    ; Arguments
    ; VarValue
    %23 = alloca [6 x i8]
    store [6 x i8] c"n < 0\00", [6 x i8]* %23
    %24 = bitcast [6 x i8]* %23 to i8*

    %25 = call %struct.IO* @IO_print(%struct.IO* %22, i8* %24)

    br label %condEnd1

condElse1:
    ; If
    ; VarValue
    %26 = alloca i32
    store i32 100, i32* %26
    %27 = load i32, i32* %26

    ; ObjectIdentifier
    %28 = load %struct.Main*, %struct.Main** %2
    %29 = getelementptr inbounds %struct.Main, %struct.Main* %28, i32 0, i32 1
    %30 = load i32, i32* %29

    ; Lower
    %31 = icmp slt i32 %27, %30

    br i1 %31, label %condIf2, label %condElse2     

    condIf2:
        ; Call Method
        %32 = load %struct.Main*, %struct.Main** %2
        %33 = getelementptr inbounds %struct.Main, %struct.Main* %32, i32 0, i32 0
        ; Arguments
        ; VarValue
        %34 = alloca [8 x i8]
        store [8 x i8] c"n > 100\00", [8 x i8]* %34
        %35 = bitcast [8 x i8]* %34 to i8*

        %36 = call %struct.IO* @IO_print(%struct.IO* %33, i8* %35)

        br label %condEnd2

    condElse2:
        ; Call Method
        %37 = load %struct.Main*, %struct.Main** %2
        %38 = getelementptr inbounds %struct.Main, %struct.Main* %37, i32 0, i32 0
        ; Arguments
        ; VarValue
        %39 = alloca [2 x i8]
        store [2 x i8] c"\0a\00", [2 x i8]* %39
        %40 = bitcast [2 x i8]* %39 to i8*

        %41 = call %struct.IO* @IO_print(%struct.IO* %38, i8* %40)

        br label %condEnd2

    condEnd2:
        %42 = phi %struct.IO* [%36, %condIf2], [%41, %condElse2]

    br label %condEnd1

condEnd1:
    %43 = phi %struct.IO* [%25, %condIf1], [%43, %condElse1]

一切都编译了,但是我得到了那些错误:

 PHI node entries do not match predecessors!
 %43 = phi %struct.IO* [ %25, %condIf1 ], [ %42, %condElse1 ]
 label %condElse1
 label %condEnd2 
 Instruction does not dominate all uses!
 %42 = phi %struct.IO* [ %36, %condIf2 ], [ %41, %condElse2 ]
 %43 = phi %struct.IO* [ %25, %condIf1 ], [ %42, %condElse1 ]

我无法确切地知道phi是什么问题。您是否有关于如何解决此问题的提示或使用phi之后的其他方法? 谢谢!

1 个答案:

答案 0 :(得分:3)

condEnd1:
    %43 = phi %struct.IO* [%25, %condIf1], [%test, %condElse1]

condEnd1的前身(即跳转到condEnd1的块)是condIf1condEnd2,而不是condElse1(这就是错误消息的含义)告诉你)。 phi节点应列出该块的所有前身,而不要列出其他任何块,因为它说“如果您从块foo跳到此处,请使用值bar”,并且只有在foo为一个实际上可以跳到phi节点所在的块的块。

另外%test在块%condElse1中不可用(%condElse1不包含%test的定义,也没有到{{1}的所有控制流路径}进行操作,所以如果%condElse1是先前版本,则从那里跳转时将不允许您使用%condElse1的值。

可以通过在phi中将%test替换为%condElse1来解决这两个问题。 %condEnd2实际上是%condEnd2的前身,它包含condEnd1的定义。


关于使用phi节点的替代方法:

表示局部变量的一种常用方法是在函数开始时使用%test为每个变量分配堆栈空间,然后在需要该值时从该内存中进行写入和读取。这样,您将为每个变量有一个寄存器来存储变量的地址,该地址将在函数的第一块中定义,从而控制整个函数,然后,每次使用变量时,临时寄存器都不会t超过当前块。因此不需要phi节点。

在永不使用变量地址的情况下,LLVM的mem2reg阶段将使用寄存器和phi节点将其重写为版本,因此您将获得相同的优化性和性能,但不必将所有内容都转换为SSA形成自己。