## Test that BOLT correctly handles debug line information for functions ## that belong to multiple compilation units (e.g., inline functions in ## common header files). This is the assembly version of the multi-cu-debug-line.test. ## The test covers two scenarios: ## 1. Normal processing: .debug_line section shows lines for the function ## in all CUs where it was compiled, with no duplicate rows within CUs ## 2. Functions not processed: When BOLT doesn't process functions (using ## --funcs with nonexistent function), original debug info is preserved # REQUIRES: system-linux # RUN: split-file %s %t # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file1.s -o %t/multi-cu-file1.o # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/multi-cu-file2.s -o %t/multi-cu-file2.o # RUN: %clang %cflags %t/multi-cu-file1.o %t/multi-cu-file2.o -o %t.exe -Wl,-q ## Test 1: Normal BOLT processing (functions are processed/optimized) # RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections # RUN: llvm-dwarfdump --debug-line %t.bolt > %t.debug-line.txt # RUN: FileCheck %s --check-prefix=BASIC --input-file %t.debug-line.txt ## Check that debug line information is present for both compilation units # BASIC: debug_line[{{.*}}] # BASIC: file_names[{{.*}}]: # BASIC: name: "{{.*}}multi-cu-file1.c" # BASIC: debug_line[{{.*}}] # BASIC: file_names[{{.*}}]: # BASIC: name: "{{.*}}multi-cu-file2.c" ## Use our helper script to create a normalized table without addresses # RUN: process-debug-line %t.debug-line.txt > %t.normalized-debug-line.txt # RUN: FileCheck %s --check-prefix=NORMALIZED --input-file %t.normalized-debug-line.txt ## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h ## in both compilation units # NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h # NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h # NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h # NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h # NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h # NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h ## Verify that we have line entries for the inline function in multiple CUs ## by checking that the header file appears multiple times in different contexts # RUN: grep -c "multi-cu-common.h" %t.debug-line.txt > %t.header-count.txt # RUN: FileCheck %s --check-prefix=MULTI-CU --input-file %t.header-count.txt ## The header should appear in debug line info for multiple CUs # MULTI-CU: {{[2-9]|[1-9][0-9]+}} ## Check that there are no duplicate line table rows within the same CU ## This verifies the fix for the bug where duplicate entries were created # RUN: sort %t.normalized-debug-line.txt | uniq -c | \ # RUN: awk '$1 > 1 {print "DUPLICATE_ROW: " $0}' > %t.duplicates.txt # RUN: FileCheck %s --check-prefix=NO-DUPLICATES --input-file %t.duplicates.txt --allow-empty ## Should have no duplicate normalized rows (file should be empty) ## Note: Cross-CU duplicates are expected and valid (same function in different CUs) ## but within-CU duplicates would indicate a bug # NO-DUPLICATES-NOT: DUPLICATE_ROW ## Test 2: Functions not processed by BOLT (using --funcs with nonexistent function) ## This tests the code path where BOLT preserves original debug info # RUN: llvm-bolt %t.exe -o %t.not-emitted.bolt --update-debug-sections --funcs=nonexistent_function # RUN: llvm-dwarfdump --debug-line %t.not-emitted.bolt > %t.not-emitted.debug-line.txt # RUN: FileCheck %s --check-prefix=PRESERVED-BASIC --input-file %t.not-emitted.debug-line.txt ## Check that debug line information is still present for both compilation units when functions aren't processed # PRESERVED-BASIC: debug_line[{{.*}}] # PRESERVED-BASIC: file_names[{{.*}}]: # PRESERVED-BASIC: name: "{{.*}}multi-cu-file1.c" # PRESERVED-BASIC: debug_line[{{.*}}] # PRESERVED-BASIC: file_names[{{.*}}]: # PRESERVED-BASIC: name: "{{.*}}multi-cu-file2.c" ## Create normalized output for the not-emitted case # RUN: process-debug-line %t.not-emitted.debug-line.txt > %t.not-emitted.normalized.txt # RUN: FileCheck %s --check-prefix=PRESERVED-NORMALIZED --input-file %t.not-emitted.normalized.txt ## Check that we have line entries for the inline function (lines 5, 6, 7) from multi-cu-common.h ## in both compilation units (preserved from original) # PRESERVED-NORMALIZED: multi-cu-file1.c 5 {{[0-9]+}} multi-cu-common.h # PRESERVED-NORMALIZED: multi-cu-file1.c 6 {{[0-9]+}} multi-cu-common.h # PRESERVED-NORMALIZED: multi-cu-file1.c 7 {{[0-9]+}} multi-cu-common.h # PRESERVED-NORMALIZED: multi-cu-file2.c 5 {{[0-9]+}} multi-cu-common.h # PRESERVED-NORMALIZED: multi-cu-file2.c 6 {{[0-9]+}} multi-cu-common.h # PRESERVED-NORMALIZED: multi-cu-file2.c 7 {{[0-9]+}} multi-cu-common.h ## Verify that we have line entries for the inline function in multiple CUs (preserved) ## by checking that the header file appears multiple times in different contexts # RUN: grep -c "multi-cu-common.h" %t.not-emitted.debug-line.txt > %t.preserved-header-count.txt # RUN: FileCheck %s --check-prefix=PRESERVED-MULTI-CU --input-file %t.preserved-header-count.txt ## The header should appear in debug line info for multiple CUs (preserved from original) # PRESERVED-MULTI-CU: {{[2-9]|[1-9][0-9]+}} ## Check that original debug info is preserved for main functions # RUN: grep "multi-cu-file1.c.*multi-cu-file1.c" %t.not-emitted.normalized.txt > %t.preserved-main.txt # RUN: FileCheck %s --check-prefix=PRESERVED-MAIN --input-file %t.preserved-main.txt # PRESERVED-MAIN: multi-cu-file1.c {{[0-9]+}} {{[0-9]+}} multi-cu-file1.c ## Check that original debug info is preserved for file2 functions # RUN: grep "multi-cu-file2.c.*multi-cu-file2.c" %t.not-emitted.normalized.txt > %t.preserved-file2.txt # RUN: FileCheck %s --check-prefix=PRESERVED-FILE2 --input-file %t.preserved-file2.txt # PRESERVED-FILE2: multi-cu-file2.c {{[0-9]+}} {{[0-9]+}} multi-cu-file2.c ;--- multi-cu-file1.s .text .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file1.c" .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h" .globl main .type main,@function main: .Lfunc_begin0: .loc 1 4 0 callq common_inline_function .loc 1 8 0 retq .Lfunc_end0: .size main, .Lfunc_end0-main .type common_inline_function,@function common_inline_function: .Lfunc_begin1: .loc 2 5 0 movl $42, %eax .loc 2 6 0 addl $10, %eax .loc 2 7 0 retq .Lfunc_end1: .size common_inline_function, .Lfunc_end1-common_inline_function .section .debug_abbrev,"",@progbits .byte 1 # Abbreviation Code .byte 17 # DW_TAG_compile_unit .byte 1 # DW_CHILDREN_yes .byte 37 # DW_AT_producer .byte 14 # DW_FORM_strp .byte 19 # DW_AT_language .byte 5 # DW_FORM_data2 .byte 3 # DW_AT_name .byte 14 # DW_FORM_strp .byte 16 # DW_AT_stmt_list .byte 23 # DW_FORM_sec_offset .byte 27 # DW_AT_comp_dir .byte 14 # DW_FORM_strp .byte 17 # DW_AT_low_pc .byte 1 # DW_FORM_addr .byte 18 # DW_AT_high_pc .byte 6 # DW_FORM_data4 .byte 0 # EOM(1) .byte 0 # EOM(2) .byte 2 # Abbreviation Code .byte 46 # DW_TAG_subprogram .byte 0 # DW_CHILDREN_no .byte 17 # DW_AT_low_pc .byte 1 # DW_FORM_addr .byte 18 # DW_AT_high_pc .byte 6 # DW_FORM_data4 .byte 3 # DW_AT_name .byte 14 # DW_FORM_strp .byte 58 # DW_AT_decl_file .byte 11 # DW_FORM_data1 .byte 59 # DW_AT_decl_line .byte 11 # DW_FORM_data1 .byte 0 # EOM(1) .byte 0 # EOM(2) .byte 0 # EOM(3) .section .debug_info,"",@progbits .Lcu_begin0: .long .Ldebug_info_end0-.Ldebug_info_start0 .Ldebug_info_start0: .short 4 # DWARF version number .long .debug_abbrev # Offset Into Abbrev. Section .byte 8 # Address Size (in bytes) .byte 1 # Abbrev [1] 0xb:0x30 DW_TAG_compile_unit .long .Linfo_string0 # DW_AT_producer .short 29 # DW_AT_language .long .Linfo_string1 # DW_AT_name .long .Lline_table_start0 # DW_AT_stmt_list .long .Linfo_string2 # DW_AT_comp_dir .quad .Lfunc_begin0 # DW_AT_low_pc .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc .byte 2 # Abbrev [2] 0x2a:0x10 DW_TAG_subprogram .quad .Lfunc_begin0 # DW_AT_low_pc .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc .long .Linfo_string3 # DW_AT_name .byte 1 # DW_AT_decl_file .byte 4 # DW_AT_decl_line .byte 2 # Abbrev [2] 0x3a:0x10 DW_TAG_subprogram .quad .Lfunc_begin1 # DW_AT_low_pc .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc .long .Linfo_string4 # DW_AT_name .byte 2 # DW_AT_decl_file .byte 5 # DW_AT_decl_line .byte 0 # End Of Children Mark .Ldebug_info_end0: .section .debug_str,"MS",@progbits,1 .Linfo_string0: .asciz "clang version 18.0.0" .Linfo_string1: .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file1.c" .Linfo_string2: .asciz "/repo/llvm-project" .Linfo_string3: .asciz "main" .Linfo_string4: .asciz "common_inline_function" .section .debug_line,"",@progbits .Lline_table_start0: ;--- multi-cu-file2.s .text .file 1 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-file2.c" .file 2 "/repo/llvm-project" "bolt/test/Inputs/multi-cu-common.h" .globl helper_function .type helper_function,@function helper_function: .Lfunc_begin0: .loc 1 4 0 callq common_inline_function .loc 1 8 0 retq .Lfunc_end0: .size helper_function, .Lfunc_end0-helper_function .type common_inline_function,@function common_inline_function: .Lfunc_begin1: .loc 2 5 0 movl $42, %eax .loc 2 6 0 addl $10, %eax .loc 2 7 0 retq .Lfunc_end1: .size common_inline_function, .Lfunc_end1-common_inline_function .section .debug_abbrev,"",@progbits .byte 1 # Abbreviation Code .byte 17 # DW_TAG_compile_unit .byte 1 # DW_CHILDREN_yes .byte 37 # DW_AT_producer .byte 14 # DW_FORM_strp .byte 19 # DW_AT_language .byte 5 # DW_FORM_data2 .byte 3 # DW_AT_name .byte 14 # DW_FORM_strp .byte 16 # DW_AT_stmt_list .byte 23 # DW_FORM_sec_offset .byte 27 # DW_AT_comp_dir .byte 14 # DW_FORM_strp .byte 17 # DW_AT_low_pc .byte 1 # DW_FORM_addr .byte 18 # DW_AT_high_pc .byte 6 # DW_FORM_data4 .byte 0 # EOM(1) .byte 0 # EOM(2) .byte 2 # Abbreviation Code .byte 46 # DW_TAG_subprogram .byte 0 # DW_CHILDREN_no .byte 17 # DW_AT_low_pc .byte 1 # DW_FORM_addr .byte 18 # DW_AT_high_pc .byte 6 # DW_FORM_data4 .byte 3 # DW_AT_name .byte 14 # DW_FORM_strp .byte 58 # DW_AT_decl_file .byte 11 # DW_FORM_data1 .byte 59 # DW_AT_decl_line .byte 11 # DW_FORM_data1 .byte 0 # EOM(1) .byte 0 # EOM(2) .byte 0 # EOM(3) .section .debug_info,"",@progbits .Lcu_begin0: .long .Ldebug_info_end0-.Ldebug_info_start0 .Ldebug_info_start0: .short 4 # DWARF version number .long .debug_abbrev # Offset Into Abbrev. Section .byte 8 # Address Size (in bytes) .byte 1 # Abbrev [1] 0xb:0x30 DW_TAG_compile_unit .long .Linfo_string0 # DW_AT_producer .short 29 # DW_AT_language .long .Linfo_string1 # DW_AT_name .long .Lline_table_start0 # DW_AT_stmt_list .long .Linfo_string2 # DW_AT_comp_dir .quad .Lfunc_begin0 # DW_AT_low_pc .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc .byte 2 # Abbrev [2] 0x2a:0x10 DW_TAG_subprogram .quad .Lfunc_begin0 # DW_AT_low_pc .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc .long .Linfo_string3 # DW_AT_name .byte 1 # DW_AT_decl_file .byte 4 # DW_AT_decl_line .byte 2 # Abbrev [2] 0x3a:0x10 DW_TAG_subprogram .quad .Lfunc_begin1 # DW_AT_low_pc .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc .long .Linfo_string4 # DW_AT_name .byte 2 # DW_AT_decl_file .byte 5 # DW_AT_decl_line .byte 0 # End Of Children Mark .Ldebug_info_end0: .section .debug_str,"MS",@progbits,1 .Linfo_string0: .asciz "clang version 18.0.0" .Linfo_string1: .asciz "/repo/llvm-project/bolt/test/Inputs/multi-cu-file2.c" .Linfo_string2: .asciz "/repo/llvm-project" .Linfo_string3: .asciz "helper_function" .Linfo_string4: .asciz "common_inline_function" .section .debug_line,"",@progbits .Lline_table_start0: