llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.11k stars 12.01k forks source link

LLVM11 debug info is different from LLVM8 debug info when running the same compilation command #60631

Open 2994186010 opened 1 year ago

2994186010 commented 1 year ago

The C source code is as following.

#include "fdlibm.h"
#include <errno.h>

#ifdef __STDC__
    double ldexp(double value, int exp)
#else
    double ldexp(value, exp)
    double value; int exp;
#endif
{
    if(!finite(value)||value==0.0) return value;
    value = scalbn(value,exp);
    if(!finite(value)||value==0.0) errno = ERANGE;
    return value;
}

The compilation command is clang -emit-llvm -g -O3 -D_IEEE_LIBM -Wall -Wuninitialized -c s_ldexp.c -o s_ldexp.bc I transform the bitcode into human-readable LLVM assembly language using this command llvm-dis s_ldexp.bc -o s_ldexp.ll

The IR compiled by clang8 is as following.

; ModuleID = 's_ldexp.llvm8.O3.bc'
source_filename = "s_ldexp.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: nounwind uwtable
define dso_local double @ldexp(double, i32) local_unnamed_addr #0 !dbg !7 {
  call void @llvm.dbg.value(metadata double %0, metadata !13, metadata !DIExpression()), !dbg !15
  call void @llvm.dbg.value(metadata i32 %1, metadata !14, metadata !DIExpression()), !dbg !16
  %3 = tail call double @llvm.fabs.f64(double %0) #4, !dbg !17
  %4 = fcmp ueq double %3, 0x7FF0000000000000, !dbg !17
  %5 = fcmp oeq double %0, 0.000000e+00, !dbg !19
  %6 = or i1 %5, %4, !dbg !20
  br i1 %6, label %15, label %7, !dbg !20

; <label>:7:                                      ; preds = %2
  %8 = tail call double @scalbn(double %0, i32 %1) #5, !dbg !21
  call void @llvm.dbg.value(metadata double %8, metadata !13, metadata !DIExpression()), !dbg !15
  %9 = tail call double @llvm.fabs.f64(double %8) #4, !dbg !22
  %10 = fcmp ueq double %9, 0x7FF0000000000000, !dbg !22
  %11 = fcmp oeq double %8, 0.000000e+00, !dbg !24
  %12 = or i1 %11, %10, !dbg !25
  br i1 %12, label %13, label %15, !dbg !25

; <label>:13:                                     ; preds = %7
  %14 = tail call i32* @__errno_location() #6, !dbg !26
  store i32 34, i32* %14, align 4, !dbg !27, !tbaa !28
  br label %15, !dbg !26

; <label>:15:                                     ; preds = %13, %7, %2
  %16 = phi double [ %0, %2 ], [ %8, %7 ], [ %8, %13 ], !dbg !32
  ret double %16, !dbg !33
}

; Function Attrs: nounwind readnone speculatable
declare double @llvm.fabs.f64(double) #1

; Function Attrs: nounwind
declare dso_local double @scalbn(double, i32) local_unnamed_addr #2

; Function Attrs: nounwind readnone
declare dso_local i32* @__errno_location() local_unnamed_addr #3

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #1

attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable }
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { readnone }
attributes #5 = { nounwind }
attributes #6 = { nounwind readnone }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.0 (tags/RELEASE_800/final)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "s_ldexp.c", directory: "/root/test/ficl/fdlibm53")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 8.0.0 (tags/RELEASE_800/final)"}
!7 = distinct !DISubprogram(name: "ldexp", scope: !1, file: !1, line: 18, type: !8, scopeLine: 23, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !11}
!10 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !{!13, !14}
!13 = !DILocalVariable(name: "value", arg: 1, scope: !7, file: !1, line: 18, type: !10)
!14 = !DILocalVariable(name: "exp", arg: 2, scope: !7, file: !1, line: 18, type: !11)
!15 = !DILocation(line: 18, column: 22, scope: !7)
!16 = !DILocation(line: 18, column: 33, scope: !7)
!17 = !DILocation(line: 24, column: 6, scope: !18)
!18 = distinct !DILexicalBlock(scope: !7, file: !1, line: 24, column: 5)
!19 = !DILocation(line: 24, column: 26, scope: !18)
!20 = !DILocation(line: 24, column: 19, scope: !18)
!21 = !DILocation(line: 25, column: 10, scope: !7)
!22 = !DILocation(line: 26, column: 6, scope: !23)
!23 = distinct !DILexicalBlock(scope: !7, file: !1, line: 26, column: 5)
!24 = !DILocation(line: 26, column: 26, scope: !23)
!25 = !DILocation(line: 26, column: 19, scope: !23)
!26 = !DILocation(line: 26, column: 33, scope: !23)
!27 = !DILocation(line: 26, column: 39, scope: !23)
!28 = !{!29, !29, i64 0}
!29 = !{!"int", !30, i64 0}
!30 = !{!"omnipotent char", !31, i64 0}
!31 = !{!"Simple C/C++ TBAA"}
!32 = !DILocation(line: 0, scope: !7)
!33 = !DILocation(line: 28, column: 1, scope: !7)

