printf doesn't work for floats in LLVM IR

我是研究僧i 提交于 2020-08-10 03:37:12

问题


I want to print the value of a floating point variable to the screen. I am declaring printf() function in the LLVM IR code, and it is linking in successfully.

Whenever I print an integer or a character data type, or a string, printf() prints them normally to the screen as it prints them in the C code. However, if I pass a float to printf(), instead of printing the floating point number, it prints 0.000000. I checked the source code multiple times and it seems that the syntax is correct. It should be printing 2.75! I am looking at this code and I absolutely do not understand how code has a different behavior than what I wrote it.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@obj1 = global {i32, float, i8} zeroinitializer

@format_string = constant [10 x i8] c"%i %f %c\0A\00"

declare i32 @printf(i8*, ...)

define i32 @main() {
entry:
    %obj1 = load {i32, float, i8}, {i32, float, i8}* @obj1

    %obj2 = insertvalue {i32, float, i8} %obj1, i32 44, 0
    %obj3 = insertvalue {i32, float, i8} %obj2, float 2.75, 1
    %obj4 = insertvalue {i32, float, i8} %obj3, i8 36, 2

    store {i32, float, i8} %obj4, {i32, float, i8}* @obj1

    %ptr.i32 = getelementptr {i32, float, i8}, {i32, float, i8}* @obj1, i32 0, i32 0
    %0 = load i32, i32* %ptr.i32
    %ptr.float = getelementptr {i32, float, i8}, {i32, float, i8}* @obj1, i32 0, i32 1
    %1 = load float, float* %ptr.float
    %ptr.i8 = getelementptr {i32, float, i8}, {i32, float, i8}* @obj1, i32 0, i32 2
    %2 = load i8, i8* %ptr.i8

    %format_ptr = getelementptr [10 x i8], [10 x i8]* @format_string, i64 0, i64 0
    call i32 (i8*, ...) @printf(i8* %format_ptr, i32 %0, float %1, i8 %2)

    ret i32 0
}

When I compile the LLVM IR code, this is the output:

$ llvm-as code.ll -o code.bc
$ lli code.bc
44 0.000000 $

It successfully printed the integer and the character, but not the floating point number!


回答1:


The reason is because printf is a variadic function and variadic functions promote float arguments to double. See Why does printf() promote a float to a double?

So you should first cast %1 to double before passing passing it to printf, which is what clang does. For example

void f() {
  float a = 1;
  printf("%f", a);
}

gives

@.str = private unnamed_addr constant [3 x i8] c"%f\00", align 1

define dso_local void @f() #0 !dbg !7 {
  %1 = alloca float, align 4
  call void @llvm.dbg.declare(metadata float* %1, metadata !11, 
                              metadata !DIExpression()), !dbg !13
  store float 1.000000e+00, float* %1, align 4, !dbg !13
  %2 = load float, float* %1, align 4, !dbg !14
  %3 = fpext float %2 to double, !dbg !14
  %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x 
            i8], [3 x i8]* @.str, i64 0, i64 0), double %3), !dbg !15
  ret void, !dbg !16
}

Note the use of fpext



来源:https://stackoverflow.com/questions/63144506/printf-doesnt-work-for-floats-in-llvm-ir

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!