; NOTE: Code has been autogenerated by utils/update_test_body.py
; REQUIRES: aarch64

; RUN: rm -rf %t && split-file %s %t

; RUN: llc -filetype=obj %t/a.ll -O3 -o %t/a.o -enable-machine-outliner=never -mtriple arm64-apple-macos -addrsig
; RUN: %lld -arch arm64 -lSystem --icf=safe_thunks -dylib -o %t/a.dylib %t/a.o

;;; Check that we generate valid dSYM
; RUN: dsymutil %t/a.dylib -o %t/a.dSYM
; RUN: llvm-dwarfdump --verify %t/a.dSYM | FileCheck %s --check-prefix=VERIFY-DSYM
; VERIFY-DSYM: No errors.

;;; Check that we don't generate STABS entries (N_FUN) for ICF'ed function thunks
; RUN: dsymutil -s %t/a.dylib | FileCheck %s --check-prefix=VERIFY-STABS
; VERIFY-STABS-NOT:  N_FUN{{.*}}_func_B
; VERIFY-STABS-NOT:  N_FUN{{.*}}_func_C

;;; Check that we do generate STABS entries (N_FUN) for non-ICF'ed functions
; VERIFY-STABS:  N_FUN{{.*}}_func_A
; VERIFY-STABS:  N_FUN{{.*}}_take_func_addr

;;;;;;;;;;;;;;;;;;;;;;;;;;;; Check safe_thunks ICF + keeping STABS entries ;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Check scenario with where we do safe_thunks ICF and also generate STABS entries
; RUN: %lld -arch arm64 -lSystem --icf=safe_thunks --keep-icf-stabs -dylib -o %t/a_thunks.dylib %t/a.o
; RUN: dsymutil -s %t/a_thunks.dylib > %t/a_thunks.txt


; RUN: dsymutil --flat --verify-dwarf=none %t/a_thunks.dylib -o %t/a_thunks.dSYM
; RUN: dsymutil -s %t/a_thunks.dSYM >> %t/a_thunks.txt
; RUN: llvm-dwarfdump -a %t/a_thunks.dSYM >> %t/a_thunks.txt

; RUN: cat %t/a_thunks.txt | FileCheck %s --check-prefix=VERIFY-THUNK

# VERIFY-THUNK-LABEL: Symbol table for: '{{.*}}/a_thunks.dylib'
# Capture the 'n_value's for N_FUN entries of _func_A, _func_B, and _func_C
# VERIFY-THUNK:  [[MERGED_FUN_ADDR:[0-9a-f]+]] '_func_A'
# VERIFY-THUNK:  [[MERGED_FUN_ADDR]] '_func_B'
# VERIFY-THUNK:  [[MERGED_FUN_ADDR]] '_func_C'

# Capture the 'n_value's for SECT EXT entries in the first part
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_A_NVALUE:[0-9a-f]+]] '_func_A'
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_B_NVALUE:[0-9a-f]+]] '_func_B'
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_C_NVALUE:[0-9a-f]+]] '_func_C'

# VERIFY-THUNK: ----------------------------------------------------------------------
# VERIFY-THUNK-LABEL: Symbol table for: '{{.*}}/a_thunks.dSYM'

# Verify that the SECT EXT 'n_value's in the second part match the first part
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_A_NVALUE]] '_func_A'
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_B_NVALUE]] '_func_B'
# VERIFY-THUNK: SECT EXT{{.*}} [[SECT_EXT_C_NVALUE]] '_func_C'

# Ensure that N_FUN 'n_value's match the DW_TAG_subprogram's DW_AT_low_pc
# and that the DW_AT_name is at a specific relative position

# VERIFY-THUNK-LABEL: .debug_info contents:
# VERIFY-THUNK: Compile Unit: length = {{.*}}

# Match the subprogram for func_A
# VERIFY-THUNK: :   DW_TAG_subprogram
# VERIFY-THUNK-NEXT: {{ +}}DW_AT_low_pc	(0x[[MERGED_FUN_ADDR]])
# VERIFY-THUNK-NEXT-NEXT-NEXT-NEXT-NEXT: {{ +}}DW_AT_name	("func_A")

# Match the subprogram for func_B
# VERIFY-THUNK: :   DW_TAG_subprogram
# VERIFY-THUNK-NEXT: {{ +}}DW_AT_low_pc	(0x[[MERGED_FUN_ADDR]])
# VERIFY-THUNK-NEXT-NEXT-NEXT-NEXT-NEXT: {{ +}}DW_AT_name	("func_B")