The IR compiled by clang11 is as following.

; ModuleID = 's_ldexp.llvm11.O3.bc'
source_filename = "s_ldexp.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: nounwind uwtable
define dso_local double @ldexp(double %0, i32 %1) local_unnamed_addr #0 !dbg !7 {
  call void @llvm.dbg.value(metadata double %0, metadata !13, metadata !DIExpression()), !dbg !15
  call void @llvm.dbg.value(metadata i32 %1, metadata !14, metadata !DIExpression()), !dbg !15
  %3 = tail call double @llvm.fabs.f64(double %0) #4, !dbg !16
  %4 = fcmp ueq double %3, 0x7FF0000000000000, !dbg !16
  %5 = fcmp oeq double %0, 0.000000e+00
  %6 = or i1 %5, %4, !dbg !18
  br i1 %6, label %15, label %7, !dbg !18

7:                                                ; preds = %2
  %8 = tail call double @scalbn(double %0, i32 %1) #5, !dbg !19
  call void @llvm.dbg.value(metadata double %8, metadata !13, metadata !DIExpression()), !dbg !15
  %9 = tail call double @llvm.fabs.f64(double %8) #4, !dbg !20
  %10 = fcmp ueq double %9, 0x7FF0000000000000, !dbg !20
  %11 = fcmp oeq double %8, 0.000000e+00
  %12 = or i1 %11, %10, !dbg !22
  br i1 %12, label %13, label %15, !dbg !22

13:                                               ; preds = %7
  %14 = tail call i32* @__errno_location() #6, !dbg !23
  store i32 34, i32* %14, align 4, !dbg !24, !tbaa !25
  br label %15, !dbg !23

15:                                               ; preds = %13, %7, %2
  %16 = phi double [ %0, %2 ], [ %8, %7 ], [ %8, %13 ], !dbg !15
  ret double %16, !dbg !29
}

; Function Attrs: nounwind readnone speculatable willreturn
declare double @llvm.fabs.f64(double) #1

; Function Attrs: nounwind
declare dso_local double @scalbn(double, i32) local_unnamed_addr #2

; Function Attrs: nounwind readnone
declare dso_local i32* @__errno_location() local_unnamed_addr #3

; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata) #1

attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable willreturn }
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { readnone }
attributes #5 = { nounwind }
attributes #6 = { nounwind readnone }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0160ad802e899c2922bc9b29564080c22eb0908c)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "s_ldexp.c", directory: "/root/test/ficl/fdlibm53")
!2 = !{}
!3 = !{i32 7, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0160ad802e899c2922bc9b29564080c22eb0908c)"}
!7 = distinct !DISubprogram(name: "ldexp", scope: !1, file: !1, line: 18, type: !8, scopeLine: 23, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !11}
!10 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !{!13, !14}
!13 = !DILocalVariable(name: "value", arg: 1, scope: !7, file: !1, line: 18, type: !10)
!14 = !DILocalVariable(name: "exp", arg: 2, scope: !7, file: !1, line: 18, type: !11)
!15 = !DILocation(line: 0, scope: !7)
!16 = !DILocation(line: 24, column: 6, scope: !17)
!17 = distinct !DILexicalBlock(scope: !7, file: !1, line: 24, column: 5)
!18 = !DILocation(line: 24, column: 19, scope: !17)
!19 = !DILocation(line: 25, column: 10, scope: !7)
!20 = !DILocation(line: 26, column: 6, scope: !21)
!21 = distinct !DILexicalBlock(scope: !7, file: !1, line: 26, column: 5)
!22 = !DILocation(line: 26, column: 19, scope: !21)
!23 = !DILocation(line: 26, column: 33, scope: !21)
!24 = !DILocation(line: 26, column: 39, scope: !21)
!25 = !{!26, !26, i64 0}
!26 = !{!"int", !27, i64 0}
!27 = !{!"omnipotent char", !28, i64 0}
!28 = !{!"Simple C/C++ TBAA"}
!29 = !DILocation(line: 28, column: 1, scope: !7)

In the s_ldexp.c, there one code snippet value==0.0. The corresponding IR compiled by clang8 is %5 = fcmp oeq double %0, 0.000000e+00, !dbg !19. The corresponding IR compiled by clang11 is %5 = fcmp oeq double %0, 0.000000e+00. Compared with clang8, the debug info !dbg !19 is missing in the corresponding IR compiled by clang11. The debug info is important to me. I hope that LLVM11 can reserve the debug info. For LLVM11, there are there methods that can solve the problem.

