File: liveness_incomplete_unit.sil

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (156 lines) | stat: -rw-r--r-- 5,076 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
//
// These tests rely on "incomplete" OSSA lifetimes. Incomplete OSSA
// lifetimes are invalid SIL, but the OSSA liveness utilities still
// need to handle them! SILGen and textual SIL is allowed to produce
// incomplete lifetimes. The OSSA liveness utilities need to be able
// to fixup those incomplete lifetimes. So the utilities need to
// handle invalid SIL in order to produce valid SIL.

sil_stage canonical

import Builtin

enum FakeOptional<T> {
case none
case some(T)
}

enum Never {}

class C {}

// CHECK-LABEL: testDeadTerminatorResult: ssa_liveness
// CHECK-LABEL: SSA lifetime analysis: %{{.*}} = argument of bb1 : $C
// CHECK: bb1: LiveWithin
// CHECK-NOT: "last user"
// CHECK-NOT: "boundary edge"
// CHECK: dead def: %{{.*}} = argument of bb1 : $C
sil [ossa] @testDeadTerminatorResult : $@convention(thin) (@owned FakeOptional<C>) -> () {
bb0(%0 : @owned $FakeOptional<C>):
  switch_enum %0 : $FakeOptional<C>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2

bb1(%payload : @owned $C):
  specify_test "ssa_liveness @trace[0]"
  debug_value [trace] %payload : $C
  unreachable

bb2:
  %99 = tuple()
  return %99 : $()
}

// CHECK-LABEL: testDeadPhi: ssa_liveness
// CHECK: SSA lifetime analysis: %{{.*}} = argument of bb3 : $C
// CHECK: bb3: LiveWithin
// CHECK: dead def: %{{.*}} = argument of bb3 : $C
sil [ossa] @testDeadPhi : $@convention(thin) (@owned C, @owned C) -> () {
bb0(%0 : @owned $C, %1 : @owned $C):
  cond_br undef, bb1, bb2

bb1:
  br bb3(%0 : $C)

bb2:
  br bb3(%1 : $C)

bb3(%2 : @owned $C):
  specify_test "ssa_liveness @trace[0]"
  debug_value [trace] %2 : $C
  unreachable
}

// CHECK-LABEL: testDeadInstruction: ssa_liveness
// CHECK: SSA lifetime analysis:   %{{.*}} = copy_value %0 : $C
// CHECK: bb0: LiveWithin
// CHECK: dead def:   %{{.*}} = copy_value %0 : $C
sil [ossa] @testDeadInstruction : $@convention(thin) (@guaranteed C) -> () {
bb0(%0 : @guaranteed $C):
  %1 = copy_value %0 : $C
  specify_test "ssa_liveness @trace[0]"
  debug_value [trace] %1 : $C
  unreachable
}

// A single instruction occurs twice on the same liveness
// boundary. Once as a last use, and once as a dead def.
// This is a particularly problematic corner case.
//
// CHECK-LABEL: testDeadSelfKill: multidef_liveness
// CHECK: MultiDef lifetime analysis:
// CHECK: def value: %1 = argument of bb1 : $C
// CHECK: def value: [[V:%.*]] = move_value %1 : $C
// CHECK: bb1: LiveWithin
// CHECK: lifetime-ending user:   [[V]] = move_value %1 : $C
// CHECK: last user:   [[V]] = move_value %1 : $C
// CHECK: dead def:   [[V]] = move_value %1 : $C
sil [ossa] @testDeadSelfKill : $@convention(thin) () -> () {
bb0:
  br bb3

bb1(%1 : @owned $C):
  specify_test "multidef_liveness @trace[0] @trace[1]"
  debug_value [trace] %1 : $C
  %2 = move_value %1 : $C
  debug_value [trace] %2 : $C
  unreachable

bb3:
  %99 = tuple()
  return %99 : $()
}

sil @genericReturn : $@convention(thin) <τ_0_0> (@guaranteed C) -> @out τ_0_0

// The store_borrow scope has an inner load_borrow scope, which is
// incomplete. Since the store_borrow produces an address, the load
// borrow is considered a ScopedAddressUse, and all of its uses,
// including the Apply will contribute to the store_borrow liveness.
// CHECK-LABEL: testInnerUnreachable: scoped-address-liveness
// CHECK: Scoped address analysis: [[SB:%.*]] = store_borrow %0
// CHECK: bb0: LiveWithin
// CHECK-NEXT: regular user: %{{.*}} = apply
// CHECK-NEXT: last user:    %{{.*}} = apply
sil shared [ossa] @testInnerUnreachable : $@convention(thin) (@guaranteed C, @thick Never.Type) -> () {
bb0(%0 : @guaranteed $C, %1 : $@thick Never.Type):
  %2 = alloc_stack $C
  %3 = store_borrow %0 to %2 : $*C
  specify_test "scoped-address-liveness @trace[0]"
  debug_value [trace] %3 : $*C
  %5 = load_borrow %3 : $*C
  %6 = function_ref @genericReturn : $@convention(thin) <τ_0_0> (@guaranteed C) -> @out τ_0_0
  %7 = alloc_stack $Never
  %8 = apply %6<Never>(%7, %5) : $@convention(thin) <τ_0_0> (@guaranteed C) -> @out τ_0_0
  unreachable
}

// A dead-end block with a def can still be a boundary edge. This can
// only happen in OSSA with incomplete lifetimes.
//
// CHECK-LABEL: testMultiDefDeadDefBoundaryEdge: multidef_liveness
// CHECK: MultiDef lifetime analysis:
// CHECK:   def value: [[CP0:%.*]] = copy_value %0 : $C
// CHECK:   def value: [[CP3:%.*]] = copy_value %0 : $C
// CHECK: bb0: LiveOut
// CHECK: bb1: LiveWithin
// CHECK: bb2: LiveWithin
// CHECK: last user:   destroy_value [[CP0]] : $C
// CHECK-NEXT: boundary edge: bb1
// CHECK-NEXT: dead def: [[CP3]] = copy_value %0 : $C
sil [ossa] @testMultiDefDeadDefBoundaryEdge : $@convention(thin) (@guaranteed C) -> () {
bb0(%0 : @guaranteed $C):
  %copy0 = copy_value %0 : $C
  specify_test "multidef_liveness @trace[0] @trace[1]"
  debug_value [trace] %copy0 : $C
  cond_br undef, bb1, bb3

bb1:
  %dead = copy_value %0 : $C
  debug_value [trace] %dead : $C
  unreachable

bb3:
  destroy_value %copy0 : $C
  %99 = tuple()
  return %99 : $()
}