# Match the subprogram for func_C
# VERIFY-THUNK: :   DW_TAG_subprogram
# VERIFY-THUNK-NEXT: {{ +}}DW_AT_low_pc	(0x[[MERGED_FUN_ADDR]])
# VERIFY-THUNK-NEXT-NEXT-NEXT-NEXT-NEXT: {{ +}}DW_AT_name	("func_C")

;--- a.cpp
#define ATTR __attribute__((noinline)) extern "C"
typedef unsigned long long ULL;

ATTR int func_A() { return 1; }
ATTR int func_B() { return 1; }
ATTR int func_C() { return 1; }

ATTR ULL take_func_addr() {
    ULL val = 0;
    val += (ULL)(void*)func_A;
    val += (ULL)(void*)func_B;
    val += (ULL)(void*)func_C;
    return val;
}

;--- gen
clang -target arm64-apple-macos11.0 -S -emit-llvm a.cpp -O3 -g -o -

;--- a.ll
; ModuleID = 'a.cpp'
source_filename = "a.cpp"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "arm64-apple-macosx11.0.0"

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind ssp willreturn memory(none) uwtable(sync)
define noundef i32 @func_A() #0 !dbg !12 {
  ret i32 1, !dbg !16
}

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind ssp willreturn memory(none) uwtable(sync)
define noundef i32 @func_B() #0 !dbg !17 {
  ret i32 1, !dbg !18
}

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind ssp willreturn memory(none) uwtable(sync)
define noundef i32 @func_C() #0 !dbg !19 {
  ret i32 1, !dbg !20
}

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind ssp willreturn memory(none) uwtable(sync)
define noundef i64 @take_func_addr() local_unnamed_addr #0 !dbg !21 {
    #dbg_value(i64 0, !25, !DIExpression(), !26)
    #dbg_value(i64 ptrtoint (ptr @func_A to i64), !25, !DIExpression(), !26)
    #dbg_value(i64 add (i64 ptrtoint (ptr @func_A to i64), i64 ptrtoint (ptr @func_B to i64)), !25, !DIExpression(), !26)
    #dbg_value(i64 add (i64 add (i64 ptrtoint (ptr @func_A to i64), i64 ptrtoint (ptr @func_B to i64)), i64 ptrtoint (ptr @func_C to i64)), !25, !DIExpression(), !26)
  ret i64 add (i64 add (i64 ptrtoint (ptr @func_A to i64), i64 ptrtoint (ptr @func_B to i64)), i64 ptrtoint (ptr @func_C to i64)), !dbg !27
}

attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind ssp willreturn memory(none) uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+altnzcv,+ccdp,+ccidx,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a,+zcm,+zcz" }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!6, !7, !8, !9, !10, !11}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
!1 = !DIFile(filename: "a.cpp", directory: "/proc/self/cwd")
!2 = !{!3, !5}
!3 = !DIDerivedType(tag: DW_TAG_typedef, name: "ULL", file: !1, line: 2, baseType: !4)
!4 = !DIBasicType(name: "unsigned long long", size: 64, encoding: DW_ATE_unsigned)
!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
!6 = !{i32 7, !"Dwarf Version", i32 4}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{i32 1, !"wchar_size", i32 4}
!9 = !{i32 8, !"PIC Level", i32 2}
!10 = !{i32 7, !"uwtable", i32 1}
!11 = !{i32 7, !"frame-pointer", i32 1}
!12 = distinct !DISubprogram(name: "func_A", scope: !1, file: !1, line: 4, type: !13, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!13 = !DISubroutineType(types: !14)
!14 = !{!15}
!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!16 = !DILocation(line: 4, column: 21, scope: !12)
!17 = distinct !DISubprogram(name: "func_B", scope: !1, file: !1, line: 5, type: !13, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!18 = !DILocation(line: 5, column: 21, scope: !17)
!19 = distinct !DISubprogram(name: "func_C", scope: !1, file: !1, line: 6, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!20 = !DILocation(line: 6, column: 21, scope: !19)
!21 = distinct !DISubprogram(name: "take_func_addr", scope: !1, file: !1, line: 8, type: !22, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !24)
!22 = !DISubroutineType(types: !23)
!23 = !{!3}
!24 = !{!25}
!25 = !DILocalVariable(name: "val", scope: !21, file: !1, line: 9, type: !3)
!26 = !DILocation(line: 0, scope: !21)
!27 = !DILocation(line: 13, column: 5, scope: !21)
