mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add InstructionRange.overlapsWithPath() utility.
This commit is contained in:
@@ -181,3 +181,122 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
|
||||
blockRange.deinitialize()
|
||||
}
|
||||
}
|
||||
|
||||
extension InstructionRange {
|
||||
enum PathOverlap {
|
||||
// range: ---
|
||||
// | pathBegin
|
||||
// | |
|
||||
// | pathEnd
|
||||
// ---
|
||||
case containsPath
|
||||
|
||||
// range: ---
|
||||
// | pathBegin
|
||||
// --- |
|
||||
// pathEnd
|
||||
case containsBegin
|
||||
|
||||
// pathBegin
|
||||
// range: --- |
|
||||
// | pathEnd
|
||||
// ---
|
||||
case containsEnd
|
||||
|
||||
// pathBegin
|
||||
// range: --- |
|
||||
// | |
|
||||
// --- |
|
||||
// pathEnd
|
||||
case overlappedByPath
|
||||
|
||||
// either: pathBegin
|
||||
// |
|
||||
// pathEnd
|
||||
// range: ---
|
||||
// |
|
||||
// ---
|
||||
// or: pathBegin
|
||||
// |
|
||||
// pathEnd
|
||||
case disjoint
|
||||
}
|
||||
|
||||
/// Return true if any exclusive path from `begin` to `end` includes an instruction in this exclusive range.
|
||||
///
|
||||
/// Returns .containsBegin, if this range has the same begin and end as the path.
|
||||
///
|
||||
/// Precondition: `begin` dominates `end`.
|
||||
func overlaps(pathBegin: Instruction, pathEnd: Instruction, _ context: some Context) -> PathOverlap {
|
||||
assert(pathBegin != pathEnd, "expect an exclusive path")
|
||||
if contains(pathBegin) {
|
||||
// Note: pathEnd != self.begin here since self.contains(pathBegin)
|
||||
if contains(pathEnd) { return .containsPath }
|
||||
return .containsBegin
|
||||
}
|
||||
if contains(pathEnd) {
|
||||
if let rangeBegin = self.begin, rangeBegin == pathEnd {
|
||||
return .disjoint
|
||||
}
|
||||
return .containsEnd
|
||||
}
|
||||
// Neither end-point is contained. If a backward path walk encouters this range, then it must overlap this
|
||||
// range. Otherwise, it is disjoint.
|
||||
var backwardBlocks = BasicBlockWorklist(context)
|
||||
defer { backwardBlocks.deinitialize() }
|
||||
backwardBlocks.pushIfNotVisited(pathEnd.parentBlock)
|
||||
while let block = backwardBlocks.pop() {
|
||||
if blockRange.inclusiveRangeContains(block) {
|
||||
// This range overlaps with this block, but there are still three possibilities:
|
||||
// (1) range, pathBegin, pathEnd = disjoint (range might not begin in this block)
|
||||
// (2) pathBegin, pathEnd, range = disjoint (pathBegin might not be in this block)
|
||||
// (3) pathBegin, range, pathEnd = overlappedByPath (range or pathBegin might not be in this block)
|
||||
//
|
||||
// Walk backward from pathEnd to find either pathBegin or an instruction in this range.
|
||||
// Both this range and the path may or may not begin in this block.
|
||||
let endInBlock = block == pathEnd.parentBlock ? pathEnd : block.terminator
|
||||
for inst in ReverseInstructionList(first: endInBlock) {
|
||||
// Check pathBegin first because the range is exclusive.
|
||||
if inst == pathBegin {
|
||||
break
|
||||
}
|
||||
// Check inclusiveRangeContains() in case the range end is the first instruction in this block.
|
||||
if inclusiveRangeContains(inst) {
|
||||
return .overlappedByPath
|
||||
}
|
||||
}
|
||||
// No instructions in this range occur between pathBegin and pathEnd.
|
||||
return .disjoint
|
||||
}
|
||||
// No range blocks have been reached.
|
||||
if block == pathBegin.parentBlock {
|
||||
return .disjoint
|
||||
}
|
||||
backwardBlocks.pushIfNotVisited(contentsOf: block.predecessors)
|
||||
}
|
||||
fatalError("begin: \(pathBegin)\n must dominate end: \(pathEnd)")
|
||||
}
|
||||
}
|
||||
|
||||
let rangeOverlapsPathTest = FunctionTest("range_overlaps_path") {
|
||||
function, arguments, context in
|
||||
let rangeValue = arguments.takeValue()
|
||||
print("Range of: \(rangeValue)")
|
||||
var range = computeLinearLiveness(for: rangeValue, context)
|
||||
defer { range.deinitialize() }
|
||||
let pathInst = arguments.takeInstruction()
|
||||
print("Path begin: \(pathInst)")
|
||||
if let pathBegin = pathInst as? ScopedInstruction {
|
||||
for end in pathBegin.endInstructions {
|
||||
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end, context))
|
||||
}
|
||||
return
|
||||
}
|
||||
if let pathValue = pathInst as? SingleValueInstruction, pathValue.ownership == .owned {
|
||||
for end in pathValue.uses.endingLifetime {
|
||||
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end.instruction, context))
|
||||
}
|
||||
return
|
||||
}
|
||||
print("Test specification error: not a scoped or owned instruction: \(pathInst)")
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ public func registerOptimizerTests() {
|
||||
enclosingValuesTest,
|
||||
forwardingDefUseTest,
|
||||
forwardingUseDefTest,
|
||||
gatherCallSitesTest,
|
||||
interiorLivenessTest,
|
||||
lifetimeDependenceScopeTest,
|
||||
lifetimeDependenceUseTest,
|
||||
@@ -167,10 +168,10 @@ public func registerOptimizerTests() {
|
||||
localVariableReachableUsesTest,
|
||||
localVariableReachingAssignmentsTest,
|
||||
parseTestSpecificationTest,
|
||||
variableIntroducerTest,
|
||||
gatherCallSitesTest,
|
||||
rangeOverlapsPathTest,
|
||||
rewrittenCallerBodyTest,
|
||||
specializedFunctionSignatureAndBodyTest,
|
||||
rewrittenCallerBodyTest
|
||||
variableIntroducerTest
|
||||
)
|
||||
|
||||
// Finally register the thunk they all call through.
|
||||
|
||||
420
test/SILOptimizer/range_unit.sil
Normal file
420
test/SILOptimizer/range_unit.sil
Normal file
@@ -0,0 +1,420 @@
|
||||
// RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
class C {}
|
||||
|
||||
// CHECK-LABEL: testIdentity: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsBegin
|
||||
sil [ossa] @testIdentity : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %range"
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsPath1: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsPath
|
||||
sil [ossa] @testContainsPath1 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
%path = begin_borrow %0
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsPath2: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsPath
|
||||
sil [ossa] @testContainsPath2 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsPath3: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsPath
|
||||
sil [ossa] @testContainsPath3 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsPath4: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsPath
|
||||
sil [ossa] @testContainsPath4 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %path
|
||||
br bb3
|
||||
bb3:
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsBegin1: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsBegin
|
||||
sil [ossa] @testContainsBegin1 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
%path = begin_borrow %0
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsBegin2: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsBegin
|
||||
sil [ossa] @testContainsBegin2 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsBegin3: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsBegin
|
||||
sil [ossa] @testContainsBegin3 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsBegin4: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsBegin
|
||||
sil [ossa] @testContainsBegin4 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%path = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %range
|
||||
br bb3
|
||||
bb3:
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsEnd1: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsEnd
|
||||
sil [ossa] @testContainsEnd1 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
%range = begin_borrow %0
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsEnd2: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsEnd
|
||||
sil [ossa] @testContainsEnd2 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsEnd3: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsEnd
|
||||
sil [ossa] @testContainsEnd3 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %path
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testContainsEnd4: range_overlaps_path
|
||||
// CHECK: Overlap kind: containsEnd
|
||||
sil [ossa] @testContainsEnd4 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %path
|
||||
br bb3
|
||||
bb3:
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testOverlappedByPath1: range_overlaps_path
|
||||
// CHECK: Overlap kind: overlappedByPath
|
||||
sil [ossa] @testOverlappedByPath1 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testOverlappedByPath2: range_overlaps_path
|
||||
// CHECK: Overlap kind: overlappedByPath
|
||||
sil [ossa] @testOverlappedByPath2 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testOverlappedByPath3: range_overlaps_path
|
||||
// CHECK: Overlap kind: overlappedByPath
|
||||
sil [ossa] @testOverlappedByPath3 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %range
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testOverlappedByPath4: range_overlaps_path
|
||||
// CHECK: Overlap kind: overlappedByPath
|
||||
sil [ossa] @testOverlappedByPath4 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
%range = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %range
|
||||
br bb3
|
||||
bb3:
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testOverlappedByPath5: range_overlaps_path
|
||||
// CHECK: Overlap kind: overlappedByPath
|
||||
sil [ossa] @testOverlappedByPath5 : $@convention(thin) (@owned C, @owned C) -> () {
|
||||
entry(%0 : @owned $C, %1 : @owned $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = move_value %0
|
||||
%range = move_value %1
|
||||
%tuple = tuple (%path, %range)
|
||||
destroy_value %tuple
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint1: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint1 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
end_borrow %path
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint2: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint2 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
%path = begin_borrow %0
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint3: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint3 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint4: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint4 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
end_borrow %path
|
||||
%range = begin_borrow %0
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint5: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint5 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
end_borrow %range
|
||||
%path = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint6: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint6 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
end_borrow %path
|
||||
%range = begin_borrow %0
|
||||
br bb2
|
||||
bb2:
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint7: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint7 : $@convention(thin) (@guaranteed C) -> () {
|
||||
entry(%0 : @guaranteed $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = begin_borrow %0
|
||||
br bb1
|
||||
bb1:
|
||||
end_borrow %path
|
||||
br bb2
|
||||
bb2:
|
||||
%range = begin_borrow %0
|
||||
br bb3
|
||||
bb3:
|
||||
end_borrow %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint8: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint8 : $@convention(thin) (@owned C) -> () {
|
||||
entry(%0 : @owned $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%path = move_value %0
|
||||
%range = move_value %path
|
||||
destroy_value %range
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testDisjoint9: range_overlaps_path
|
||||
// CHECK: Overlap kind: disjoint
|
||||
sil [ossa] @testDisjoint9 : $@convention(thin) (@owned C) -> () {
|
||||
entry(%0 : @owned $C):
|
||||
specify_test "range_overlaps_path %range %path"
|
||||
%range = move_value %0
|
||||
%path = move_value %range
|
||||
destroy_value %path
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
Reference in New Issue
Block a user