LLVM IR:<;N x i1>;导致不同结果的向量指令排序

LLVM IR:<;N x i1>;导致不同结果的向量指令排序,llvm,code-generation,llvm-ir,Llvm,Code Generation,Llvm Ir,我一直在使用LLVM作为编译器的后端(我不是使用LLVM库,而是使用我自己的库来生成必要的IR,原因有很多)。目前,我正在实现向量运算。向量的比较会产生布尔向量,这会给我带来问题 为了访问向量元素,我一直在使用extractelement和insertelement但是,当我以不同的顺序执行这些指令时,我会出现一些奇怪的行为。下面的代码示例具有相同的说明,逻辑上应该相同。版本1输出BAA,而版本2输出BAB。版本2是逻辑上正确的版本,但我无法理解为什么版本1输出错误的版本,但具有完全相同的指令,

我一直在使用LLVM作为编译器的后端(我不是使用LLVM库,而是使用我自己的库来生成必要的IR,原因有很多)。目前,我正在实现向量运算。向量的比较会产生布尔向量
,这会给我带来问题

为了访问向量元素,我一直在使用
extractelement
insertelement
但是,当我以不同的顺序执行这些指令时,我会出现一些奇怪的行为。下面的代码示例具有相同的说明,逻辑上应该相同。版本1输出
BAA
,而版本2输出
BAB
。版本2是逻辑上正确的版本,但我无法理解为什么版本1输出错误的版本,但具有完全相同的指令,只是顺序不同

编辑:解决此问题的一种解决方法是将IR传递给
opt-mem2reg
,然后编译位码。这是可以的,但对调试没有用处,因此不能解决我的问题

版本1
;版本1-由我天真的SSA生成器生成
; 输出:BAA(不正确)
声明i32@putchar(i32)
定义void@main(){
条目:
%0=alloca,对齐8;v
存储零初始值设定项*%0
%1=alloca,对齐8
存储零初始值设定项,*%1
%2=负载,*%1,对齐8
%3=插入元素%2,i1为真,i64为0
%4=插入元素%3,i1为假,i64为1
%5=插入元素%4,i1为真,i64为2
%6=插入元素%5,i1为假,i64为3
%7=插入元素%6,i1为真,i64为4
%8=插入元素%7,i1为假,i64为5
%9=插入元素%8,i1为真,i64为6
%10=插入元素%9,i1为假,i64为7
存储%10,*%0
%11=负载,*%0,对齐8
%12=提取元素%11,i64 0
%13=zext i1%12至i32
%14=加上i32%13,65;+A
%15=调用i32@putchar(i32%14)
%16=负载,*%0,对齐8
%17=提取元素%16,i64 1
%18=zext i1%17至i32
%19=加上i32%18,65;+A
%20=调用i32@putchar(i32%19)
%21=负载,*%0,对齐8
%22=提取元素%21,i64 2
%23=zext i1%22至i32
%24=加上i32%23,65;+A
%25=调用i32@putchar(i32%24)
%26=调用i32@putchar(i32 10);\n
ret void
}

版本2
;版本2-手动修改版本1的版本
; 输出:BAB(正确)
声明i32@putchar(i32)
定义void@main(){
条目:
%0=alloca,对齐8;v
存储零初始值设定项*%0
%1=alloca,对齐8
存储零初始值设定项,*%1
%2=负载,*%1,对齐8
%3=插入元素%2,i1为真,i64为0
%4=插入元素%3,i1为假,i64为1
%5=插入元素%4,i1为真,i64为2
%6=插入元素%5,i1为假,i64为3
%7=插入元素%6,i1为真,i64为4
%8=插入元素%7,i1为假,i64为5
%9=插入元素%8,i1为真,i64为6
%10=插入元素%9,i1为假,i64为7
存储%10,*%0
%11=负载,*%0,对齐8
%12=负载,*%0,对齐8
%13=负载,*%0,对齐8
%14=提取元素%11,i64 0
%15=提取元素%12,i64 1
%16=提取元素%13,i64 2
%17=zext i1%14至i32
%18=zext i1%15至i32
%19=zext i1%16至i32
%20=加上i32%17,65;+A
%21=加上i32%18,65;+A
%22=加上i32%19,65;+A
%23=调用i32@putchar(i32%20)
%24=调用i32@putchar(i32%21)
%25=调用i32@putchar(i32%22)
%26=调用i32@putchar(i32 10);\n
ret void
}

