【问题标题】:LLVM IR floating point castLLVM IR 浮点转换
【发布时间】:2015-12-20 10:31:43
【问题描述】:

今天我在 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 更好的方向,即从双精度到浮点)。

我是这么认为的,因为当我改变这一行时:

%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 转换为double 的代码,然后将其传递给printf。结果又不正确了。那么如何安全地打印一个浮点数呢?如何安全地使用花车?

我不确定这个问题是否也出现在 Linux 上。

【问题讨论】:

  • 在我的带有 LLVM 3.4 的 Linux 机器上,Clang 也会产生 fcmp double 但会计算出您预期的结果。
  • 我不明白——你为什么认为floatdouble 的精确比较是真的?不能保证。您问“如何打印浮点数”,但您没有在实际代码中打印浮点数。你见过这个吗? docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
  • 好的,我明白了。你有一点,当混合这些类型时,我通常不应该期待好的结果。所以现在的问题实际上是关于打印花车。正如我写的,当我尝试在我的机器上使用上面的代码使用printf("%f", c) 执行此操作时,我也得到了0。似乎 printf 破坏了我的浮点数(因为检查时它确实等于 2.0f)。我不知道如何解决它。
  • @xan 用于打印浮动,请参阅 stackoverflow.com/a/63156309/8925535

标签: c llvm llvm-ir


【解决方案1】:

我在 ubuntu14.04 LLVM3.6 工作,我和你一样有保存输出,但也得到了正确的答案。

我认为你应该检查一下你的clang版本,如果clang使用了其他版本的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)"}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 2020-07-26
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多