@@ -181,3 +181,122 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
181181 blockRange. deinitialize ( )
182182 }
183183}
184+
185+ extension InstructionRange {
186+ enum PathOverlap {
187+ // range: ---
188+ // | pathBegin
189+ // | |
190+ // | pathEnd
191+ // ---
192+ case containsPath
193+
194+ // range: ---
195+ // | pathBegin
196+ // --- |
197+ // pathEnd
198+ case containsBegin
199+
200+ // pathBegin
201+ // range: --- |
202+ // | pathEnd
203+ // ---
204+ case containsEnd
205+
206+ // pathBegin
207+ // range: --- |
208+ // | |
209+ // --- |
210+ // pathEnd
211+ case overlappedByPath
212+
213+ // either: pathBegin
214+ // |
215+ // pathEnd
216+ // range: ---
217+ // |
218+ // ---
219+ // or: pathBegin
220+ // |
221+ // pathEnd
222+ case disjoint
223+ }
224+
225+ /// Return true if any exclusive path from `begin` to `end` includes an instruction in this exclusive range.
226+ ///
227+ /// Returns .containsBegin, if this range has the same begin and end as the path.
228+ ///
229+ /// Precondition: `begin` dominates `end`.
230+ func overlaps( pathBegin: Instruction , pathEnd: Instruction , _ context: some Context ) -> PathOverlap {
231+ assert ( pathBegin != pathEnd, " expect an exclusive path " )
232+ if contains ( pathBegin) {
233+ // Note: pathEnd != self.begin here since self.contains(pathBegin)
234+ if contains ( pathEnd) { return . containsPath }
235+ return . containsBegin
236+ }
237+ if contains ( pathEnd) {
238+ if let rangeBegin = self . begin, rangeBegin == pathEnd {
239+ return . disjoint
240+ }
241+ return . containsEnd
242+ }
243+ // Neither end-point is contained. If a backward path walk encouters this range, then it must overlap this
244+ // range. Otherwise, it is disjoint.
245+ var backwardBlocks = BasicBlockWorklist ( context)
246+ defer { backwardBlocks. deinitialize ( ) }
247+ backwardBlocks. pushIfNotVisited ( pathEnd. parentBlock)
248+ while let block = backwardBlocks. pop ( ) {
249+ if blockRange. inclusiveRangeContains ( block) {
250+ // This range overlaps with this block, but there are still three possibilities:
251+ // (1) range, pathBegin, pathEnd = disjoint (range might not begin in this block)
252+ // (2) pathBegin, pathEnd, range = disjoint (pathBegin might not be in this block)
253+ // (3) pathBegin, range, pathEnd = overlappedByPath (range or pathBegin might not be in this block)
254+ //
255+ // Walk backward from pathEnd to find either pathBegin or an instruction in this range.
256+ // Both this range and the path may or may not begin in this block.
257+ let endInBlock = block == pathEnd. parentBlock ? pathEnd : block. terminator
258+ for inst in ReverseInstructionList ( first: endInBlock) {
259+ // Check pathBegin first because the range is exclusive.
260+ if inst == pathBegin {
261+ break
262+ }
263+ // Check inclusiveRangeContains() in case the range end is the first instruction in this block.
264+ if inclusiveRangeContains ( inst) {
265+ return . overlappedByPath
266+ }
267+ }
268+ // No instructions in this range occur between pathBegin and pathEnd.
269+ return . disjoint
270+ }
271+ // No range blocks have been reached.
272+ if block == pathBegin. parentBlock {
273+ return . disjoint
274+ }
275+ backwardBlocks. pushIfNotVisited ( contentsOf: block. predecessors)
276+ }
277+ fatalError ( " begin: \( pathBegin) \n must dominate end: \( pathEnd) " )
278+ }
279+ }
280+
281+ let rangeOverlapsPathTest = FunctionTest ( " range_overlaps_path " ) {
282+ function, arguments, context in
283+ let rangeValue = arguments. takeValue ( )
284+ print ( " Range of: \( rangeValue) " )
285+ var range = computeLinearLiveness ( for: rangeValue, context)
286+ defer { range. deinitialize ( ) }
287+ let pathInst = arguments. takeInstruction ( )
288+ print ( " Path begin: \( pathInst) " )
289+ if let pathBegin = pathInst as? ScopedInstruction {
290+ for end in pathBegin. endInstructions {
291+ print ( " Overlap kind: " , range. overlaps ( pathBegin: pathInst, pathEnd: end, context) )
292+ }
293+ return
294+ }
295+ if let pathValue = pathInst as? SingleValueInstruction , pathValue. ownership == . owned {
296+ for end in pathValue. uses. endingLifetime {
297+ print ( " Overlap kind: " , range. overlaps ( pathBegin: pathInst, pathEnd: end. instruction, context) )
298+ }
299+ return
300+ }
301+ print ( " Test specification error: not a scoped or owned instruction: \( pathInst) " )
302+ }
0 commit comments