Skip to content

Commit 28daec0

Browse files
committed
Add InstructionRange.overlapsWithPath() utility.
1 parent 2bdaa68 commit 28daec0

File tree

3 files changed

+543
-3
lines changed

3 files changed

+543
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,18 @@ public func registerOptimizerTests() {
160160
enclosingValuesTest,
161161
forwardingDefUseTest,
162162
forwardingUseDefTest,
163+
gatherCallSitesTest,
163164
interiorLivenessTest,
164165
lifetimeDependenceScopeTest,
165166
lifetimeDependenceUseTest,
166167
linearLivenessTest,
167168
localVariableReachableUsesTest,
168169
localVariableReachingAssignmentsTest,
169170
parseTestSpecificationTest,
170-
variableIntroducerTest,
171-
gatherCallSitesTest,
171+
rangeOverlapsPathTest,
172+
rewrittenCallerBodyTest,
172173
specializedFunctionSignatureAndBodyTest,
173-
rewrittenCallerBodyTest
174+
variableIntroducerTest
174175
)
175176

176177
// Finally register the thunk they all call through.

0 commit comments

Comments
 (0)