| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # In certain cases CodeGenPrepare folds a return instruction into | ||
| # the return block's predecessor blocks and subsequently deletes the return block. | ||
| # The purpose of this is to enable tail call optimization in the predecessor blocks. | ||
| # Removal of the return block also removes fake use instructions that were present | ||
| # in the return block, potentially causing debug information to be lost. | ||
| # | ||
| # The fix is to clone any fake use instructions that are not dominated by definitions | ||
| # in the return block itself into the predecessor blocks. This test enures that we do so. | ||
| # | ||
| # Generated from the following source with | ||
| # clang -fextend-lifetimes -S -emit-llvm -O2 -mllvm -stop-before=codegenprepare -o test.mir test.c | ||
| # | ||
| # extern int f0(); | ||
| # extern int f1(); | ||
| # | ||
| # int foo(int i) { | ||
| # int temp = i; | ||
| # if (temp == 0) | ||
| # temp = f0(); | ||
| # else | ||
| # temp = f1(); | ||
| # return temp; | ||
| # } | ||
| # | ||
| # RUN: llc -run-pass=codegenprepare -mtriple=x86_64-unknown-linux -o - %s | FileCheck %s | ||
| # | ||
| # CHECK: define{{.*}}foo | ||
| # CHECK: if.then: | ||
| # CHECK-NEXT: call{{.*}}fake.use(i32 %i) | ||
| # CHECK-NEXT: tail call i32{{.*}}@f0 | ||
| # CHECK-NEXT: ret | ||
| # CHECK: if.else: | ||
| # CHECK-NEXT: call{{.*}}fake.use(i32 %i) | ||
| # CHECK-NEXT: tail call i32{{.*}}@f1 | ||
| # CHECK-NEXT: ret | ||
|
|
||
| --- | | ||
| define hidden i32 @foo(i32 %i) local_unnamed_addr optdebug { | ||
| entry: | ||
| %cmp = icmp eq i32 %i, 0 | ||
| br i1 %cmp, label %if.then, label %if.else | ||
|
|
||
| if.then: | ||
| %call = tail call i32 (...) @f0() | ||
| br label %if.end | ||
|
|
||
| if.else: | ||
| %call1 = tail call i32 (...) @f1() | ||
| br label %if.end | ||
|
|
||
| if.end: | ||
| %temp.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ] | ||
| notail call void (...) @llvm.fake.use(i32 %temp.0) | ||
| notail call void (...) @llvm.fake.use(i32 %i) | ||
| ret i32 %temp.0 | ||
| } | ||
| declare i32 @f0(...) local_unnamed_addr | ||
| declare i32 @f1(...) local_unnamed_addr | ||
|
|
||
| ... | ||
| --- | ||
| name: foo | ||
| alignment: 16 | ||
| exposesReturnsTwice: false | ||
| legalized: false | ||
| regBankSelected: false | ||
| selected: false | ||
| failedISel: false | ||
| tracksRegLiveness: true | ||
| hasWinCFI: false | ||
| registers: [] | ||
| liveins: [] | ||
| frameInfo: | ||
| isFrameAddressTaken: false | ||
| isReturnAddressTaken: false | ||
| hasStackMap: false | ||
| hasPatchPoint: false | ||
| stackSize: 0 | ||
| offsetAdjustment: 0 | ||
| maxAlignment: 1 | ||
| adjustsStack: false | ||
| hasCalls: false | ||
| stackProtector: '' | ||
| maxCallFrameSize: 4294967295 | ||
| cvBytesOfCalleeSavedRegisters: 0 | ||
| hasOpaqueSPAdjustment: false | ||
| hasVAStart: false | ||
| hasMustTailInVarArgFunc: false | ||
| localFrameSize: 0 | ||
| savePoint: '' | ||
| restorePoint: '' | ||
| fixedStack: [] | ||
| stack: [] | ||
| callSites: [] | ||
| constants: [] | ||
| machineFunctionInfo: {} | ||
| body: | | ||
|
|
||
| ... |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| ; assert in DAGlegalizer with fake use of half precision float. | ||
| ; Changes to half float promotion. | ||
| ; RUN: llc -stop-after=finalize-isel -o - %s | FileCheck %s | ||
| ; | ||
| ; CHECK: bb.0.entry: | ||
| ; CHECK-NEXT: %0:fr16 = FsFLD0SH | ||
| ; CHECK-NEXT: FAKE_USE killed %0 | ||
| ; | ||
| target triple = "x86_64-unknown-unknown" | ||
|
|
||
| define void @_Z6doTestv() local_unnamed_addr optdebug { | ||
| entry: | ||
| tail call void (...) @llvm.fake.use(half 0xH0000) | ||
| ret void | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| ; RUN: llc -O0 -mtriple=x86_64-unknown-unknown < %s | FileCheck %s | ||
|
|
||
| ; Checks that fake uses of the FP stack do not cause a crash. | ||
| ; | ||
| ; /*******************************************************************/ | ||
| ; extern long double foo(long double, long double, long double); | ||
| ; | ||
| ; long double actual(long double p1, long double p2, long double p3) { | ||
| ; return fmal(p1, p2, p3); | ||
| ; } | ||
| ; /*******************************************************************/ | ||
|
|
||
| define x86_fp80 @actual(x86_fp80 %p1, x86_fp80 %p2, x86_fp80 %p3) optdebug { | ||
| ; | ||
| ; CHECK: actual | ||
| ; | ||
| entry: | ||
| %p1.addr = alloca x86_fp80, align 16 | ||
| %p2.addr = alloca x86_fp80, align 16 | ||
| %p3.addr = alloca x86_fp80, align 16 | ||
| store x86_fp80 %p1, ptr %p1.addr, align 16 | ||
| store x86_fp80 %p2, ptr %p2.addr, align 16 | ||
| store x86_fp80 %p3, ptr %p3.addr, align 16 | ||
| %0 = load x86_fp80, ptr %p1.addr, align 16 | ||
| %1 = load x86_fp80, ptr %p2.addr, align 16 | ||
| %2 = load x86_fp80, ptr %p3.addr, align 16 | ||
| ; | ||
| ; CHECK: callq{{.*}}foo | ||
| ; | ||
| %3 = call x86_fp80 @foo(x86_fp80 %0, x86_fp80 %1, x86_fp80 %2) | ||
| %4 = load x86_fp80, ptr %p1.addr, align 16 | ||
| call void (...) @llvm.fake.use(x86_fp80 %4) | ||
| %5 = load x86_fp80, ptr %p2.addr, align 16 | ||
| call void (...) @llvm.fake.use(x86_fp80 %5) | ||
| %6 = load x86_fp80, ptr %p3.addr, align 16 | ||
| call void (...) @llvm.fake.use(x86_fp80 %6) | ||
| ; | ||
| ; CHECK: ret | ||
| ; | ||
| ret x86_fp80 %3 | ||
| } | ||
|
|
||
| declare x86_fp80 @foo(x86_fp80, x86_fp80, x86_fp80) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| # Prevent the machine scheduler from moving instructions past FAKE_USE. | ||
| # RUN: llc -run-pass machine-scheduler -mtriple=x86_64-unknown-linux -debug-only=machine-scheduler 2>&1 -o - %s | FileCheck %s | ||
| # REQUIRES: asserts | ||
| # | ||
| # We make sure that, beginning with the first FAKE_USE instruction, | ||
| # no changes to the sequence of instructions are undertaken by the | ||
| # scheduler. We don't bother to check that the order of the FAKE_USEs | ||
| # remains the same. They should, but it is irrelevant. | ||
| # | ||
| # CHECK: ********** MI Scheduling ********** | ||
| # CHECK-NEXT: foo:%bb.0 entry | ||
| # CHECK-NEXT: From: %0:gr64 = COPY $rdi | ||
| # CHECK-NEXT: To: FAKE_USE %5:gr64 | ||
| # CHECK-NEXT: RegionInstrs: 7 | ||
| # | ||
| # CHECK: ********** MI Scheduling ********** | ||
| # CHECK-NEXT: bar:%bb.0 entry | ||
| # CHECK-NEXT: From: %0:gr64 = COPY $rdi | ||
| # CHECK-NEXT: To: RET 0, killed $rax | ||
| # CHECK-NEXT: RegionInstrs: 7 | ||
| # | ||
| --- | | ||
| ; ModuleID = 'test.ll' | ||
| source_filename = "test.ll" | ||
| target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" | ||
|
|
||
| @glb = common dso_local local_unnamed_addr global [100 x i32] zeroinitializer, align 16 | ||
|
|
||
| define dso_local i64 @foo(ptr %p) local_unnamed_addr optdebug { | ||
| entry: | ||
| %0 = load i32, ptr @glb, align 16 | ||
| store i32 %0, ptr %p, align 4 | ||
| %conv = sext i32 %0 to i64 | ||
| %1 = load i32, ptr getelementptr inbounds ([100 x i32], ptr @glb, i64 0, i64 1), align 4 | ||
| %arrayidx1 = getelementptr inbounds i32, ptr %p, i64 1 | ||
| store i32 %1, ptr %arrayidx1, align 4 | ||
| %conv2 = sext i32 %1 to i64 | ||
| %add3 = add nsw i64 %conv2, %conv | ||
| notail call void (...) @llvm.fake.use(i64 %add3) | ||
| notail call void (...) @llvm.fake.use(i32 %1) | ||
| notail call void (...) @llvm.fake.use(i32 %0) | ||
| notail call void (...) @llvm.fake.use(ptr %p) | ||
| ret i64 %add3 | ||
| } | ||
|
|
||
| define dso_local i64 @bar(ptr %p) local_unnamed_addr optdebug { | ||
| entry: | ||
| %0 = load i32, ptr @glb, align 16 | ||
| store i32 %0, ptr %p, align 4 | ||
| %conv = sext i32 %0 to i64 | ||
| %1 = load i32, ptr getelementptr inbounds ([100 x i32], ptr @glb, i64 0, i64 1), align 4 | ||
| %arrayidx1 = getelementptr inbounds i32, ptr %p, i64 1 | ||
| store i32 %1, ptr %arrayidx1, align 4 | ||
| %conv2 = sext i32 %1 to i64 | ||
| %add3 = add nsw i64 %conv2, %conv | ||
| ret i64 %add3 | ||
| } | ||
|
|
||
| ; Function Attrs: nocallback nofree nosync nounwind willreturn | ||
| declare void @llvm.stackprotector(ptr, ptr) | ||
|
|
||
| ... | ||
| --- | ||
| name: foo | ||
| alignment: 16 | ||
| tracksRegLiveness: true | ||
| debugInstrRef: true | ||
| registers: | ||
| - { id: 0, class: gr64, preferred-register: '' } | ||
| - { id: 1, class: gr64_with_sub_8bit, preferred-register: '' } | ||
| - { id: 2, class: gr32, preferred-register: '' } | ||
| - { id: 3, class: gr64_with_sub_8bit, preferred-register: '' } | ||
| - { id: 4, class: gr32, preferred-register: '' } | ||
| - { id: 5, class: gr64, preferred-register: '' } | ||
| liveins: | ||
| - { reg: '$rdi', virtual-reg: '%0' } | ||
| body: | | ||
| bb.0.entry: | ||
| liveins: $rdi | ||
|
|
||
| %0:gr64 = COPY $rdi | ||
| %1:gr64_with_sub_8bit = MOVSX64rm32 $rip, 1, $noreg, @glb, $noreg | ||
| MOV32mr %0, 1, $noreg, 0, $noreg, %1.sub_32bit | ||
| %3:gr64_with_sub_8bit = MOVSX64rm32 $rip, 1, $noreg, @glb + 4, $noreg | ||
| MOV32mr %0, 1, $noreg, 4, $noreg, %3.sub_32bit | ||
| %5:gr64 = COPY %3 | ||
| %5:gr64 = nsw ADD64rr %5, %1, implicit-def dead $eflags | ||
| FAKE_USE %5 | ||
| FAKE_USE %3.sub_32bit | ||
| FAKE_USE %1.sub_32bit | ||
| FAKE_USE %0 | ||
| $rax = COPY %5 | ||
| RET 0, killed $rax | ||
|
|
||
| ... | ||
| --- | ||
| name: bar | ||
| alignment: 16 | ||
| tracksRegLiveness: true | ||
| debugInstrRef: true | ||
| registers: | ||
| - { id: 0, class: gr64, preferred-register: '' } | ||
| - { id: 1, class: gr64_with_sub_8bit, preferred-register: '' } | ||
| - { id: 2, class: gr32, preferred-register: '' } | ||
| - { id: 3, class: gr64_with_sub_8bit, preferred-register: '' } | ||
| - { id: 4, class: gr32, preferred-register: '' } | ||
| - { id: 5, class: gr64_with_sub_8bit, preferred-register: '' } | ||
| liveins: | ||
| - { reg: '$rdi', virtual-reg: '%0' } | ||
| body: | | ||
| bb.0.entry: | ||
| liveins: $rdi | ||
|
|
||
| %0:gr64 = COPY $rdi | ||
| %1:gr64_with_sub_8bit = MOVSX64rm32 $rip, 1, $noreg, @glb, $noreg | ||
| MOV32mr %0, 1, $noreg, 0, $noreg, %1.sub_32bit | ||
| %5:gr64_with_sub_8bit = MOVSX64rm32 $rip, 1, $noreg, @glb + 4, $noreg | ||
| MOV32mr %0, 1, $noreg, 4, $noreg, %5.sub_32bit | ||
| %5:gr64_with_sub_8bit = nsw ADD64rr %5, %1, implicit-def dead $eflags | ||
| $rax = COPY %5 | ||
| RET 0, killed $rax | ||
|
|
||
| ... |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| ; RUN: llc < %s -mtriple=x86_64-unknown-unknown -O2 -o - \ | ||
| ; RUN: | FileCheck %s --implicit-check-not=TAILCALL | ||
| ; Generated with: clang -emit-llvm -O2 -S -fextend-lifetimes test.cpp -o - | ||
| ; =========== test.cpp =============== | ||
| ; extern int bar(int); | ||
| ; int foo1(int i) | ||
| ; { | ||
| ; return bar(i); | ||
| ; } | ||
| ; =========== test.cpp =============== | ||
|
|
||
| ; CHECK: TAILCALL | ||
|
|
||
| ; ModuleID = 'test.cpp' | ||
| source_filename = "test.cpp" | ||
|
|
||
| define i32 @_Z4foo1i(i32 %i) local_unnamed_addr optdebug { | ||
| entry: | ||
| %call = tail call i32 @_Z3bari(i32 %i) | ||
| tail call void (...) @llvm.fake.use(i32 %i) | ||
| ret i32 %call | ||
| } | ||
|
|
||
| declare i32 @_Z3bari(i32) local_unnamed_addr |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| ; Suppress redundant loads feeding into fake uses. | ||
| ; RUN: llc -filetype=asm -o - %s --mtriple=x86_64-unknown-unknown | FileCheck %s | ||
| ; Windows ABI works differently, there's no offset. | ||
| ; | ||
| ; Look for the spill | ||
| ; CHECK: movq %r{{[a-z]+,}} -{{[0-9]+\(%rsp\)}} | ||
| ; CHECK-NOT: movq -{{[0-9]+\(%rsp\)}}, %r{{[a-z]+}} | ||
|
|
||
| define dso_local i32 @f(ptr %p) local_unnamed_addr optdebug { | ||
| entry: | ||
| call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{dirflag},~{fpsr},~{flags}"() #1 | ||
| notail call void (...) @llvm.fake.use(ptr %p) | ||
| ret i32 4 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| ; RUN: llc < %s -stop-after=finalize-isel -mtriple=x86_64-unknown-linux - | FileCheck %s --implicit-check-not FAKE_USE | ||
| ; Fake uses following tail calls should be pulled in front | ||
| ; of the TCRETURN instruction. Fake uses using something defined by | ||
| ; the tail call or after it should be suppressed. | ||
|
|
||
| ; CHECK: name:{{ +}}bar | ||
| ; CHECK: body: | ||
| ; CHECK: bb.0.{{.*}}: | ||
| ; CHECK: %0:{{.*}}= COPY | ||
| ; CHECK: FAKE_USE %0 | ||
| ; CHECK: TCRETURN | ||
|
|
||
| ; CHECK: name:{{ +}}baz | ||
| ; CHECK: body: | ||
| ; CHECK: bb.0.{{.*}}: | ||
| ; CHECK: %0:{{.*}}= COPY | ||
| ; CHECK: FAKE_USE %0 | ||
| ; CHECK: TCRETURN | ||
|
|
||
| define void @bar(i32 %v) optdebug { | ||
| entry: | ||
| %call = tail call i32 @_Z3fooi(i32 %v) | ||
| %mul = mul nsw i32 %call, 3 | ||
| notail call void (...) @llvm.fake.use(i32 %mul) | ||
| notail call void (...) @llvm.fake.use(i32 %call) | ||
| notail call void (...) @llvm.fake.use(i32 %v) | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @baz(i32 %v) optdebug { | ||
| entry: | ||
| %call = tail call i32 @_Z3fooi(i32 %v) | ||
| notail call void (...) @llvm.fake.use(i32 %v) | ||
| ret i32 %call | ||
| } | ||
|
|
||
| declare i32 @_Z3fooi(i32) local_unnamed_addr |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| ; assert in DAGlegalizer with fake use of 1-element vectors. | ||
| ; RUN: llc -stop-after=finalize-isel -filetype=asm -o - %s | FileCheck %s | ||
| ; | ||
| ; ModuleID = 't2.cpp' | ||
| ; source_filename = "t2.cpp" | ||
| ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
| ; | ||
| ; Check that we get past ISel and generate FAKE_USE machine instructions for | ||
| ; one-element vectors. | ||
| ; | ||
| ; CHECK: bb.0.entry: | ||
| ; CHECK-DAG: %1:gr64 = COPY $rdi | ||
| ; CHECK-DAG: %0:vr128 = COPY $xmm0 | ||
| ; CHECK: %2:vr64 = | ||
| ; CHECK-DAG: FAKE_USE %1 | ||
| ; CHECK-DAG: FAKE_USE %0 | ||
| ; CHECK: RET | ||
|
|
||
|
|
||
| target triple = "x86_64-unknown-unknown" | ||
|
|
||
| ; Function Attrs: nounwind sspstrong uwtable | ||
| define <4 x float> @_Z3runDv4_fDv1_x(<4 x float> %r, i64 %b.coerce) local_unnamed_addr #0 { | ||
| entry: | ||
| %0 = insertelement <1 x i64> undef, i64 %b.coerce, i32 0 | ||
| %1 = bitcast i64 %b.coerce to <1 x i64> | ||
| %2 = tail call <4 x float> @llvm.x86.sse.cvtpi2ps(<4 x float> %r, <1 x i64> %1) | ||
| tail call void (...) @llvm.fake.use(<1 x i64> %0) | ||
| tail call void (...) @llvm.fake.use(<4 x float> %r) | ||
| ret <4 x float> %2 | ||
| } | ||
|
|
||
| ; Function Attrs: nounwind readnone | ||
| declare <4 x float> @llvm.x86.sse.cvtpi2ps(<4 x float>, <1 x i64>) | ||
|
|
||
| ; Function Attrs: nounwind | ||
| declare void @llvm.fake.use(...) | ||
|
|
||
| attributes #0 = { "target-cpu"="btver2" optdebug } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| ; RUN: llc -stop-after=finalize-isel -mtriple=x86_64-unknown-linux -filetype=asm -o - %s | FileCheck %s | ||
| ; | ||
| ; Make sure we can split vectors that are used as operands of FAKE_USE. | ||
|
|
||
| ; Generated from: | ||
| ; | ||
| ; typedef long __attribute__((ext_vector_type(8))) long8; | ||
| ; void test0() { long8 id208 {0, 1, 2, 3, 4, 5, 6, 7}; } | ||
|
|
||
| ; ModuleID = 't5.cpp' | ||
| source_filename = "t5.cpp" | ||
|
|
||
|
|
||
| ; CHECK: %0:vr256 = VMOV | ||
| ; CHECK: %1:vr256 = VMOV | ||
| ; CHECK-DAG: FAKE_USE killed %1 | ||
| ; CHECK-DAG: FAKE_USE killed %0 | ||
| ; CHECK: RET | ||
| define void @_Z5test0v() local_unnamed_addr #0 { | ||
| entry: | ||
| tail call void (...) @llvm.fake.use(<8 x i64> <i64 0, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7>) #1 | ||
| ret void | ||
| } | ||
|
|
||
| declare void @llvm.fake.use(...) | ||
|
|
||
| attributes #0 = { "target-cpu"="btver2" optdebug } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| ; RUN: llc < %s -stop-after=finalize-isel -mtriple=x86_64-unknown-linux | FileCheck %s --implicit-check-not=FAKE_USE | ||
| ; | ||
| ; Make sure SelectionDAG does not crash handling fake uses of zero-length arrays | ||
| ; and structs. Check also that they are not propagated. | ||
| ; | ||
| ; Generated from the following source with | ||
| ; clang -fextend-lifetimes -S -emit-llvm -O2 -mllvm -stop-after=safe-stack -o test.mir test.cpp | ||
| ; | ||
| ; int main () | ||
| ; { int array[0]; } | ||
| ; | ||
| ; | ||
| ; CHECK: liveins: $[[IN_REG:[a-zA-Z0-9]+]] | ||
| ; CHECK: %[[IN_VREG:[a-zA-Z0-9]+]]:gr32 = COPY $[[IN_REG]] | ||
| ; CHECK: FAKE_USE %[[IN_VREG]] | ||
|
|
||
| source_filename = "test.ll" | ||
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
|
|
||
| define hidden i32 @main([0 x i32] %zero, [1 x i32] %one) local_unnamed_addr optdebug { | ||
| entry: | ||
| notail call void (...) @bar([0 x i32] %zero) | ||
| notail call void (...) @baz([1 x i32] %one) | ||
| notail call void (...) @llvm.fake.use([0 x i32] %zero) | ||
| notail call void (...) @llvm.fake.use([1 x i32] %one) | ||
| ret i32 0 | ||
| } | ||
|
|
||
| declare void @bar([0 x i32] %a) | ||
| declare void @baz([1 x i32] %a) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| ; REQUIRES: object-emission | ||
|
|
||
| ; Make sure the fake use of 'b' at the end of 'foo' causes location information for 'b' | ||
| ; to extend all the way to the end of the function. | ||
| ; Duplicates `DebugInfo/X86/fake-use.ll` for global-isel. | ||
|
|
||
| ; RUN: %llc_dwarf -O2 --global-isel=1 -mtriple=aarch64--linux-gnu -filetype=obj -dwarf-linkage-names=Abstract < %s | llvm-dwarfdump --debug-info --debug-line -v - -o %t | ||
| ; RUN: %python %p/../Inputs/check-fake-use.py %t | ||
| ; RUN: sed -e 's,call void (...) @llvm.fake.use,;,' %s \ | ||
| ; RUN: | %llc_dwarf - -O2 --global-isel=1 -mtriple=aarch64--linux-gnu -filetype=obj -dwarf-linkage-names=Abstract \ | ||
| ; RUN: | llvm-dwarfdump --debug-info --debug-line -v - -o %t | ||
| ; RUN: not %python %p/../Inputs/check-fake-use.py %t | ||
|
|
||
| ; Generated with: | ||
| ; clang -O2 -g -S -emit-llvm -fextend-this-ptr fake-use.c | ||
| ; | ||
| ; int glob[10]; | ||
| ; extern void bar(); | ||
| ; | ||
| ; int foo(int b, int i) | ||
| ; { | ||
| ; int loc = glob[i] * 2; | ||
| ; if (b) { | ||
| ; glob[2] = loc; | ||
| ; bar(); | ||
| ; } | ||
| ; return loc; | ||
| ; } | ||
| ; | ||
| ; ModuleID = 't2.c' | ||
| source_filename = "t2.c" | ||
|
|
||
| @glob = common local_unnamed_addr global [10 x i32] zeroinitializer, align 16, !dbg !0 | ||
|
|
||
| ; Function Attrs: nounwind sspstrong uwtable | ||
| define i32 @foo(i32 %b, i32 %i) local_unnamed_addr optdebug !dbg !13 { | ||
| entry: | ||
| #dbg_value(i32 %b, !17, !20, !21) | ||
| %c = add i32 %b, 42 | ||
| %tobool = icmp sgt i32 %c, 2, !dbg !27 | ||
| tail call void (...) @bar() #2, !dbg !32 | ||
| %idxprom = sext i32 %i to i64, !dbg !22 | ||
| %arrayidx = getelementptr inbounds [10 x i32], [10 x i32]* @glob, i64 0, i64 %idxprom, !dbg !22 | ||
| %0 = load i32, i32* %arrayidx, align 4, !dbg !22, !tbaa !23 | ||
| %mul = shl nsw i32 %0, 1, !dbg !22 | ||
| br i1 %tobool, label %if.end, label %if.then, !dbg !29 | ||
|
|
||
| if.then: ; preds = %entry | ||
| store i32 %mul, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @glob, i64 0, i64 2), align 8, !dbg !30, !tbaa !23 | ||
| tail call void (...) @bar() #2, !dbg !32 | ||
| br label %if.end, !dbg !33 | ||
|
|
||
| if.end: ; preds = %entry, %if.then | ||
| call void (...) @llvm.fake.use(i32 %b), !dbg !34 | ||
| ret i32 %mul, !dbg !35 | ||
| } | ||
|
|
||
| declare void @bar(...) local_unnamed_addr | ||
|
|
||
| !llvm.dbg.cu = !{!1} | ||
| !llvm.module.flags = !{!9, !10, !11} | ||
| !llvm.ident = !{!12} | ||
|
|
||
| !0 = distinct !DIGlobalVariableExpression(var: !DIGlobalVariable(name: "glob", scope: !1, file: !2, line: 1, type: !5, isLocal: false, isDefinition: true), expr: !DIExpression()) | ||
| !1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 4.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !3, globals: !4) | ||
| !2 = !DIFile(filename: "t2.c", directory: "/") | ||
| !3 = !{} | ||
| !4 = !{!0} | ||
| !5 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 320, align: 32, elements: !7) | ||
| !6 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) | ||
| !7 = !{!8} | ||
| !8 = !DISubrange(count: 10) | ||
| !9 = !{i32 2, !"Dwarf Version", i32 4} | ||
| !10 = !{i32 2, !"Debug Info Version", i32 3} | ||
| !11 = !{i32 1, !"PIC Level", i32 2} | ||
| !12 = !{!"clang version 4.0.0"} | ||
| !13 = distinct !DISubprogram(name: "foo", scope: !2, file: !2, line: 4, type: !14, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: true, unit: !1, retainedNodes: !16) | ||
| !14 = !DISubroutineType(types: !15) | ||
| !15 = !{!6, !6, !6} | ||
| !16 = !{!17, !19} | ||
| !17 = !DILocalVariable(name: "b", arg: 1, scope: !13, file: !2, line: 4, type: !6) | ||
| !19 = !DILocalVariable(name: "loc", scope: !13, file: !2, line: 6, type: !6) | ||
| !20 = !DIExpression() | ||
| !21 = !DILocation(line: 4, scope: !13) | ||
| !22 = !DILocation(line: 6, scope: !13) | ||
| !23 = !{!24, !24, i64 0} | ||
| !24 = !{!"int", !25, i64 0} | ||
| !25 = !{!"omnipotent char", !26, i64 0} | ||
| !26 = !{!"Simple C/C++ TBAA"} | ||
| !27 = !DILocation(line: 7, scope: !28) | ||
| !28 = distinct !DILexicalBlock(scope: !13, file: !2, line: 7) | ||
| !29 = !DILocation(line: 7, scope: !13) | ||
| !30 = !DILocation(line: 8, scope: !31) | ||
| !31 = distinct !DILexicalBlock(scope: !28, file: !2, line: 7) | ||
| !32 = !DILocation(line: 9, scope: !31) | ||
| !33 = !DILocation(line: 10, scope: !31) | ||
| !34 = !DILocation(line: 12, scope: !13) | ||
| !35 = !DILocation(line: 11, scope: !13) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| #!/usr/bin/python3 | ||
|
|
||
| # Parsing dwarfdump's output to determine whether the location list for the | ||
| # parameter "b" covers all of the function. The script searches for information | ||
| # in the input file to determine the [prologue, epilogue) range for the | ||
| # function, the location list range for "b", and checks that the latter covers | ||
| # the entirety of the former. | ||
| import re | ||
| import sys | ||
|
|
||
| DebugInfoPattern = r"\.debug_info contents:" | ||
| DebugLinePattern = r"\.debug_line contents:" | ||
| ProloguePattern = r"^\s*0x([0-9a-f]+)\s.+prologue_end" | ||
| EpiloguePattern = r"^\s*0x([0-9a-f]+)\s.+epilogue_begin" | ||
| FormalPattern = r"^0x[0-9a-f]+:\s+DW_TAG_formal_parameter" | ||
| LocationPattern = r"DW_AT_location\s+\[DW_FORM_([a-z_]+)\](?:.*0x([a-f0-9]+))" | ||
| DebugLocPattern = r'\[0x([a-f0-9]+),\s+0x([a-f0-9]+)\) ".text": (.+)$' | ||
|
|
||
| SeenDebugInfo = False | ||
| SeenDebugLine = False | ||
| LocationRanges = None | ||
| PrologueEnd = None | ||
| EpilogueBegin = None | ||
|
|
||
| # The dwarfdump output should contain the DW_AT_location for "b" first, then the | ||
| # line table which should contain prologue_end and epilogue_begin entries. | ||
| with open(sys.argv[1], "r") as dwarf_dump_file: | ||
| dwarf_iter = iter(dwarf_dump_file) | ||
| for line in dwarf_iter: | ||
| if not SeenDebugInfo and re.match(DebugInfoPattern, line): | ||
| SeenDebugInfo = True | ||
| if not SeenDebugLine and re.match(DebugLinePattern, line): | ||
| SeenDebugLine = True | ||
| # Get the range of DW_AT_location for "b". | ||
| if LocationRanges is None: | ||
| if match := re.match(FormalPattern, line): | ||
| # Go until we either find DW_AT_location or reach the end of this entry. | ||
| location_match = None | ||
| while location_match is None: | ||
| if (line := next(dwarf_iter, "")) == "\n": | ||
| raise RuntimeError( | ||
| ".debug_info output is missing DW_AT_location for 'b'" | ||
| ) | ||
| location_match = re.search(LocationPattern, line) | ||
| # Variable has whole-scope location, represented by an empty tuple. | ||
| if location_match.group(1) == "exprloc": | ||
| LocationRanges = () | ||
| continue | ||
| if location_match.group(1) != "sec_offset": | ||
| raise RuntimeError( | ||
| f"Unhandled form for DW_AT_location: DW_FORM_{location_match.group(1)}" | ||
| ) | ||
| # Variable has location range list. | ||
| if ( | ||
| debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, "")) | ||
| ) is None: | ||
| raise RuntimeError(f"Invalid location range list for 'b'") | ||
| LocationRanges = ( | ||
| int(debug_loc_match.group(1), 16), | ||
| int(debug_loc_match.group(2), 16), | ||
| ) | ||
| while ( | ||
| debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, "")) | ||
| ) is not None: | ||
| match_loc_start = int(debug_loc_match.group(1), 16) | ||
| match_loc_end = int(debug_loc_match.group(2), 16) | ||
| match_expr = debug_loc_match.group(3) | ||
| if match_loc_start != LocationRanges[1]: | ||
| raise RuntimeError( | ||
| f"Location list for 'b' is discontinuous from [0x{LocationRanges[1]:x}, 0x{match_loc_start:x})" | ||
| ) | ||
| if "stack_value" in match_expr: | ||
| raise RuntimeError( | ||
| f"Location list for 'b' contains a stack_value expression: {match_expr}" | ||
| ) | ||
| LocationRanges = (LocationRanges[0], match_loc_end) | ||
| # Get the prologue_end address. | ||
| elif PrologueEnd is None: | ||
| if match := re.match(ProloguePattern, line): | ||
| PrologueEnd = int(match.group(1), 16) | ||
| # Get the epilogue_begin address. | ||
| elif EpilogueBegin is None: | ||
| if match := re.match(EpiloguePattern, line): | ||
| EpilogueBegin = int(match.group(1), 16) | ||
| break | ||
|
|
||
| if not SeenDebugInfo: | ||
| raise RuntimeError(".debug_info section not found.") | ||
| if not SeenDebugLine: | ||
| raise RuntimeError(".debug_line section not found.") | ||
|
|
||
| if LocationRanges is None: | ||
| raise RuntimeError(".debug_info output is missing parameter 'b'") | ||
| if PrologueEnd is None: | ||
| raise RuntimeError(".debug_line output is missing prologue_end") | ||
| if EpilogueBegin is None: | ||
| raise RuntimeError(".debug_line output is missing epilogue_begin") | ||
|
|
||
| if len(LocationRanges) == 2 and ( | ||
| LocationRanges[0] > PrologueEnd or LocationRanges[1] < EpilogueBegin | ||
| ): | ||
| raise RuntimeError( | ||
| f"""Location list for 'b' does not cover the whole function:") | ||
| Prologue to Epilogue = [0x{PrologueEnd:x}, 0x{EpilogueBegin:x}) | ||
| Location range = [0x{LocationRanges[0]:x}, 0x{LocationRanges[1]:x}) | ||
| """ | ||
| ) |