今天我遇到了LLVM IR中浮点转换的奇怪问题。我使用的是Windows 10和llvm-3.5.2。我在C:
中写了这段代码#include <stdio.h>
int main() {
double b = 2.0;
float c = b;
printf("%d\n", c == 2.0);
return 0;
}
我使用了clang -S -emit-llvm
并获得了这个LLVM IR:
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%b = alloca double, align 8
%c = alloca float, align 4
store i32 0, i32* %retval
store double 2.000000e+00, double* %b, align 8
%0 = load double* %b, align 8
%conv = fptrunc double %0 to float
store float %conv, float* %c, align 4
%1 = load float* %c, align 4
%conv1 = fpext float %1 to double
%cmp = fcmp oeq double %conv1, 2.000000e+00
%conv2 = zext i1 %cmp to i32
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %conv2) #1
ret i32 0
}
; Function Attrs: nounwind
declare i32 @printf(i8*, ...) #0
执行的内容(在llvm-as
和lli
之后)给出了0
的错误结果!
问题似乎出现在fpext
中,并不是非常直观,因为它应该是这样的“安全”。转换(比fptrunc
更好的方向,从double到float)。
我是这么认为的,因为当我改变这一行时:
%cmp = fcmp oeq double %conv1, 2.000000e+00
到此:
%cmp = fcmp oeq float %1, 2.000000e+00
然后结果符合预期1
。
所以我的问题是为什么会这样,为什么这种转换失败了?这是LLVM中的某种错误吗?或者存在这种转换的更好方法?或许我在C中写了一个不安全的代码?如果是这样,那么使用浮点数就存在很大问题。执行此转换时,我不会对clang产生影响。例如,让我们说我想在上面输出c
。然后我写printf("%f", c)
并且clang生成代码,在将c
传递给printf
之前将其转换为双精度。结果再次不正确。那么如何安全地打印浮动?如何安全地使用浮子?
我不确定这个问题是否也出现在Linux上。
答案 0 :(得分:1)
我在ubuntu14.04 LLVM3.6中工作,我有像你一样的保存输出,但也得到了正确的答案。
我认为你应该检查你的铿锵版,如果铿锵使用其他版本的llvm,你可能会有意想不到的答案。
像这样:clang --version
Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)
Target: x86_64-pc-linux-gnu
Thread model: posix
llvm-IR:
; ModuleID = 'main.c'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
; Function Attrs: nounwind uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
%b = alloca double, align 8
%c = alloca float, align 4
store i32 0, i32* %1
store double 2.000000e+00, double* %b, align 8
%2 = load double* %b, align 8
%3 = fptrunc double %2 to float
store float %3, float* %c, align 4
%4 = load float* %c, align 4
%5 = fpext float %4 to double
%6 = fcmp oeq double %5, 2.000000e+00
%7 = zext i1 %6 to i32
%8 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %7)
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = !{!"Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)"}