LLVM IR浮点转换

时间:2015-12-20 10:31:44

标签: c llvm llvm-ir

今天我遇到了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-aslli之后)给出了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上。

1 个答案:

答案 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)"}