2015-12-20 190 views
1

今天我遇到了一個奇怪的問題,在LLVM IR中使用浮點數鑄造。我使用Windows 10和llvm-3.5.2。我在用C寫的代碼:LLVM IR浮點數鑄造

#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?如果是這樣,那麼在使用浮點數方面存在一個巨大的問題。我在執行此轉換時對叮噹聲沒有影響。例如,假設我想輸出上面的c。然後我寫printf("%f", c)和叮噹生成的代碼,使c加倍之前,將其傳遞到printf。結果又不正確。那麼如何安全地打印一個浮點數?如何安全地使用浮動?

我不確定這個問題是否也出現在Linux上。

+1

在我的Linux機器LLVM 3.4鏘還生產鈣鎂磷肥雙,但計算你所期望的結果。 – box

+1

我不遵循 - 爲什麼你期望'float'和'double'的確切比較是真實的?這是不能保證的。你問「如何打印一個浮動」,但你沒有在你的實際代碼中打印浮動。你見過這個嗎? https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html –

+0

好吧,我明白了。你有一點,當混合這些類型時,我不應該期望一般的好結果。所以現在的問題是關於印刷浮標。正如我在使用'printf(「%f」,c)''在我的機器上使用上面的代碼嘗試這樣做時所寫的,我也得到了'0'。似乎printf破壞我的浮動(因爲它真的等於2.0f時勾選)。我不知道如何解決它。 – xan

回答

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