276 lines
16 KiB
LLVM
276 lines
16 KiB
LLVM
; RUN: opt %s -passes=sroa -S | FileCheck %s --check-prefixes=COMMON,OLD
|
|
; RUN: opt %s -passes=declare-to-assign,sroa -S | FileCheck %s --check-prefixes=COMMON,NEW
|
|
|
|
;; C++17 source:
|
|
;; struct two { int a, b; } gt;
|
|
;; int fun1() {
|
|
;; auto [x, y] = gt;
|
|
;; return x + y;
|
|
;; }
|
|
;;
|
|
;; struct four { two a, b; } gf;
|
|
;; int fun2() {
|
|
;; auto [x, y] = gf;
|
|
;; return x.a + y.b;
|
|
;; }
|
|
;; Plus some hand-written IR.
|
|
;;
|
|
;; Check that SROA understands how to split dbg.declares and dbg.assigns with
|
|
;; offsets into their storge (e.g., the second variable in a structured binding
|
|
;; is stored at an offset into the shared alloca).
|
|
;;
|
|
;; Additional notes:
|
|
;; We expect the same dbg.value intrinsics to come out of SROA whether assignment
|
|
;; tracking is enabled or not. However, the order of the debug intrinsics may
|
|
;; differ, and assignment tracking replaces some dbg.declares with dbg.assigns.
|
|
;;
|
|
;; Structured bindings produce an artificial variable that covers the entire
|
|
;; alloca. Although they add clutter to the test, they've been preserved in
|
|
;; order to increase coverage. These use the placehold name 'A' in comments and
|
|
;; checks.
|
|
|
|
%struct.two = type { i32, i32 }
|
|
%struct.four = type { %struct.two, %struct.two }
|
|
|
|
@gt = dso_local global %struct.two zeroinitializer, align 4, !dbg !0
|
|
@gf = dso_local global %struct.four zeroinitializer, align 4, !dbg !5
|
|
|
|
|
|
; COMMON-LABEL: @_Z4fun1v
|
|
; COMMON-NEXT: entry
|
|
;; 32 bit variable x (!27): value a_reg.
|
|
;;
|
|
;; 32 bit variable y (!28): value b_reg.
|
|
;;
|
|
;; 64 bit variable A (!29) bits [0, 32): value a_reg.
|
|
;; 64 bit variable A (!29) bits [32, 64): value b_reg.
|
|
|
|
; OLD-NEXT: %[[a_reg:.*]] = load i32, ptr @gt
|
|
; OLD-NEXT: #dbg_value(i32 %[[a_reg]], ![[x0:[0-9]+]], !DIExpression(),
|
|
; OLD-NEXT: #dbg_value(i32 %[[a_reg]], ![[A0:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
; OLD-NEXT: %[[b_reg:.*]] = load i32, ptr getelementptr inbounds (i8, ptr @gt, i64 4)
|
|
; OLD-NEXT: #dbg_value(i32 %[[b_reg]], ![[y0:[0-9]+]], !DIExpression(),
|
|
; OLD-NEXT: #dbg_value(i32 %[[b_reg]], ![[A0]], !DIExpression(DW_OP_LLVM_fragment, 32, 32),
|
|
|
|
; NEW-NEXT: %[[a_reg:.*]] = load i32, ptr @gt
|
|
; NEW-NEXT: %[[b_reg:.*]] = load i32, ptr getelementptr inbounds (i8, ptr @gt, i64 4)
|
|
; NEW-NEXT: #dbg_value(i32 %[[b_reg]], ![[y0:[0-9]+]], !DIExpression(),
|
|
; NEW-NEXT: #dbg_value(i32 %[[a_reg]], ![[A0:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
; NEW-NEXT: #dbg_value(i32 %[[b_reg]], ![[A0]], !DIExpression(DW_OP_LLVM_fragment, 32, 32),
|
|
; NEW-NEXT: #dbg_value(i32 %[[a_reg]], ![[x0:[0-9]+]], !DIExpression(),
|
|
define dso_local noundef i32 @_Z4fun1v() #0 !dbg !23 {
|
|
entry:
|
|
%0 = alloca %struct.two, align 4
|
|
#dbg_declare(ptr %0, !27, !DIExpression(), !31)
|
|
#dbg_declare(ptr %0, !28, !DIExpression(DW_OP_plus_uconst, 4), !31)
|
|
#dbg_declare(ptr %0, !29, !DIExpression(), !31)
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %0, ptr align 4 @gt, i64 8, i1 false), !dbg !31
|
|
%a = getelementptr inbounds %struct.two, ptr %0, i32 0, i32 0, !dbg !31
|
|
%1 = load i32, ptr %a, align 4, !dbg !31
|
|
%b = getelementptr inbounds %struct.two, ptr %0, i32 0, i32 1, !dbg !31
|
|
%2 = load i32, ptr %b, align 4, !dbg !31
|
|
%add = add nsw i32 %1, %2, !dbg !31
|
|
ret i32 %add, !dbg !31
|
|
}
|
|
|
|
; COMMON-LABEL: _Z4fun2v()
|
|
; COMMON-NEXT: entry:
|
|
;; 64 bit variable x (!50) bits [0, 32): value aa_reg.
|
|
;; 64 bit variable x (!50) bits [32, 64): deref ab_ba_addr
|
|
;;
|
|
;; 64 bit variable y (!51) bits [0, 32): deref ab_ba_addr + 32
|
|
;; 64 bit variable y (!51) bits [32, 64): value bb_reg.
|
|
;;
|
|
;; 128 bit variable A (!52) bits [0, 32): value aa_reg
|
|
;; 128 bit variable A (!52) bits [32, 64): deref ab_ba_addr
|
|
;; 128 bit variable A (!52) bits [96, 128): value bb_reg
|
|
;;
|
|
;; NOTE: This 8 byte alloca contains x.b (4 bytes) and y.a (4 bytes).
|
|
; COMMON-NEXT: %[[ab_ba_addr:.*]] = alloca [8 x i8], align 4
|
|
; OLD-NEXT: #dbg_declare(ptr %[[ab_ba_addr]], ![[A1:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 32, 64),
|
|
; OLD-NEXT: #dbg_declare(ptr %[[ab_ba_addr]], ![[y1:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_LLVM_fragment, 0, 32),
|
|
; OLD-NEXT: #dbg_declare(ptr %[[ab_ba_addr]], ![[x1:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 32, 32),
|
|
; OLD-NEXT: %[[aa_reg:.*]] = load i32, ptr @gf, align 4
|
|
; OLD-NEXT: #dbg_value(i32 %[[aa_reg]], ![[x1]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
; OLD-NEXT: #dbg_value(i32 %[[aa_reg]], ![[A1]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
; OLD-NEXT: call void @llvm.memcpy{{.*}}(ptr align 4 %[[ab_ba_addr]], ptr align 4 getelementptr inbounds (i8, ptr @gf, i64 4), i64 8, i1 false)
|
|
; OLD-NEXT: %[[bb_reg:.*]] = load i32, ptr getelementptr inbounds (i8, ptr @gf, i64 12), align 4
|
|
; OLD-NEXT: #dbg_value(i32 %[[bb_reg]], ![[y1]], !DIExpression(DW_OP_LLVM_fragment, 32, 32),
|
|
; OLD-NEXT: #dbg_value(i32 %[[bb_reg]], ![[A1]], !DIExpression(DW_OP_LLVM_fragment, 96, 32),
|
|
|
|
; NEW-NEXT: #dbg_assign(i1 poison, ![[x1:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 32, 32), ![[#]], ptr %[[ab_ba_addr]], !DIExpression(),
|
|
; NEW-NEXT: #dbg_assign(i1 poison, ![[A1:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 32, 64), ![[#]], ptr %[[ab_ba_addr]], !DIExpression(),
|
|
; NEW-NEXT: #dbg_declare(ptr %[[ab_ba_addr]], ![[y1:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4, DW_OP_LLVM_fragment, 0, 32),
|
|
; NEW-NEXT: %[[aa_reg:.*]] = load i32, ptr @gf, align 4
|
|
; NEW-NEXT: llvm.memcpy{{.*}}(ptr align 4 %[[ab_ba_addr]], ptr align 4 getelementptr inbounds (i8, ptr @gf, i64 4), i64 8, i1 false){{.*}}, !DIAssignID ![[ID:[0-9]+]]
|
|
; NEW-NEXT: %[[bb_reg:.*]] = load i32, ptr getelementptr inbounds (i8, ptr @gf, i64 12), align 4
|
|
; NEW-NEXT: #dbg_value(i32 %[[bb_reg]], ![[y1:[0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 32, 32),
|
|
; NEW-NEXT: #dbg_value(i32 %[[aa_reg]], ![[A1]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
; NEW-NEXT: #dbg_assign(i1 poison, ![[A1]], !DIExpression(DW_OP_LLVM_fragment, 32, 64), ![[ID]], ptr %[[ab_ba_addr]], !DIExpression(),
|
|
; NEW-NEXT: #dbg_value(i32 %[[bb_reg]], ![[A1]], !DIExpression(DW_OP_LLVM_fragment, 96, 32),
|
|
; NEW-NEXT: #dbg_value(i32 %[[aa_reg]], ![[x1]], !DIExpression(DW_OP_LLVM_fragment, 0, 32),
|
|
define dso_local noundef i32 @_Z4fun2v() #0 !dbg !48 {
|
|
entry:
|
|
%0 = alloca %struct.four, align 4
|
|
#dbg_declare(ptr %0, !50, !DIExpression(), !54)
|
|
#dbg_declare(ptr %0, !51, !DIExpression(DW_OP_plus_uconst, 8), !54)
|
|
#dbg_declare(ptr %0, !52, !DIExpression(), !54)
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %0, ptr align 4 @gf, i64 16, i1 false), !dbg !54
|
|
%a = getelementptr inbounds %struct.four, ptr %0, i32 0, i32 0, !dbg !54
|
|
%a1 = getelementptr inbounds %struct.two, ptr %a, i32 0, i32 0, !dbg !54
|
|
%1 = load i32, ptr %a1, align 4, !dbg !54
|
|
%b = getelementptr inbounds %struct.four, ptr %0, i32 0, i32 1, !dbg !54
|
|
%b2 = getelementptr inbounds %struct.two, ptr %b, i32 0, i32 1, !dbg !54
|
|
%2 = load i32, ptr %b2, align 4, !dbg !54
|
|
%add = add nsw i32 %1, %2, !dbg !54
|
|
ret i32 %add, !dbg !54
|
|
}
|
|
|
|
;; Hand-written part to test what happens when variables are smaller than the
|
|
;; new alloca slices (i.e., check offset rewriting works correctly). Note that
|
|
;; mem2reg incorrectly preserves the offest in the DIExpression of a variable
|
|
;; stuffed into the upper bits of a value (that is a bug), e.g. alloca+offset
|
|
;; becomes vreg+offest. It should either convert the offest to a shift, encode
|
|
;; the register-bit offest using DW_OP_bit_piece, or use the new
|
|
;; DW_OP_LLVM_extract_bits_[sz]ext operation.
|
|
; COMMON-LABEL: _Z4fun3v()
|
|
; COMMON-NEXT: entry:
|
|
;; 16 bit variable e (!61): value ve (upper bits)
|
|
;;
|
|
;; 16 bit variable f (!62): value vgf (lower bits)
|
|
;; 16 bit variable g (!63): value vgf (upper bits)
|
|
;;
|
|
;; 16 bit variable h (!64): deref dead_64_128
|
|
; COMMON-NEXT: %[[dead_64_128:.*]] = alloca %struct.two
|
|
; COMMON-NEXT: #dbg_declare(ptr %[[dead_64_128]], ![[h:[0-9]+]], !DIExpression(),
|
|
; COMMON-NEXT: %[[ve:.*]] = load i32, ptr @gf
|
|
;; FIXME: mem2reg bug - offset is incorrect - see comment above.
|
|
; COMMON-NEXT: #dbg_value(i32 %[[ve]], ![[e:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 2),
|
|
; COMMON-NEXT: %[[vfg:.*]] = load i32, ptr getelementptr inbounds (i8, ptr @gf, i64 4)
|
|
; COMMON-NEXT: #dbg_value(i32 %[[vfg]], ![[f:[0-9]+]], !DIExpression(),
|
|
;; FIXME: mem2reg bug - offset is incorrect - see comment above.
|
|
; COMMON-NEXT: #dbg_value(i32 %[[vfg]], ![[g:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 2),
|
|
define dso_local noundef i32 @_Z4fun3v() #0 !dbg !55 {
|
|
entry:
|
|
%0 = alloca %struct.four, align 4
|
|
#dbg_declare(ptr %0, !61, !DIExpression(DW_OP_plus_uconst, 2), !58)
|
|
#dbg_declare(ptr %0, !62, !DIExpression(DW_OP_plus_uconst, 4), !58)
|
|
#dbg_declare(ptr %0, !63, !DIExpression(DW_OP_plus_uconst, 6), !58)
|
|
#dbg_declare(ptr %0, !64, !DIExpression(DW_OP_plus_uconst, 8), !58)
|
|
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %0, ptr align 4 @gf, i64 16, i1 false), !dbg !58
|
|
%1 = getelementptr inbounds %struct.four, ptr %0, i32 0, i32 0, !dbg !58
|
|
%2 = getelementptr inbounds %struct.two, ptr %1, i32 0, i32 1, !dbg !58
|
|
%3 = load i32, ptr %2, align 4, !dbg !58
|
|
ret i32 %3, !dbg !58
|
|
}
|
|
|
|
;; Check that DW_OP_extract_bits_[sz]ext compose with expression offsets and
|
|
;; that new fragments are not created. DW_OP_extract_bits_[sz]ext and fragments
|
|
;; don't compose currently (but could). There are checks that expressions with
|
|
;; bit extracts and fragments are dropped in SROA the test
|
|
;; in llvm/test/DebugInfo/Generic/sroa-extract-bits.ll. FIXME: Don't do that.
|
|
;;
|
|
;; Checks are inline for this one.
|
|
;;
|
|
;; %p alloca is 128 bits
|
|
;; SROA is going to split it in half, discard the lower bits, then split
|
|
;; the upper bits in half and discard the upper bits leaving us with
|
|
;; bits [64, 96) of the original alloca.
|
|
;;
|
|
; COMMON-LABEL: fun4
|
|
define dso_local noundef i32 @fun4(i64 %0) !dbg !65 {
|
|
entry:
|
|
%p = alloca [2 x i64]
|
|
%1 = getelementptr inbounds [2 x i64], ptr %p, i32 0, i32 1
|
|
store i64 %0, ptr %1
|
|
; COMMON: %p.sroa.0.8.extract.trunc = trunc i64 %0 to i32
|
|
;; Simple case - the expression offset (8 bytes) matches the offset of the
|
|
;; slice into the alloca, so can be discarded away entirely.
|
|
; COMMON-NEXT: #dbg_value(i32 %p.sroa.0.8.extract.trunc, ![[p:[0-9]+]], !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 32)
|
|
#dbg_declare(ptr %p, !67, !DIExpression(DW_OP_plus_uconst, 8, DW_OP_LLVM_extract_bits_zext, 0, 32), !66)
|
|
;; The expression offset is 6 bytes, with a bit-extract offset of 32 bits from
|
|
;; there for a total offset of 80 bits. SROA is going to split the alloca in
|
|
;; half (at bit 64). The new expression needs a final bit extract offset of
|
|
;; 80-64=16 bits applied to the mem2reg'd value.
|
|
; COMMON-NEXT: #dbg_value(i32 %p.sroa.0.8.extract.trunc, ![[q:[0-9]+]], !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 8)
|
|
#dbg_declare(ptr %p, !68, !DIExpression(DW_OP_plus_uconst, 6, DW_OP_LLVM_extract_bits_zext, 32, 8), !66)
|
|
;; FIXME: Just as in _Z4fun3v, the offset from the new alloca (2 bytes) is
|
|
;; correct but mem2reg needs to change it from an offset to a shift or
|
|
;; adjust the bit-extract (e.g., add the 2 byte offset to the existing 8 bit
|
|
;; offset for a 24 bit total bit-extract offset).
|
|
; COMMON-NEXT: #dbg_value(i32 %p.sroa.0.8.extract.trunc, ![[r:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 2, DW_OP_LLVM_extract_bits_zext, 8, 8)
|
|
#dbg_declare(ptr %p, !69, !DIExpression(DW_OP_plus_uconst, 10, DW_OP_LLVM_extract_bits_zext, 8, 8), !66)
|
|
%2 = load i32, ptr %1, align 4
|
|
ret i32 %2
|
|
}
|
|
|
|
; COMMON-DAG: ![[x0]] = !DILocalVariable(name: "x",
|
|
; COMMON-DAG: ![[y0]] = !DILocalVariable(name: "y",
|
|
; COMMON-DAG: ![[A0]] = !DILocalVariable(scope:
|
|
|
|
; COMMON-DAG: ![[x1]] = !DILocalVariable(name: "x",
|
|
; COMMON-DAG: ![[y1]] = !DILocalVariable(name: "y",
|
|
; COMMON-DAG: ![[A1]] = !DILocalVariable(scope:
|
|
|
|
; COMMON-DAG: ![[e]] = !DILocalVariable(name: "e",
|
|
; COMMON-DAG: ![[f]] = !DILocalVariable(name: "f",
|
|
; COMMON-DAG: ![[g]] = !DILocalVariable(name: "g",
|
|
; COMMON-DAG: ![[h]] = !DILocalVariable(name: "h",
|
|
|
|
; COMMON-DAG: ![[p]] = !DILocalVariable(name: "p"
|
|
; COMMON-DAG: ![[q]] = !DILocalVariable(name: "q"
|
|
; COMMON-DAG: ![[r]] = !DILocalVariable(name: "r"
|
|
|
|
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
|
|
|
|
!llvm.dbg.cu = !{!2}
|
|
!llvm.module.flags = !{!16, !17}
|
|
!llvm.ident = !{!22}
|
|
|
|
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
|
|
!1 = distinct !DIGlobalVariable(name: "gt", scope: !2, file: !3, line: 1, type: !10, isLocal: false, isDefinition: true)
|
|
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 17.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
|
|
!3 = !DIFile(filename: "test.cpp", directory: "/")
|
|
!4 = !{!0, !5}
|
|
!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
|
|
!6 = distinct !DIGlobalVariable(name: "gf", scope: !2, file: !3, line: 7, type: !7, isLocal: false, isDefinition: true)
|
|
!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "four", file: !3, line: 7, size: 128, flags: DIFlagTypePassByValue, elements: !8, identifier: "_ZTS4four")
|
|
!8 = !{!9, !15}
|
|
!9 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !7, file: !3, line: 7, baseType: !10, size: 64)
|
|
!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "two", file: !3, line: 1, size: 64, flags: DIFlagTypePassByValue, elements: !11, identifier: "_ZTS3two")
|
|
!11 = !{!12, !14}
|
|
!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !3, line: 1, baseType: !13, size: 32)
|
|
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
|
!14 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !10, file: !3, line: 1, baseType: !13, size: 32, offset: 32)
|
|
!15 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !7, file: !3, line: 7, baseType: !10, size: 64, offset: 64)
|
|
!16 = !{i32 7, !"Dwarf Version", i32 5}
|
|
!17 = !{i32 2, !"Debug Info Version", i32 3}
|
|
!22 = !{!"clang version 17.0.0"}
|
|
!23 = distinct !DISubprogram(name: "fun1", linkageName: "_Z4fun1v", scope: !3, file: !3, line: 2, type: !24, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !26)
|
|
!24 = !DISubroutineType(types: !25)
|
|
!25 = !{!13}
|
|
!26 = !{!27, !28, !29}
|
|
!27 = !DILocalVariable(name: "x", scope: !23, file: !3, line: 3, type: !13)
|
|
!28 = !DILocalVariable(name: "y", scope: !23, file: !3, line: 3, type: !13)
|
|
!29 = !DILocalVariable(scope: !23, file: !3, line: 3, type: !10)
|
|
!31 = !DILocation(line: 3, column: 9, scope: !23)
|
|
!48 = distinct !DISubprogram(name: "fun2", linkageName: "_Z4fun2v", scope: !3, file: !3, line: 8, type: !24, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !49)
|
|
!49 = !{!50, !51, !52}
|
|
!50 = !DILocalVariable(name: "x", scope: !48, file: !3, line: 9, type: !10)
|
|
!51 = !DILocalVariable(name: "y", scope: !48, file: !3, line: 9, type: !10)
|
|
!52 = !DILocalVariable(scope: !48, file: !3, line: 9, type: !7)
|
|
!54 = !DILocation(line: 9, column: 9, scope: !48)
|
|
!55 = distinct !DISubprogram(name: "fun3", linkageName: "_Z4fun3v", scope: !3, file: !3, line: 8, type: !24, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !56)
|
|
!56 = !{}
|
|
!58 = !DILocation(line: 9, column: 9, scope: !55)
|
|
!60 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
|
|
!61 = !DILocalVariable(name: "e", scope: !55, file: !3, line: 9, type: !60)
|
|
!62 = !DILocalVariable(name: "f", scope: !55, file: !3, line: 9, type: !60)
|
|
!63 = !DILocalVariable(name: "g", scope: !55, file: !3, line: 9, type: !60)
|
|
!64 = !DILocalVariable(name: "h", scope: !55, file: !3, line: 9, type: !60)
|
|
!65 = distinct !DISubprogram(name: "fun4", linkageName: "_Z4fun4v", scope: !3, file: !3, line: 8, type: !24, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !56)
|
|
!66 = !DILocation(line: 9, column: 9, scope: !65)
|
|
!67 = !DILocalVariable(name: "p", scope: !65, file: !3, line: 9, type: !13)
|
|
!68 = !DILocalVariable(name: "q", scope: !65, file: !3, line: 9, type: !13)
|
|
!69 = !DILocalVariable(name: "r", scope: !65, file: !3, line: 9, type: !13)
|