The first method is to run these two commands.

clang -emit-llvm -O3 -D_IEEE_LIBM -Wall -Wuninitialized -c s_ldexp.c -o s_ldexp.bc
opt -enable-debugify s_ldexp.bc -o s_ldexp.debug.bc

When using Clang to compile source code, -g parameter is not used. And we can use LLVM opt command to attach debug info into the IR.

The second method is to run this command.

clang -emit-llvm -g -O0 -D_IEEE_LIBM -Wall -Wuninitialized -c s_ldexp.c -o s_ldexp.bc

The command is to lower the optimization level of compiler and keep as much debug info as possible.

The third method is to modify the C source code. The code after modification is as following.

#include "fdlibm.h"
#include <errno.h>
#include <stdbool.h>

#ifdef __STDC__
    double ldexp(double value, int exp)
#else
    double ldexp(value, exp)
    double value; int exp;
#endif
{
    bool flag1 = (!finite(value)) || (value==0.0);
    if(flag1)  return value;
    value = scalbn(value,exp);
    bool flag2 = (!finite(value)) || (value==0.0);
    if(flag2) errno = ERANGE;
    return value;
}

We can get around this problem using the third method.

Though these three method can solve this problem, I still have two questions. The first question is why the debug info is missing in the IR compiled by clang11. The second question is that is there any option or LLVM pass can check whether some debug info is missing and complement the missing debug info.

llvmbot commented 1 year ago

@llvm/issue-subscribers-debuginfo

EugeneZelenko commented 1 year ago

Both versions are too old. Could you please try 16 release candidate or main branch? https://godbolt.org should be helpful.

2994186010 commented 1 year ago

@llvmbot @EugeneZelenko , about this problem, I want to tell you some interesting attempts. First, I use the following code to make some tests in https://godbolt.org/.

double sum(double value, int exp) {
    if(value==1.0||value==0.0) return value;
    return 3.0;
}

The main branch should be x86-64 clang(trunk). I use x86-64 clang(trunk) as the compiler. The debug info of "value==0.0" is still missing. I try other clang releases. The debug info is not missing when using clang8, clang9 and clang10. The debug info is missing when using clang11, clang12, clang13, clang14 and clang15. The corresponding tests is in the above short links. The source code is considered as C++ for the compiler. One test is as following. compiler_c++

Strangely, The debug info is not missing when the source code is considered as C for the compiler. The corresponding test is as following. compiler_c

I have one question. Can I get the detailed compilation command in https://godbolt.org/. In my local clang compilation environment, I can only get the same compilation result as https://godbolt.org/ when https://godbolt.org/ consider the source code as C++.

I make one test using clang11 and find that the simplify pass throws down the debug info.

/root/apps/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04/bin/clang -emit-llvm -g -O3 -c s_ldexp.c -mllvm -print-after-all >& clang.log

At last, I hope that I can get some useful tips to help me fix this bug. Certainly, I would appreciate it if someone can fix the bug.

jmorse commented 1 year ago

This replicates with eb8fcc1e835ac, which is only a month old -- however, I believe the source location is being dropped deliberately, and it's the correct behaviour in this situation.

The SimplifyCFG pass chooses to speculatively execute the second part of each "if" condition to reduce the amount of branching, and folds the relevant instructions into the same block as the first condition, hence you get two fcmp's together. The updating-debug-info guide [0] says that when this happens, we need to drop the source location, so that debuggers don't appear to step through lines that are never actually reached. This is designed for the scenario where the whole body of an "if" condition gets speculated, unfortunately your code sample matches that pattern in a different way.

We can handle this situation better, and there's a general interest in LLVM for separating out the attribution of where-an-instruction-came-from from what-to-step-through, however that isn't likely to happen very soon, sorry.

[0] https://llvm.org/docs/HowToUpdateDebugInfo.html

2994186010 commented 1 year ago

@jmorse , I don't agree with you. When change the optimization level from "-O3" to "-O0", the debug info is not missing as the following. trunk_O0

And the debug info is not missing when the source code is considered as C for the compiler as the following. clang_C

jmorse commented 1 year ago

Indeed, at O0 almost everything about the source program will be retained, as no optimisations will be applied to the LLVM-IR program. It's the transformations that happen to the program during optimisation destroy the relationship between the source program and the optimised one.

Following both examples in your screenshots, the fcmp in question has stayed in it's own conditionally executed block, therefore there has been no need to remove that information.

dwblaikie commented 1 year ago

(links to godbolt rather than/or in addition to screenshots is probably good)

But yeah, everything @jmorse said - this looks correct to me. Optimizations made conditional code unconditional and so the location was removed so as not to confuse users or profilers.