| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp") | ||
| // CHECK-NOT: func{{[23]}} | ||
| } | ||
|
|
||
| void func2(); | ||
|
|
||
| void __attribute__((noinline)) func1() { | ||
| if (x < 1) | ||
| func2(); | ||
| else | ||
| sink(); | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func2() { | ||
| if (x < 1) | ||
| sink(); | ||
| else | ||
| func1(); | ||
| } | ||
|
|
||
| int main() { | ||
| // Tail recursion creates ambiguous execution histories. | ||
| x = 0; | ||
| func1(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial") | ||
| // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt] | ||
| // CHECK-NEXT: func2{{.*}} [opt] [artificial] | ||
| // CHECK-NEXT: main{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func2() { | ||
| sink(); /* tail */ | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func1() { sink(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main(int argc, char **) { | ||
| // The sequences `main -> f{1,2} -> sink` are both plausible. Test that | ||
| // return-pc call site info allows lldb to pick the correct sequence. | ||
| func2(); | ||
| if (argc == 100) | ||
| func1(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink2() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=FROM-FUNC1") | ||
| // FROM-FUNC1: frame #0: 0x{{[0-9a-f]+}} a.out`sink{{.*}} at main.cpp:[[@LINE-1]]:{{.*}} [opt] | ||
| // FROM-FUNC1-NEXT: sink({{.*}} [opt] | ||
| // FROM-FUNC1-NEXT: func1{{.*}} [opt] [artificial] | ||
| // FROM-FUNC1-NEXT: main{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) sink(bool called_from_main) { | ||
| if (called_from_main) { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=FROM-MAIN") | ||
| // FROM-MAIN: frame #0: 0x{{[0-9a-f]+}} a.out`sink{{.*}} at main.cpp:[[@LINE-1]]:{{.*}} [opt] | ||
| // FROM-MAIN-NEXT: main{{.*}} [opt] | ||
| } else { | ||
| sink2(); | ||
| } | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func1() { sink(false); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main(int argc, char **) { | ||
| // When func1 tail-calls sink, make sure that the former appears in the | ||
| // backtrace. | ||
| sink(true); | ||
| func1(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial") | ||
| // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt] | ||
| // CHECK-NEXT: func3{{.*}} [opt] [artificial] | ||
| // CHECK-NEXT: func1{{.*}} [opt] [artificial] | ||
| // CHECK-NEXT: main{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((noinline)) func2() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func3(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main(int argc, char **) { | ||
| // The sequences `main -> func1 -> f{2,3} -> sink` are both plausible. Test | ||
| // that lldb picks the latter sequence. | ||
| func1(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) tail_call_sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=TAIL-CALL-SINK") | ||
| // TAIL-CALL-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`tail_call_sink() at main.cpp:[[@LINE-1]]:4 [opt] | ||
| // TAIL-CALL-SINK-NEXT: func3{{.*}} [opt] [artificial] | ||
| // TAIL-CALL-SINK-NEXT: main{{.*}} [opt] | ||
|
|
||
| // TODO: The backtrace should include inlinable_function_which_tail_calls. | ||
| } | ||
|
|
||
| void __attribute__((always_inline)) inlinable_function_which_tail_calls() { | ||
| tail_call_sink(); | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { | ||
| inlinable_function_which_tail_calls(); | ||
| } | ||
|
|
||
| void __attribute__((always_inline)) inline_sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=INLINE-SINK") | ||
| // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`func2() [inlined] inline_sink() at main.cpp:[[@LINE-1]]:4 [opt] | ||
| // INLINE-SINK-NEXT: func2{{.*}} [opt] | ||
| // INLINE-SINK-NEXT: func1{{.*}} [opt] [artificial] | ||
| // INLINE-SINK-NEXT: main{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func2() { inline_sink(); /* inlined */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func2(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main() { | ||
| // First, call a function that tail-calls a function, which itself inlines | ||
| // a third function. | ||
| func1(); | ||
|
|
||
| // Next, call a function which contains an inlined tail-call. | ||
| func3(); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| """ | ||
| Test SB API support for identifying artificial (tail call) frames. | ||
| """ | ||
|
|
||
| import lldb | ||
| import lldbsuite.test.lldbutil as lldbutil | ||
| from lldbsuite.test.lldbtest import * | ||
|
|
||
| class TestTailCallFrameSBAPI(TestBase): | ||
| mydir = TestBase.compute_mydir(__file__) | ||
|
|
||
| # If your test case doesn't stress debug info, the | ||
| # set this to true. That way it won't be run once for | ||
| # each debug info format. | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def test_tail_call_frame_sbapi(self): | ||
| self.build() | ||
| self.do_test() | ||
|
|
||
| def setUp(self): | ||
| # Call super's setUp(). | ||
| TestBase.setUp(self) | ||
|
|
||
| def do_test(self): | ||
| exe = self.getBuildArtifact("a.out") | ||
|
|
||
| # Create a target by the debugger. | ||
| target = self.dbg.CreateTarget(exe) | ||
| self.assertTrue(target, VALID_TARGET) | ||
|
|
||
| breakpoint = target.BreakpointCreateBySourceRegex("break here", | ||
| lldb.SBFileSpec("main.cpp")) | ||
| self.assertTrue(breakpoint and | ||
| breakpoint.GetNumLocations() == 1, | ||
| VALID_BREAKPOINT) | ||
|
|
||
| error = lldb.SBError() | ||
| launch_info = lldb.SBLaunchInfo(None) | ||
| process = target.Launch(launch_info, error) | ||
| self.assertTrue(process, PROCESS_IS_VALID) | ||
|
|
||
| # Did we hit our breakpoint? | ||
| threads = lldbutil.get_threads_stopped_at_breakpoint(process, | ||
| breakpoint) | ||
| self.assertTrue( | ||
| len(threads) == 1, | ||
| "There should be a thread stopped at our breakpoint") | ||
|
|
||
| self.assertTrue(breakpoint.GetHitCount() == 1) | ||
|
|
||
| thread = threads[0] | ||
|
|
||
| # Here's what we expect to see in the backtrace: | ||
| # frame #0: ... a.out`sink() at main.cpp:13:4 [opt] | ||
| # frame #1: ... a.out`func3() at main.cpp:14:1 [opt] [artificial] | ||
| # frame #2: ... a.out`func2() at main.cpp:18:62 [opt] | ||
| # frame #3: ... a.out`func1() at main.cpp:18:85 [opt] [artificial] | ||
| # frame #4: ... a.out`main at main.cpp:23:3 [opt] | ||
| names = ["sink()", "func3()", "func2()", "func1()", "main"] | ||
| artificiality = [False, True, False, True, False] | ||
| for idx, (name, is_artificial) in enumerate(zip(names, artificiality)): | ||
| frame = thread.GetFrameAtIndex(idx) | ||
| self.assertTrue(frame.GetDisplayFunctionName() == name) | ||
| self.assertTrue(frame.IsArtificial() == is_artificial) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; /* break here */ | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func2(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main() { | ||
| func1(); /* regular */ | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; //% self.filecheck("finish", "main.cpp", "-implicit-check-not=artificial") | ||
| // CHECK: stop reason = step out | ||
| // CHECK-NEXT: Stepped out past: frame #1: 0x{{[0-9a-f]+}} a.out`func3{{.*}} [opt] [artificial] | ||
| // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`func2{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func2(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main() { | ||
| func1(); /* regular */ | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| """ | ||
| Test SB API support for identifying artificial (tail call) frames. | ||
| """ | ||
|
|
||
| import lldb | ||
| import lldbsuite.test.lldbutil as lldbutil | ||
| from lldbsuite.test.lldbtest import * | ||
|
|
||
| class TestArtificialFrameThreadStepOut1(TestBase): | ||
| mydir = TestBase.compute_mydir(__file__) | ||
|
|
||
| # If your test case doesn't stress debug info, the | ||
| # set this to true. That way it won't be run once for | ||
| # each debug info format. | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def prepare_thread(self): | ||
| exe = self.getBuildArtifact("a.out") | ||
|
|
||
| # Create a target by the debugger. | ||
| target = self.dbg.CreateTarget(exe) | ||
| self.assertTrue(target, VALID_TARGET) | ||
|
|
||
| breakpoint = target.BreakpointCreateBySourceRegex("break here", | ||
| lldb.SBFileSpec("main.cpp")) | ||
| self.assertTrue(breakpoint and | ||
| breakpoint.GetNumLocations() == 1, | ||
| VALID_BREAKPOINT) | ||
|
|
||
| error = lldb.SBError() | ||
| launch_info = lldb.SBLaunchInfo(None) | ||
| process = target.Launch(launch_info, error) | ||
| self.assertTrue(process, PROCESS_IS_VALID) | ||
|
|
||
| # Did we hit our breakpoint? | ||
| threads = lldbutil.get_threads_stopped_at_breakpoint(process, | ||
| breakpoint) | ||
| self.assertTrue( | ||
| len(threads) == 1, | ||
| "There should be a thread stopped at our breakpoint") | ||
|
|
||
| self.assertTrue(breakpoint.GetHitCount() == 1) | ||
|
|
||
| thread = threads[0] | ||
|
|
||
| # Here's what we expect to see in the backtrace: | ||
| # frame #0: ... a.out`sink() at main.cpp:13:4 [opt] | ||
| # frame #1: ... a.out`func3() at main.cpp:14:1 [opt] [artificial] | ||
| # frame #2: ... a.out`func2() at main.cpp:18:62 [opt] | ||
| # frame #3: ... a.out`func1() at main.cpp:18:85 [opt] [artificial] | ||
| # frame #4: ... a.out`main at main.cpp:23:3 [opt] | ||
| return thread | ||
|
|
||
| def test_stepping_out_past_artificial_frame(self): | ||
| self.build() | ||
| thread = self.prepare_thread() | ||
|
|
||
| # Frame #0's ancestor is artificial. Stepping out should move to | ||
| # frame #2, because we behave as-if artificial frames were not present. | ||
| thread.StepOut() | ||
| frame2 = thread.GetSelectedFrame() | ||
| self.assertTrue(frame2.GetDisplayFunctionName() == "func2()") | ||
| self.assertFalse(frame2.IsArtificial()) | ||
|
|
||
| # Ditto: stepping out of frame #2 should move to frame #4. | ||
| thread.StepOut() | ||
| frame4 = thread.GetSelectedFrame() | ||
| self.assertTrue(frame4.GetDisplayFunctionName() == "main") | ||
| self.assertFalse(frame2.IsArtificial()) | ||
|
|
||
| def test_return_past_artificial_frame(self): | ||
| self.build() | ||
| thread = self.prepare_thread() | ||
|
|
||
| value = lldb.SBValue() | ||
|
|
||
| # Frame #0's ancestor is artificial. Returning from frame #0 should move | ||
| # to frame #2. | ||
| thread.ReturnFromFrame(thread.GetSelectedFrame(), value) | ||
| frame2 = thread.GetSelectedFrame() | ||
| self.assertTrue(frame2.GetDisplayFunctionName() == "func2()") | ||
| self.assertFalse(frame2.IsArtificial()) | ||
|
|
||
| # Ditto: stepping out of frame #2 should move to frame #4. | ||
| thread.ReturnFromFrame(thread.GetSelectedFrame(), value) | ||
| frame4 = thread.GetSelectedFrame() | ||
| self.assertTrue(frame4.GetDisplayFunctionName() == "main") | ||
| self.assertFalse(frame2.IsArtificial()) | ||
|
|
||
| def setUp(self): | ||
| # Call super's setUp(). | ||
| TestBase.setUp(self) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; // break here | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func2(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main() { | ||
| func1(); /* regular */ | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| LEVEL = ../../../make | ||
| CXX_SOURCES := main.cpp | ||
| include $(LEVEL)/Makefile.rules | ||
| CXXFLAGS += -g -O1 -glldb |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lldbsuite.test import lldbinline | ||
| from lldbsuite.test import decorators | ||
|
|
||
| lldbinline.MakeInlineTest(__file__, globals(), | ||
| [decorators.skipUnlessHasCallSiteInfo]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| //===-- main.cpp ------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| volatile int x; | ||
|
|
||
| void __attribute__((noinline)) sink() { | ||
| x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial") | ||
| // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt] | ||
| // CHECK-NEXT: frame #1: 0x{{[0-9a-f]+}} a.out`func3{{.*}} [opt] [artificial] | ||
| // CHECK-NEXT: frame #2: 0x{{[0-9a-f]+}} a.out`func2{{.*}} [opt] | ||
| // CHECK-NEXT: frame #3: 0x{{[0-9a-f]+}} a.out`func1{{.*}} [opt] [artificial] | ||
| // CHECK-NEXT: frame #4: 0x{{[0-9a-f]+}} a.out`main{{.*}} [opt] | ||
| } | ||
|
|
||
| void __attribute__((noinline)) func3() { sink(); /* tail */ } | ||
|
|
||
| void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ } | ||
|
|
||
| void __attribute__((noinline)) func1() { func2(); /* tail */ } | ||
|
|
||
| int __attribute__((disable_tail_calls)) main() { | ||
| func1(); /* regular */ | ||
| return 0; | ||
| } |