这是LLVM中的一个bug,这并非不可能。版本#2和第一次调用#1可能可以工作,因为它可以优化为常量。尝试将布尔表示为
i8
,而不是
i1
——至少在内存中是这样。您也可以尝试禁用向量指令:<代码> -MATTR=SSE 。您可以考虑在LLVM DEV邮件列表()BANTAR上询问这个问题。似乎
-mattr=-sse
产生了更奇怪的结果(仍然不是我所期望的)<代码>向量似乎按大小进行压缩,即
=(1*N+7)/8字节的大小,这可能是问题的根源。常规大小的向量正如我所期望的那样工作,我对它们没有问题,但我不能不使用布尔向量,因为它们是比较指令的返回类型。这在LLVM中不是不可能的。版本#2和第一次调用#1可能可以工作,因为它可以优化为常量。尝试将布尔表示为
i8
,而不是
i1
——至少在内存中是这样。您也可以尝试禁用向量指令:<代码> -MATTR=SSE 。您可以考虑在LLVM DEV邮件列表()BANTAR上询问这个问题。似乎
-mattr=-sse
产生了更奇怪的结果(仍然不是我所期望的)<代码>
向量似乎按大小进行压缩,即
=(1*N+7)/8字节的大小,这可能是问题的根源。常规大小的向量正如我所期望的那样工作,我对它们没有任何问题,但我不能使用布尔向量,因为它们是比较指令的返回类型。
; Version 1 - Generated by my naïve SSA generator
; Outputs: BAA (incorrect)
declare i32 @putchar(i32)

define void @main() {
entry:
    %0 = alloca <8 x i1>, align 8 ; v
    store <8 x i1> zeroinitializer, <8 x i1>* %0
    %1 = alloca <8 x i1>, align 8
    store <8 x i1> zeroinitializer, <8 x i1>* %1
    %2 = load <8 x i1>, <8 x i1>* %1, align 8
    %3 = insertelement <8 x i1> %2, i1 true, i64 0
    %4 = insertelement <8 x i1> %3, i1 false, i64 1
    %5 = insertelement <8 x i1> %4, i1 true, i64 2
    %6 = insertelement <8 x i1> %5, i1 false, i64 3
    %7 = insertelement <8 x i1> %6, i1 true, i64 4
    %8 = insertelement <8 x i1> %7, i1 false, i64 5
    %9 = insertelement <8 x i1> %8, i1 true, i64 6
    %10 = insertelement <8 x i1> %9, i1 false, i64 7
    store <8 x i1> %10, <8 x i1>* %0

    %11 = load <8 x i1>, <8 x i1>* %0, align 8
    %12 = extractelement <8 x i1> %11, i64 0
    %13 = zext i1 %12 to i32
    %14 = add i32 %13, 65 ; + 'A'
    %15 = call i32 @putchar(i32 %14)

    %16 = load <8 x i1>, <8 x i1>* %0, align 8
    %17 = extractelement <8 x i1> %16, i64 1
    %18 = zext i1 %17 to i32
    %19 = add i32 %18, 65 ; + 'A'
    %20 = call i32 @putchar(i32 %19)

    %21 = load <8 x i1>, <8 x i1>* %0, align 8
    %22 = extractelement <8 x i1> %21, i64 2
    %23 = zext i1 %22 to i32
    %24 = add i32 %23, 65 ; + 'A'
    %25 = call i32 @putchar(i32 %24)

    %26 = call i32 @putchar(i32 10) ; \n

    ret void
}
; Version 2 - Manually modified version of Version 1
; Outputs: BAB (correct)
declare i32 @putchar(i32)

define void @main() {
entry:
    %0 = alloca <8 x i1>, align 8 ; v
    store <8 x i1> zeroinitializer, <8 x i1>* %0
    %1 = alloca <8 x i1>, align 8
    store <8 x i1> zeroinitializer, <8 x i1>* %1
    %2 = load <8 x i1>, <8 x i1>* %1, align 8
    %3 = insertelement <8 x i1> %2, i1 true, i64 0
    %4 = insertelement <8 x i1> %3, i1 false, i64 1
    %5 = insertelement <8 x i1> %4, i1 true, i64 2
    %6 = insertelement <8 x i1> %5, i1 false, i64 3
    %7 = insertelement <8 x i1> %6, i1 true, i64 4
    %8 = insertelement <8 x i1> %7, i1 false, i64 5
    %9 = insertelement <8 x i1> %8, i1 true, i64 6
    %10 = insertelement <8 x i1> %9, i1 false, i64 7
    store <8 x i1> %10, <8 x i1>* %0

    %11 = load <8 x i1>, <8 x i1>* %0, align 8
    %12 = load <8 x i1>, <8 x i1>* %0, align 8
    %13 = load <8 x i1>, <8 x i1>* %0, align 8

    %14 = extractelement <8 x i1> %11, i64 0
    %15 = extractelement <8 x i1> %12, i64 1
    %16 = extractelement <8 x i1> %13, i64 2

    %17 = zext i1 %14 to i32
    %18 = zext i1 %15 to i32
    %19 = zext i1 %16 to i32

    %20 = add i32 %17, 65 ; + 'A'
    %21 = add i32 %18, 65 ; + 'A'
    %22 = add i32 %19, 65 ; + 'A'

    %23 = call i32 @putchar(i32 %20)
    %24 = call i32 @putchar(i32 %21)
    %25 = call i32 @putchar(i32 %22)

    %26 = call i32 @putchar(i32 10) ; \n

    ret void
}