mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #85334 from eeckstein/mandatory-destroy-hoisting
Optimizer: make destroy hoisting a mandatory pass
This commit is contained in:
@@ -29,6 +29,7 @@ swift_compiler_sources(Optimizer
|
||||
LoopInvariantCodeMotion.swift
|
||||
ObjectOutliner.swift
|
||||
ObjCBridgingOptimization.swift
|
||||
MandatoryDestroyHoisting.swift
|
||||
MergeCondFails.swift
|
||||
NamedReturnValueOptimization.swift
|
||||
RedundantLoadElimination.swift
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
//===--- MandatoryDestroyHoisting.swift ------------------------------------==//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import SIL
|
||||
|
||||
/// Hoists `destroy_value` instructions for non-lexical values.
|
||||
///
|
||||
/// ```
|
||||
/// %1 = some_ownedValue
|
||||
/// ...
|
||||
/// last_use(%1)
|
||||
/// ... // other instructions
|
||||
/// destroy_value %1
|
||||
/// ```
|
||||
/// ->
|
||||
/// ```
|
||||
/// %1 = some_ownedValue
|
||||
/// ...
|
||||
/// last_use(%1)
|
||||
/// destroy_value %1 // <- moved after the last use
|
||||
/// ... // other instructions
|
||||
/// ```
|
||||
///
|
||||
/// In contrast to non-mandatory optimization passes, this is the only pass which hoists destroys
|
||||
/// over deinit-barriers. This ensures consistent behavior in -Onone and optimized builds.
|
||||
///
|
||||
///
|
||||
let mandatoryDestroyHoisting = FunctionPass(name: "mandatory-destroy-hoisting") {
|
||||
(function: Function, context: FunctionPassContext) in
|
||||
|
||||
var endAccesses = Stack<EndAccessInst>(context)
|
||||
defer { endAccesses.deinitialize() }
|
||||
endAccesses.append(contentsOf: function.instructions.compactMap{ $0 as? EndAccessInst })
|
||||
|
||||
for block in function.blocks {
|
||||
for arg in block.arguments {
|
||||
hoistDestroys(of: arg, endAccesses: endAccesses, context)
|
||||
if !context.continueWithNextSubpassRun() {
|
||||
return
|
||||
}
|
||||
}
|
||||
for inst in block.instructions {
|
||||
for result in inst.results {
|
||||
hoistDestroys(of: result, endAccesses: endAccesses, context)
|
||||
if !context.continueWithNextSubpassRun(for: inst) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func hoistDestroys(of value: Value, endAccesses: Stack<EndAccessInst>, _ context: FunctionPassContext) {
|
||||
guard value.ownership == .owned,
|
||||
|
||||
// We must not violate side-effect dependencies of non-copyable deinits.
|
||||
// Therefore we don't handle non-copyable values.
|
||||
!value.type.isMoveOnly,
|
||||
|
||||
// Just a shortcut to avoid all the computations if there is no destroy at all.
|
||||
!value.uses.users(ofType: DestroyValueInst.self).isEmpty,
|
||||
|
||||
// Hoisting destroys is only legal for non-lexical lifetimes.
|
||||
!value.isInLexicalLiverange(context),
|
||||
|
||||
// Avoid compromimsing debug-info in Onone builds for source-level variables with non-lexical lifetimes.
|
||||
// For example COW types, like Array, which are "eager-move" and therefore not lexical.
|
||||
!needPreserveDebugInfo(of: value, context)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
guard var liverange = Liverange(of: value, context) else {
|
||||
return
|
||||
}
|
||||
defer { liverange.deinitialize() }
|
||||
|
||||
// We must not move a destroy into an access scope, because the deinit can have an access scope as well.
|
||||
// And that would cause a false exclusivite error at runtime.
|
||||
liverange.extendWithAccessScopes(of: endAccesses)
|
||||
|
||||
var aliveDestroys = insertNewDestroys(of: value, in: liverange)
|
||||
defer { aliveDestroys.deinitialize() }
|
||||
|
||||
removeOldDestroys(of: value, ignoring: aliveDestroys, context)
|
||||
}
|
||||
|
||||
private func insertNewDestroys(of value: Value, in liverange: Liverange) -> InstructionSet {
|
||||
var aliveDestroys = InstructionSet(liverange.context)
|
||||
|
||||
if liverange.nonDestroyingUsers.isEmpty {
|
||||
// Handle the corner case where the value has no use at all (beside the destroy).
|
||||
immediatelyDestroy(value: value, ifIn: liverange, &aliveDestroys)
|
||||
return aliveDestroys
|
||||
}
|
||||
// Insert new destroys at the end of the pruned liverange.
|
||||
for user in liverange.nonDestroyingUsers {
|
||||
insertDestroy(of: value, after: user, ifIn: liverange, &aliveDestroys)
|
||||
}
|
||||
// Also, we need new destroys at exit edges from the pruned liverange.
|
||||
for exitInst in liverange.prunedLiverange.exits {
|
||||
insertDestroy(of: value, before: exitInst, ifIn: liverange, &aliveDestroys)
|
||||
}
|
||||
return aliveDestroys
|
||||
}
|
||||
|
||||
private func removeOldDestroys(of value: Value, ignoring: InstructionSet, _ context: FunctionPassContext) {
|
||||
for destroy in value.uses.users(ofType: DestroyValueInst.self) {
|
||||
if !ignoring.contains(destroy) {
|
||||
context.erase(instruction: destroy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func insertDestroy(of value: Value,
|
||||
before insertionPoint: Instruction,
|
||||
ifIn liverange: Liverange,
|
||||
_ aliveDestroys: inout InstructionSet
|
||||
) {
|
||||
guard liverange.isOnlyInExtendedLiverange(insertionPoint) else {
|
||||
return
|
||||
}
|
||||
if let existingDestroy = insertionPoint as? DestroyValueInst, existingDestroy.destroyedValue == value {
|
||||
aliveDestroys.insert(existingDestroy)
|
||||
return
|
||||
}
|
||||
let builder = Builder(before: insertionPoint, liverange.context)
|
||||
let newDestroy = builder.createDestroyValue(operand: value)
|
||||
aliveDestroys.insert(newDestroy)
|
||||
}
|
||||
|
||||
private func insertDestroy(of value: Value,
|
||||
after insertionPoint: Instruction,
|
||||
ifIn liverange: Liverange,
|
||||
_ aliveDestroys: inout InstructionSet
|
||||
) {
|
||||
if let next = insertionPoint.next {
|
||||
insertDestroy(of: value, before: next, ifIn: liverange, &aliveDestroys)
|
||||
} else {
|
||||
for succ in insertionPoint.parentBlock.successors {
|
||||
insertDestroy(of: value, before: succ.instructions.first!, ifIn: liverange, &aliveDestroys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func immediatelyDestroy(value: Value, ifIn liverange: Liverange, _ aliveDestroys: inout InstructionSet) {
|
||||
if let arg = value as? Argument {
|
||||
insertDestroy(of: value, before: arg.parentBlock.instructions.first!, ifIn: liverange, &aliveDestroys)
|
||||
} else {
|
||||
insertDestroy(of: value, after: value.definingInstruction!, ifIn: liverange, &aliveDestroys)
|
||||
}
|
||||
}
|
||||
|
||||
private func needPreserveDebugInfo(of value: Value, _ context: FunctionPassContext) -> Bool {
|
||||
if value.parentFunction.shouldOptimize {
|
||||
// No need to preserve debug info in optimized builds.
|
||||
return false
|
||||
}
|
||||
// Check if the value is associated to a source-level variable.
|
||||
if let inst = value.definingInstruction {
|
||||
return inst.findVarDecl() != nil
|
||||
}
|
||||
if let arg = value as? Argument {
|
||||
return arg.findVarDecl() != nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Represents the "extended" liverange of a value which is the range after the last uses until the
|
||||
/// final destroys of the value.
|
||||
///
|
||||
/// ```
|
||||
/// %1 = definition -+ -+
|
||||
/// ... | pruned liverange |
|
||||
/// last_use(%1) -+ -+ | full liverange
|
||||
/// ... no uses of %1 | extended liverange |
|
||||
/// destroy_value %1 -+ -+
|
||||
/// ```
|
||||
private struct Liverange {
|
||||
var nonDestroyingUsers: Stack<Instruction>
|
||||
var prunedLiverange: InstructionRange
|
||||
var fullLiverange: InstructionRange
|
||||
let context: FunctionPassContext
|
||||
|
||||
init?(of value: Value, _ context: FunctionPassContext) {
|
||||
guard let users = Stack(usersOf: value, context) else {
|
||||
return nil
|
||||
}
|
||||
self.nonDestroyingUsers = users
|
||||
|
||||
self.prunedLiverange = InstructionRange(for: value, context)
|
||||
prunedLiverange.insert(contentsOf: nonDestroyingUsers)
|
||||
|
||||
self.fullLiverange = InstructionRange(for: value, context)
|
||||
fullLiverange.insert(contentsOf: value.users)
|
||||
|
||||
self.context = context
|
||||
}
|
||||
|
||||
func isOnlyInExtendedLiverange(_ instruction: Instruction) -> Bool {
|
||||
fullLiverange.inclusiveRangeContains(instruction) && !prunedLiverange.inclusiveRangeContains(instruction)
|
||||
}
|
||||
|
||||
mutating func extendWithAccessScopes(of endAccesses: Stack<EndAccessInst>) {
|
||||
var changed: Bool
|
||||
// We need to do this repeatedly because if access scopes are not nested properly, an overlapping scope
|
||||
// can make a non-overlapping scope also overlapping, e.g.
|
||||
// ```
|
||||
// %1 = begin_access // overlapping
|
||||
// last_use %value
|
||||
// %2 = begin_access // initially not overlapping, but overlapping because of scope %1
|
||||
// end_access %1
|
||||
// end_access %2
|
||||
// destroy_value %value
|
||||
// ```
|
||||
repeat {
|
||||
changed = false
|
||||
for endAccess in endAccesses {
|
||||
if isOnlyInExtendedLiverange(endAccess), !isOnlyInExtendedLiverange(endAccess.beginAccess) {
|
||||
prunedLiverange.insert(endAccess)
|
||||
nonDestroyingUsers.append(endAccess)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
} while changed
|
||||
}
|
||||
|
||||
mutating func deinitialize() {
|
||||
fullLiverange.deinitialize()
|
||||
prunedLiverange.deinitialize()
|
||||
nonDestroyingUsers.deinitialize()
|
||||
}
|
||||
}
|
||||
|
||||
private extension Stack where Element == Instruction {
|
||||
init?(usersOf value: Value, _ context: FunctionPassContext) {
|
||||
var users = Stack<Instruction>(context)
|
||||
|
||||
var visitor = InteriorUseWalker(definingValue: value, ignoreEscape: false, visitInnerUses: true, context) {
|
||||
if $0.instruction is DestroyValueInst, $0.value == value {
|
||||
return .continueWalk
|
||||
}
|
||||
users.append($0.instruction)
|
||||
return .continueWalk
|
||||
}
|
||||
defer { visitor.deinitialize() }
|
||||
|
||||
guard visitor.visitUses() == .continueWalk else {
|
||||
users.deinitialize()
|
||||
return nil
|
||||
}
|
||||
self = users
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,7 @@ private func registerSwiftPasses() {
|
||||
registerPass(computeSideEffects, { computeSideEffects.run($0) })
|
||||
registerPass(diagnoseInfiniteRecursion, { diagnoseInfiniteRecursion.run($0) })
|
||||
registerPass(destroyHoisting, { destroyHoisting.run($0) })
|
||||
registerPass(mandatoryDestroyHoisting, { mandatoryDestroyHoisting.run($0) })
|
||||
registerPass(initializeStaticGlobalsPass, { initializeStaticGlobalsPass.run($0) })
|
||||
registerPass(objCBridgingOptimization, { objCBridgingOptimization.run($0) })
|
||||
registerPass(objectOutliner, { objectOutliner.run($0) })
|
||||
|
||||
@@ -72,10 +72,10 @@ extension Value {
|
||||
switch v {
|
||||
case let fw as ForwardingInstruction:
|
||||
worklist.pushIfNotVisited(contentsOf: fw.definedOperands.values)
|
||||
case let ot as OwnershipTransitionInstruction where !(ot is CopyingInstruction):
|
||||
worklist.pushIfNotVisited(ot.operand.value)
|
||||
case let bf as BorrowedFromInst:
|
||||
worklist.pushIfNotVisited(bf.borrowedValue)
|
||||
case let bb as BeginBorrowInst:
|
||||
worklist.pushIfNotVisited(bb.borrowedValue)
|
||||
case let arg as Argument:
|
||||
if let phi = Phi(arg) {
|
||||
worklist.pushIfNotVisited(contentsOf: phi.incomingValues)
|
||||
|
||||
@@ -71,6 +71,8 @@ PASS(BooleanLiteralFolding, "boolean-literal-folding",
|
||||
"Constant folds initializers of boolean literals")
|
||||
PASS(DestroyHoisting, "destroy-hoisting",
|
||||
"Hoist destroy_value instructions")
|
||||
PASS(MandatoryDestroyHoisting, "mandatory-destroy-hoisting",
|
||||
"Hoist destroy_value instructions for non-lexical values")
|
||||
PASS(DeadEndBlockDumper, "dump-deadendblocks",
|
||||
"Tests the DeadEndBlocks utility")
|
||||
PASS(EscapeInfoDumper, "dump-escape-info",
|
||||
|
||||
@@ -480,11 +480,16 @@ private:
|
||||
}
|
||||
|
||||
bool respectsDeinitBarriers() const {
|
||||
if (!currentDef->isLexical())
|
||||
auto &module = currentDef->getFunction()->getModule();
|
||||
|
||||
// The move-only checker (which runs in raw SIL) relies on ignoring deinit
|
||||
// barriers for non-lexical lifetimes.
|
||||
// Optimizations, on the other hand, should always respect deinit barriers.
|
||||
if (module.getStage() == SILStage::Raw && !currentDef->isLexical())
|
||||
return false;
|
||||
|
||||
if (currentDef->getFunction()->forceEnableLexicalLifetimes())
|
||||
return true;
|
||||
auto &module = currentDef->getFunction()->getModule();
|
||||
return module.getASTContext().SILOpts.supportsLexicalLifetimes(module);
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,8 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
|
||||
P.addOnoneSimplification();
|
||||
P.addInitializeStaticGlobals();
|
||||
|
||||
P.addMandatoryDestroyHoisting();
|
||||
|
||||
// MandatoryPerformanceOptimizations might create specializations that are not
|
||||
// used, and by being unused they are might have unspecialized applies.
|
||||
// Eliminate them via the DeadFunctionAndGlobalElimination in embedded Swift
|
||||
|
||||
@@ -408,19 +408,16 @@ static bool tryJoinIfDestroyConsumingUseInSameBlock(
|
||||
return true;
|
||||
}
|
||||
|
||||
// The lifetime of the original ends after the lifetime of the copy. If the
|
||||
// original is lexical, its lifetime must not be shortened through deinit
|
||||
// barriers.
|
||||
if (cvi->getOperand()->isLexical()) {
|
||||
// At this point, visitedInsts contains all the instructions between the
|
||||
// consuming use of the copy and the destroy. If any of those instructions
|
||||
// is a deinit barrier, it would be illegal to shorten the original lexical
|
||||
// value's lifetime to end at that consuming use. Bail if any are.
|
||||
if (llvm::any_of(visitedInsts, [](auto *inst) {
|
||||
return mayBeDeinitBarrierNotConsideringSideEffects(inst);
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
// The lifetime of the original ends after the lifetime of the copy.
|
||||
// Its lifetime must not be shortened through deinit barriers.
|
||||
// At this point, visitedInsts contains all the instructions between the
|
||||
// consuming use of the copy and the destroy. If any of those instructions
|
||||
// is a deinit barrier, it would be illegal to shorten the original lexical
|
||||
// value's lifetime to end at that consuming use. Bail if any are.
|
||||
if (llvm::any_of(visitedInsts, [](auto *inst) {
|
||||
return mayBeDeinitBarrierNotConsideringSideEffects(inst);
|
||||
}))
|
||||
return false;
|
||||
|
||||
// If we reached this point, isUseBetweenInstAndBlockEnd succeeded implying
|
||||
// that we found destroy_value to be after our consuming use. Noting that
|
||||
|
||||
@@ -990,7 +990,7 @@ void DestroyAddrHoisting::hoistDestroys(
|
||||
if (!continueWithNextSubpassRun(asi))
|
||||
return;
|
||||
changed |= ::hoistDestroys(asi,
|
||||
/*ignoreDeinitBarriers=*/!asi->isLexical(),
|
||||
/*ignoreDeinitBarriers=*/false,
|
||||
remainingDestroyAddrs, deleter, calleeAnalysis);
|
||||
}
|
||||
// Arguments enclose everything.
|
||||
|
||||
@@ -12,8 +12,7 @@ public func testPartialApply(_ obj: Test) {
|
||||
// CHECK: dynamic_method_br [[CURRIED1_OBJ:%.+]] : $@opened([[CURRIED1_EXISTENTIAL:.+]], any Test) Self, #Test.normalObject!foreign, [[CURRIED1_TRUE:[^,]+]], [[CURRIED1_FALSE:[^,]+]]
|
||||
// CHECK: [[CURRIED1_FALSE]]:
|
||||
// CHECK: [[CURRIED1_TRUE]]([[CURRIED1_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject):
|
||||
// CHECK: [[CURRIED1_OBJECT_COPY:%.*]] = copy_value [[CURRIED1_OBJ]]
|
||||
// CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJECT_COPY]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject
|
||||
// CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED1_METHOD]]([[CURRIED1_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject
|
||||
// CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @$syXlIego_ypIegr_TR : $@convention(thin) (@guaranteed @callee_guaranteed () -> @owned AnyObject) -> @out Any
|
||||
// CHECK: = partial_apply [callee_guaranteed] [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]])
|
||||
curried1()
|
||||
@@ -22,16 +21,14 @@ public func testPartialApply(_ obj: Test) {
|
||||
// CHECK: dynamic_method_br [[CURRIED2_OBJ:%.+]] : $@opened([[CURRIED2_EXISTENTIAL:.+]], any Test) Self, #Test.innerPointer!foreign, [[CURRIED2_TRUE:[^,]+]], [[CURRIED2_FALSE:[^,]+]]
|
||||
// CHECK: [[CURRIED2_FALSE]]:
|
||||
// CHECK: [[CURRIED2_TRUE]]([[CURRIED2_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer):
|
||||
// CHECK: [[CURRIED2_OBJ_COPY:%.*]] = copy_value [[CURRIED2_OBJ]]
|
||||
// CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer
|
||||
// CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[CURRIED2_METHOD]]([[CURRIED2_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer
|
||||
curried2()
|
||||
}
|
||||
if let prop1 = obj.normalObjectProp {
|
||||
// CHECK: dynamic_method_br [[PROP1_OBJ:%.+]] : $@opened([[PROP1_EXISTENTIAL:.+]], any Test) Self, #Test.normalObjectProp!getter.foreign, [[PROP1_TRUE:[^,]+]], [[PROP1_FALSE:[^,]+]]
|
||||
// CHECK: [[PROP1_FALSE]]:
|
||||
// CHECK: [[PROP1_TRUE]]([[PROP1_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject):
|
||||
// CHECK: [[PROP1_OBJ_COPY:%.*]] = copy_value [[PROP1_OBJ]]
|
||||
// CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject
|
||||
// CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP1_METHOD]]([[PROP1_OBJ]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]], any Test) Self) -> @autoreleased AnyObject
|
||||
// CHECK: = apply [[PROP1_PARTIAL]]() : $@callee_guaranteed () -> @owned AnyObject
|
||||
_ = prop1
|
||||
}
|
||||
@@ -39,8 +36,7 @@ public func testPartialApply(_ obj: Test) {
|
||||
// CHECK: dynamic_method_br [[PROP2_OBJ:%.+]] : $@opened([[PROP2_EXISTENTIAL:.+]], any Test) Self, #Test.innerPointerProp!getter.foreign, [[PROP2_TRUE:[^,]+]], [[PROP2_FALSE:[^,]+]]
|
||||
// CHECK: [[PROP2_FALSE]]:
|
||||
// CHECK: [[PROP2_TRUE]]([[PROP2_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer):
|
||||
// CHECK: [[PROP2_OBJ_COPY:%.*]] = copy_value [[PROP2_OBJ]]
|
||||
// CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ_COPY]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer
|
||||
// CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [callee_guaranteed] [[PROP2_METHOD]]([[PROP2_OBJ]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]], any Test) Self) -> @unowned_inner_pointer UnsafeMutableRawPointer
|
||||
// CHECK: = apply [[PROP2_PARTIAL]]() : $@callee_guaranteed () -> UnsafeMutableRawPointer
|
||||
_ = prop2
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
// REQUIRES: CPU=arm64 || CPU=x86_64 || CPU=arm64e
|
||||
|
||||
// The test currently fails because various optimizations optimize away most parts of the SIL.
|
||||
// TODO: re-write this test so that it's testing what it's intended to test.
|
||||
// REQUIRES: fix_this_test
|
||||
|
||||
protocol External {
|
||||
func use(str: String);
|
||||
func decode<T>(_: T.Type) -> T
|
||||
|
||||
@@ -49,7 +49,6 @@ distributed actor MyDistActor {
|
||||
// CHECK: [[TP_FIELD2:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.actorSystem
|
||||
// CHECK: [[RELOADED_SYS1:%[0-9]+]] = load [[TP_FIELD2]] : $*FakeRoundtripActorSystem
|
||||
// CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type
|
||||
// CHECK: strong_retain [[RELOADED_SYS1]] : $FakeRoundtripActorSystem
|
||||
// CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0a9RoundtripC6SystemC8assignIDyAA0C7AddressVxm0B00bC0RzlF
|
||||
// CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]<MyDistActor>([[SELF_METATYPE]], {{.*}})
|
||||
// CHECK: strong_release {{.*}} : $FakeRoundtripActorSystem
|
||||
@@ -72,7 +71,6 @@ distributed actor MyDistActor {
|
||||
// CHECK: return [[SELF]] : $MyDistActor
|
||||
|
||||
// CHECK: [[SYSTEM_ERROR_BB]]([[ERROR_ARG:%[0-9]+]] : $any Error):
|
||||
// CHECK: strong_retain [[ERROR_ARG]] : $any Error
|
||||
// CHECK: function_ref @$s27FakeDistributedActorSystems0a9RoundtripC6SystemCACycfC
|
||||
// CHECK: store {{.*}} to {{.*}} : $*FakeRoundtripActorSystem
|
||||
// CHECK: store {{.*}} to {{.*}} : $*ActorAddress
|
||||
|
||||
@@ -20,8 +20,8 @@ import Foundation
|
||||
// CHECK-NEXT: %._value = getelementptr inbounds{{.*}} %TSi, ptr %[[T2]], i32 0, i32 0
|
||||
// CHECK: %[[T7:.+]] = call swiftcc ptr @"$ss27_finalizeUninitializedArrayySayxGABnlF"(ptr %[[T1]], ptr @"$sSiN")
|
||||
// CHECK: %[[T4:.+]] = call swiftcc ptr @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(ptr %[[T7]], ptr @"$sSiN")
|
||||
// CHECK-NEXT: store ptr %[[T4]]
|
||||
// CHECK-NEXT: call void @swift_bridgeObjectRelease(ptr %{{[0-9]+}}) #{{[0-9]+}}
|
||||
// CHECK-NEXT: store ptr %[[T4]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(ptr %[[T4]])
|
||||
// CHECK-NEXT: ret ptr %[[T4]]
|
||||
let arr = [1] as CFArray
|
||||
@@ -37,12 +37,14 @@ import Foundation
|
||||
|
||||
// CHECK: [[L2]]: ; preds = %entry
|
||||
// CHECK-NEXT: %[[T4:.+]] = phi ptr [ %[[T0]], %entry ]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(ptr %{{.+}})
|
||||
// CHECK-NEXT: %[[T5:.+]] = ptrtoint ptr %[[T4]] to i{{32|64}}
|
||||
// CHECK-NEXT: br label %[[L3:.+]]
|
||||
|
||||
// CHECK: [[L1]]: ; preds = %entry
|
||||
// CHECK-NEXT: %[[T6:.+]] = phi ptr [ %[[T2]], %entry ]
|
||||
// CHECK-NEXT: store ptr null, ptr %swifterror, align {{[0-9]+}}
|
||||
// CHECK-NEXT: call void @llvm.objc.release(ptr %{{.+}})
|
||||
// CHECK-NEXT: %[[T7:.+]] = icmp eq i{{32|64}} %{{.+}}, 0
|
||||
// CHECK-NEXT: br i1 %[[T7]], label %[[L4:.+]], label %[[L5:.+]]
|
||||
|
||||
@@ -54,8 +56,7 @@ import Foundation
|
||||
// CHECK-NEXT: %[[T9:.+]] = phi ptr [ %[[T8]], %[[L5]] ]
|
||||
// CHECK-NEXT: %[[T10:.+]] = call swiftcc ptr @"$s10Foundation22_convertErrorToNSErrorySo0E0Cs0C0_pF"(ptr %[[T6]]) #{{[0-9]+}}
|
||||
// CHECK: call swiftcc void @"$sSA7pointeexvs"(ptr noalias %{{.+}}, ptr %[[T9]], ptr %{{.+}}) #{{[0-9]+}}
|
||||
// CHECK: call void @swift_errorRelease(ptr %[[T6]]) #{{[0-9]+}}
|
||||
// CHECK-NEXT: br label %[[L7:.+]]
|
||||
// CHECK: br label %[[L7:.+]]
|
||||
|
||||
// CHECK: [[L4]]: ; preds = %[[L1]]
|
||||
// CHECK-NEXT: call void @swift_errorRelease(ptr %[[T6]]) #{{[0-9]+}}
|
||||
@@ -66,6 +67,5 @@ import Foundation
|
||||
|
||||
// CHECK: [[L3]]: ; preds = %[[L2]], %[[L7]]
|
||||
// CHECK-NEXT: %[[T12:.+]] = phi i{{32|64}} [ 0, %[[L7]] ], [ %[[T5]], %[[L2]] ]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(ptr %{{.+}})
|
||||
// CHECK-NEXT: %[[T14:.+]] = inttoptr i{{32|64}} %[[T12]] to ptr
|
||||
// CHECK-NEXT: ret ptr %[[T14]]
|
||||
|
||||
@@ -129,6 +129,14 @@ entry(%flag : $Builtin.Int1):
|
||||
// CHECK-NEXT: [[R6:%.*]] = load ptr, ptr [[T0]], align
|
||||
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 7
|
||||
// CHECK-NEXT: [[R7:%.*]] = load ptr, ptr [[T0]], align
|
||||
// CHECK: call void @swift_release(ptr [[R0]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R1]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R2]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R3]])
|
||||
// CHECK: call void @swift_release(ptr [[R4]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R5]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R6]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R7]])
|
||||
|
||||
// Branch.
|
||||
// CHECK-NEXT: br i1 %0,
|
||||
@@ -155,17 +163,9 @@ no:
|
||||
br cont
|
||||
|
||||
cont:
|
||||
// CHECK: call void @swift_release(ptr [[R0]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R1]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R2]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R3]])
|
||||
// CHECK: call void @swift_release(ptr [[R4]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R5]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R6]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R7]])
|
||||
destroy_value %value : $Big<SomeSubclass>
|
||||
|
||||
// CHECK-NEXT: ret void
|
||||
// CHECK: ret void
|
||||
%ret = tuple ()
|
||||
return %ret : $()
|
||||
}
|
||||
|
||||
@@ -122,6 +122,10 @@ entry(%flag : $Builtin.Int1):
|
||||
%0 = function_ref @test_simple : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @owned Biggish<T>)
|
||||
(%value, %token) = begin_apply %0<SomeSubclass>() : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @owned Biggish<T>)
|
||||
|
||||
// CHECK: call void @swift_release(ptr [[R0_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R1_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R2_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R3_ORIG]])
|
||||
// Branch.
|
||||
// CHECK-NEXT: br i1 %0,
|
||||
cond_br %flag, yes, no
|
||||
@@ -147,13 +151,9 @@ no:
|
||||
br cont
|
||||
|
||||
cont:
|
||||
// CHECK: call void @swift_release(ptr [[R0_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R1_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R2_ORIG]])
|
||||
// CHECK-NEXT: call void @swift_release(ptr [[R3_ORIG]])
|
||||
destroy_value %value : $Biggish<SomeSubclass>
|
||||
|
||||
// CHECK-NEXT: ret void
|
||||
// CHECK: ret void
|
||||
%ret = tuple ()
|
||||
return %ret : $()
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import Closure
|
||||
// CHECK: apply %[[V3]](%[[V1]]) : $@callee_guaranteed (@in_guaranteed ARCWeak) -> ()
|
||||
// CHECK: %[[V6:.*]] = tuple ()
|
||||
// CHECK: destroy_addr %[[V1]] : $*ARCWeak
|
||||
// CHECK: strong_release %[[V3]] : $@callee_guaranteed (@in_guaranteed ARCWeak) -> ()
|
||||
// CHECK: return %[[V6]] : $()
|
||||
|
||||
// ARCWeak is destroyed by the callee.
|
||||
|
||||
@@ -27,7 +27,6 @@ import Closure
|
||||
// CHECK: strong_retain %[[V3]] : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||
// CHECK: apply %[[V3]](%[[V1]]) : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||
// CHECK: %[[V6:.*]] = tuple ()
|
||||
// CHECK: strong_release %[[V3]] : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||
// CHECK: return %[[V6]] : $()
|
||||
|
||||
// NonTrivial is destroyed by the caller.
|
||||
|
||||
@@ -175,9 +175,9 @@ int main() {
|
||||
// CHECK-NEXT: take shared frt x 2
|
||||
UseCxx::consumeSharedFRT(&sfrt);
|
||||
// CHECK-NEXT: retainShared
|
||||
// CHECK-NEXT: releaseShared
|
||||
// CHECK-NEXT: consume shared frt x 2
|
||||
SharedFRT *sfrtptr = UseCxx::returnSharedFRT(&sfrt);
|
||||
// CHECK-NEXT: releaseShared
|
||||
// CHECK-NEXT: retainShared
|
||||
// CHECK-NEXT: return shared frt x 2
|
||||
SharedFRT *sfrtptr2 = UseCxx::returnSharedFRT2();
|
||||
@@ -191,9 +191,9 @@ int main() {
|
||||
UseCxx::consumeValueWrapper(wrapper);
|
||||
// CHECK-NEXT: retainShared
|
||||
// CHECK-NEXT: retainShared
|
||||
// CHECK-NEXT: releaseShared
|
||||
// CHECK-NEXT: return shared frt x 4
|
||||
// CHECK-NEXT: releaseShared
|
||||
// CHECK-NEXT: releaseShared
|
||||
}
|
||||
{
|
||||
SharedFRT sfrt;
|
||||
|
||||
@@ -54,8 +54,8 @@ func test0() {
|
||||
// MANDATORY-NEXT: // function_ref
|
||||
// MANDATORY-NEXT: [[T0:%.*]] = function_ref @$s10reabstract6takeFn{{[_0-9a-zA-Z]*}}F
|
||||
// MANDATORY-NEXT: apply [[T0]]<Int>([[T5]])
|
||||
// MANDATORY-NEXT: strong_release [[T2]]
|
||||
// MANDATORY-NEXT: dealloc_stack [[T4]] : $@noescape @callee_guaranteed (@in_guaranteed Int) -> @out Optional<Int>
|
||||
// MANDATORY-NEXT: strong_release [[T2]]
|
||||
// MANDATORY-NEXT: tuple ()
|
||||
// MANDATORY-NEXT: return
|
||||
// MANDATORY-NEXT: } // end sil function '$s10reabstract5test0yyF'
|
||||
|
||||
@@ -64,6 +64,7 @@ func test1() throws {
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = tuple ()
|
||||
// CHECK-NEXT: return [[RESULT]]
|
||||
// CHECK: [[ERROR]]([[T0:%.*]] : $any Error):
|
||||
// CHECK-NEXT: strong_release [[T0]]
|
||||
// CHECK-NEXT: unreachable
|
||||
func test2() {
|
||||
rethrower(nonthrower)
|
||||
|
||||
@@ -88,7 +88,6 @@ public func couldActuallyEscapeWithLoop(_ closure: @escaping () -> (), _ villain
|
||||
// CHECK: [[BLOCK_PROJ:%.*]] = project_block_storage [[BLOCK_SLOT]]
|
||||
// CHECK: store [[MDI]] to [[BLOCK_PROJ]] :
|
||||
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_SLOT]]
|
||||
// CHECK: release_value [[LOOP_IND_VAR]]
|
||||
// CHECK: [[SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[MDI]]
|
||||
// CHECK: [[BLOCK_COPY:%.*]] = copy_block [[BLOCK]]
|
||||
// CHECK: destroy_addr [[BLOCK_PROJ]]
|
||||
@@ -100,7 +99,6 @@ public func couldActuallyEscapeWithLoop(_ closure: @escaping () -> (), _ villain
|
||||
// CHECK: br [[LOOP_HEADER_BB]]([[NONE_FOR_BACKEDGE]] :
|
||||
//
|
||||
// CHECK: [[NONE_BB]]:
|
||||
// CHECK: release_value [[LOOP_IND_VAR]]
|
||||
// CHECK: return
|
||||
// CHECK: } // end sil function '$s27closure_lifetime_fixup_objc9dontCrashyyF'
|
||||
public func dontCrash() {
|
||||
@@ -135,7 +133,6 @@ class C: NSObject {
|
||||
// CHECK-LABEL: sil hidden @$s27closure_lifetime_fixup_objc9CWithLoopCfD : $@convention(method) (@owned CWithLoop) -> () {
|
||||
// CHECK: [[METH:%.*]] = objc_super_method
|
||||
// CHECK-NEXT: release_value
|
||||
// CHECK-NEXT: release_value
|
||||
// CHECK-NEXT: upcast {{%.*}} : $CWithLoop to $NSObject
|
||||
// CHECK-NEXT: apply [[METH]]({{%.*}}) : $@convention(objc_method) (NSObject) -> ()
|
||||
// CHECK-NEXT: tuple ()
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @async_dead_arg_call : {{.*}} {
|
||||
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @noImplicitCopy @_eagerMove @owned
|
||||
// CHECK: destroy_value [[INSTANCE]]
|
||||
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<any Actor>, #Optional.none!enumelt
|
||||
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee
|
||||
// CHECK: apply [[CALLEE]]()
|
||||
// CHECK: hop_to_executor [[EXECUTOR]]
|
||||
// CHECK: destroy_value [[INSTANCE]]
|
||||
// CHECK-LABEL: } // end sil function 'async_dead_arg_call'
|
||||
@_silgen_name("async_dead_arg_call")
|
||||
public func async_dead_arg_call(o: consuming AnyObject) async {
|
||||
@@ -46,11 +46,11 @@ extension C {
|
||||
public class C {
|
||||
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_method : {{.*}} {
|
||||
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @noImplicitCopy @_eagerMove @owned
|
||||
// CHECK: destroy_value [[INSTANCE]]
|
||||
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<any Actor>, #Optional.none!enumelt
|
||||
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee : $@convention(thin) @async () -> ()
|
||||
// CHECK: apply [[CALLEE]]() : $@convention(thin) @async () -> ()
|
||||
// CHECK: hop_to_executor [[EXECUTOR]]
|
||||
// CHECK: destroy_value [[INSTANCE]]
|
||||
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_method'
|
||||
@_silgen_name("async_dead_arg_call_method")
|
||||
consuming
|
||||
|
||||
@@ -20,9 +20,13 @@ struct MOS : ~Copyable {
|
||||
deinit {}
|
||||
}
|
||||
|
||||
sil [ossa] @dummy : $@convention(thin) () -> ()
|
||||
sil [ossa] @dummy : $@convention(thin) () -> () {
|
||||
[global:]
|
||||
}
|
||||
sil [ossa] @barrier : $@convention(thin) () -> ()
|
||||
sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C)
|
||||
sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C) {
|
||||
[global:]
|
||||
}
|
||||
sil [ossa] @getOwnedB : $@convention(thin) () -> (@owned B)
|
||||
sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
||||
sil [ossa] @takeOwnedCTwice : $@convention(thin) (@owned C, @owned C) -> ()
|
||||
@@ -1076,7 +1080,7 @@ sil [ossa] @dontShortenDeadMoveOnlyLifetime : $@convention(thin) () -> () {
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @look_through_end_init_let_ref :
|
||||
// CHECK: [[E:%.*]] = end_init_let_ref
|
||||
// CHECK: [[D:%.*]] = function_ref @dummy
|
||||
// CHECK: [[D:%.*]] = function_ref @barrier
|
||||
// CHECK: apply [[D]]
|
||||
// CHECK: destroy_value [[E]]
|
||||
// CHECK-LABEL: } // end sil function 'look_through_end_init_let_ref'
|
||||
@@ -1087,7 +1091,7 @@ bb0:
|
||||
%2 = end_init_let_ref %1
|
||||
%4 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
|
||||
apply %4(%2) : $@convention(thin) (@guaranteed C) -> ()
|
||||
%6 = function_ref @dummy : $@convention(thin) () -> ()
|
||||
%6 = function_ref @barrier : $@convention(thin) () -> ()
|
||||
apply %6() : $@convention(thin) () -> ()
|
||||
destroy_value %2
|
||||
%3 = tuple ()
|
||||
|
||||
@@ -41,7 +41,9 @@ class C {
|
||||
|
||||
sil [ossa] @dummy : $@convention(thin) () -> ()
|
||||
sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C)
|
||||
sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
||||
sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> () {
|
||||
[global:]
|
||||
}
|
||||
sil [ossa] @getOwnedWrapper : $@convention(thin) () -> (@owned Wrapper)
|
||||
sil [ossa] @getOwnedMultiWrapper : $@convention(thin) () -> (@owned MultiWrapper)
|
||||
sil [ossa] @getOwnedHasObjectAndInt : $@convention(thin) () -> (@owned HasObjectAndInt)
|
||||
|
||||
@@ -10,7 +10,9 @@ enum FakeOptional<T> {
|
||||
}
|
||||
|
||||
sil [ossa] @getX : $@convention(thin) () -> (@owned X)
|
||||
sil [ossa] @holdX : $@convention(thin) (@guaranteed X) -> ()
|
||||
sil [ossa] @holdX : $@convention(thin) (@guaranteed X) -> () {
|
||||
[global:]
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_reborrow_endborrow : {{.*}} {
|
||||
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
||||
|
||||
@@ -15,22 +15,22 @@ import Foundation
|
||||
// CHECK: retain_value %0
|
||||
// CHECK: retain_value %0
|
||||
// CHECK: bb1
|
||||
// CHECK: convert_escape_to_noescape %
|
||||
// CHECK: strong_release
|
||||
// CHECK: convert_escape_to_noescape %
|
||||
// CHECK: bb5
|
||||
// CHECK: retain_value %1
|
||||
// CHECK: retain_value %1
|
||||
// CHECK: bb6
|
||||
// CHECK: convert_escape_to_noescape %
|
||||
// CHECK: strong_release
|
||||
// CHECK: convert_escape_to_noescape %
|
||||
// CHECK: bb10
|
||||
// CHECK: [[F:%.*]] = function_ref @$sSo14noescapeBlock3yyySSSgXESg_AcBtFTo
|
||||
// CHECK: apply [[F]]
|
||||
// CHECK: release_value {{.*}} : $Optional<NSString>
|
||||
// CHECK: release_value %1 : $Optional<@callee_guaranteed (@guaranteed Optional<String>) -> ()>
|
||||
// CHECK: release_value {{.*}} : $Optional<@convention(block) @noescape (Optional<NSString>) -> ()>
|
||||
// CHECK: release_value %0 : $Optional<@callee_guaranteed (@guaranteed Optional<String>) -> ()>
|
||||
// CHECK: release_value {{.*}} : $Optional<@convention(block) @noescape (Optional<NSString>)
|
||||
// CHECK: release_value %1 : $Optional<@callee_guaranteed (@guaranteed Optional<String>) -> ()>
|
||||
// CHECK: release_value %0 : $Optional<@callee_guaranteed (@guaranteed Optional<String>) -> ()>
|
||||
public func bridgeNoescapeBlock( optFn: ((String?) -> ())?, optFn2: ((String?) -> ())?) {
|
||||
noescapeBlock3(optFn, optFn2, "Foobar")
|
||||
}
|
||||
@@ -53,7 +53,6 @@ public func returnOptionalEscape() -> (() ->())?
|
||||
// CHECK: [[V1_UNWRAPPED:%.*]] = unchecked_enum_data [[V1]]
|
||||
// CHECK: [[CVT:%.*]] = convert_escape_to_noescape [[V1_UNWRAPPED]]
|
||||
// CHECK: [[SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[CVT]]
|
||||
// CHECK: strong_release [[V2]]
|
||||
// CHECK: br [[NEXT_BB:bb[0-9]+]]([[SOME]] :
|
||||
//
|
||||
// CHECK: [[NEXT_BB]]([[SOME_PHI:%.*]] :
|
||||
@@ -77,11 +76,11 @@ public func returnOptionalEscape() -> (() ->())?
|
||||
// CHECK: br bb5([[NONE_BLOCK]] : {{.*}}, [[NONE]] :
|
||||
//
|
||||
// CHECK: bb5([[BLOCK_PHI:%.*]] : $Optional<{{.*}}>, [[SWIFT_CLOSURE_PHI:%.*]] :
|
||||
// CHECK: release_value [[SWIFT_CLOSURE_PHI]]
|
||||
// CHECK: [[F:%.*]] = function_ref @$sSo13noescapeBlockyyyyXESgFTo
|
||||
// CHECK: apply [[F]]([[BLOCK_PHI]])
|
||||
// CHECK: release_value [[BLOCK_PHI]]
|
||||
// CHECK: release_value [[SWIFT_CLOSURE_PHI]]
|
||||
// CHECK-NEXT: return
|
||||
// CHECK: return
|
||||
|
||||
// NOPEEPHOLE-LABEL: sil @$s1A19bridgeNoescapeBlockyyF : $@convention(thin) () -> () {
|
||||
// NOPEEPHOLE: bb0:
|
||||
@@ -93,11 +92,11 @@ public func returnOptionalEscape() -> (() ->())?
|
||||
//
|
||||
// NOPEEPHOLE: [[SOME_BB]]([[V2:%.*]]: $@callee_guaranteed () -> ()):
|
||||
// NOPEEPHOLE-NEXT: strong_retain [[V2]]
|
||||
// NOPEEPHOLE-NEXT: strong_release [[V2]]
|
||||
// NOPEEPHOLE-NEXT: [[CVT:%.*]] = convert_escape_to_noescape [[V2]]
|
||||
// NOPEEPHOLE-NEXT: [[SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[V2]]
|
||||
// NOPEEPHOLE-NEXT: [[MDI:%.*]] = mark_dependence [[CVT]] : $@noescape @callee_guaranteed () -> () on [[V2]]
|
||||
// NOPEEPHOLE-NEXT: [[NOESCAPE_SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[MDI]]
|
||||
// NOPEEPHOLE-NEXT: strong_release [[V2]]
|
||||
// NOPEEPHOLE-NEXT: br bb2([[NOESCAPE_SOME]] : $Optional<{{.*}}>, [[SOME]] : $Optional<{{.*}}>, [[SOME]] : $Optional<{{.*}}>)
|
||||
//
|
||||
// NOPEEPHOLE: bb2([[NOESCAPE_SOME:%.*]] : $Optional<{{.*}}>, [[SOME1:%.*]] : $Optional<{{.*}}>, [[SOME:%.*]] : $Optional<{{.*}}>):
|
||||
@@ -110,12 +109,12 @@ public func returnOptionalEscape() -> (() ->())?
|
||||
// NOPEEPHOLE: br bb5
|
||||
//
|
||||
// NOPEEPHOLE: bb5([[BLOCK_PHI:%.*]] : $Optional<{{.*}}>, [[SWIFT_CLOSURE_PHI:%.*]] :
|
||||
// NOPEEPHOLE-NEXT: release_value [[SWIFT_CLOSURE_PHI]]
|
||||
// NOPEEPHOLE: [[F:%.*]] = function_ref @$sSo13noescapeBlockyyyyXESgFTo :
|
||||
// NOPEEPHOLE-NEXT: apply [[F]]([[BLOCK_PHI]])
|
||||
// NOPEEPHOLE-NEXT: release_value [[BLOCK_PHI]]
|
||||
// NOPEEPHOLE-NEXT: tuple
|
||||
// NOPEEPHOLE-NEXT: release_value [[SOME]]
|
||||
// NOPEEPHOLE-NEXT: release_value [[SWIFT_CLOSURE_PHI]]
|
||||
// NOPEEPHOLE-NEXT: return
|
||||
// NOPEEPHOLE: } // end sil function '$s1A19bridgeNoescapeBlockyyF'
|
||||
public func bridgeNoescapeBlock() {
|
||||
|
||||
@@ -89,6 +89,9 @@ struct UnsafeMutablePointer<T> {
|
||||
}
|
||||
|
||||
sil @unknown : $@convention(thin) () -> ()
|
||||
sil @nobarrier : $@convention(thin) () -> () {
|
||||
[global:]
|
||||
}
|
||||
sil @use_S : $@convention(thin) (@in_guaranteed S) -> ()
|
||||
|
||||
// This function is not a synchronization point.
|
||||
@@ -99,6 +102,9 @@ sil @empty : $@convention(thin) () -> () {
|
||||
|
||||
sil @f_out : $@convention(thin) <T> () -> @out T
|
||||
sil @f_bool : $@convention(thin) () -> Builtin.Int1
|
||||
sil @f_bool_no_barrier : $@convention(thin) () -> Builtin.Int1 {
|
||||
[global:]
|
||||
}
|
||||
sil [ossa] @take_trivial_struct : $@convention(thin) (TrivialStruct) -> ()
|
||||
sil [ossa] @get_change_out : $@convention(thin) () -> @out Change
|
||||
sil [ossa] @coro : $@yield_once @convention(thin) (@inout X) -> @yields ()
|
||||
@@ -283,7 +289,7 @@ bb0(%0 : $*T, %1 : $Builtin.Int1):
|
||||
cond_br %1, bb1, bb2
|
||||
|
||||
bb1:
|
||||
%8 = function_ref @unknown : $@convention(thin) () -> ()
|
||||
%8 = function_ref @nobarrier : $@convention(thin) () -> ()
|
||||
%9 = apply %8() : $@convention(thin) () -> ()
|
||||
br bb3
|
||||
|
||||
@@ -356,7 +362,7 @@ bb0(%0 : $*T):
|
||||
br bb1
|
||||
|
||||
bb1:
|
||||
%f = function_ref @f_bool : $@convention(thin) () -> Builtin.Int1
|
||||
%f = function_ref @f_bool_no_barrier : $@convention(thin) () -> Builtin.Int1
|
||||
%c = apply %f() : $@convention(thin) () -> Builtin.Int1
|
||||
cond_br %c, bb2, bb3
|
||||
|
||||
@@ -492,7 +498,8 @@ entry(%instance : @owned $S):
|
||||
br applier
|
||||
|
||||
applier:
|
||||
apply undef() : $@convention(thin) () -> ()
|
||||
%f = function_ref @nobarrier : $@convention(thin) () -> ()
|
||||
apply %f() : $@convention(thin) () -> ()
|
||||
br good
|
||||
|
||||
good:
|
||||
@@ -731,7 +738,7 @@ entry(%instance : @owned $S):
|
||||
end_access %store_scope : $*S
|
||||
%load_scope = begin_access [modify] [static] %addr : $*S
|
||||
%value = load [copy] %load_scope : $*S
|
||||
%unknown = function_ref @unknown : $@convention(thin) () -> ()
|
||||
%unknown = function_ref @nobarrier : $@convention(thin) () -> ()
|
||||
apply %unknown() : $@convention(thin) () -> ()
|
||||
end_access %load_scope : $*S
|
||||
destroy_addr %addr : $*S
|
||||
|
||||
@@ -11,6 +11,9 @@ class X {
|
||||
}
|
||||
|
||||
sil [ossa] @get_owned_X : $@convention(thin) () -> @owned X
|
||||
sil [ossa] @get_owned_X_no_barrier : $@convention(thin) () -> @owned X {
|
||||
[global:]
|
||||
}
|
||||
|
||||
// Hoist the destroy_addr of an inout over a loop and over a deinit barrier to
|
||||
// the top of the enclosing function.
|
||||
@@ -83,7 +86,7 @@ bb0(%instance : @owned $X, %second: $*X):
|
||||
%addr = alloc_stack $X
|
||||
store %instance to [init] %addr : $*X
|
||||
%scope = begin_access [modify] [static] %second : $*X
|
||||
%1 = function_ref @get_owned_X : $@convention(thin) () -> @owned X
|
||||
%1 = function_ref @get_owned_X_no_barrier : $@convention(thin) () -> @owned X
|
||||
%14 = apply %1() : $@convention(thin) () -> @owned X
|
||||
destroy_value %14 : $X
|
||||
end_access %scope : $*X
|
||||
|
||||
447
test/SILOptimizer/mandatory-destroy-hoisting.sil
Normal file
447
test/SILOptimizer/mandatory-destroy-hoisting.sil
Normal file
@@ -0,0 +1,447 @@
|
||||
// RUN: %target-sil-opt -mandatory-destroy-hoisting %s | %FileCheck %s
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @simple :
|
||||
// CHECK: fix_lifetime
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'simple'
|
||||
sil [ossa] @simple : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @multi_block :
|
||||
// CHECK: fix_lifetime
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'multi_block'
|
||||
sil [ossa] @multi_block : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
br bb3
|
||||
bb2:
|
||||
br bb3
|
||||
bb3:
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @two_uses_in_one_user :
|
||||
// CHECK: apply
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'two_uses_in_one_user'
|
||||
sil [ossa] @two_uses_in_one_user : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
apply undef(%0, %0) : $(@guaranteed String, @guaranteed String) -> ()
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @no_destroy :
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: } // end sil function 'no_destroy'
|
||||
sil [ossa] @no_destroy : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
apply undef(%0) : $(@owned String) -> ()
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @no_arg_user :
|
||||
// CHECK: bb0(%0 : @owned $String):
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'no_arg_user'
|
||||
sil [ossa] @no_arg_user : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @no_inst_user :
|
||||
// CHECK: %1 = move_value %0
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'no_inst_user'
|
||||
sil [ossa] @no_inst_user : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = move_value %0
|
||||
debug_step
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @destroy_already_in_place :
|
||||
// CHECK: fix_lifetime
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'destroy_already_in_place'
|
||||
sil [ossa] @destroy_already_in_place : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
destroy_value %0
|
||||
debug_step
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @two_consecutive_users :
|
||||
// CHECK: fix_lifetime
|
||||
// CHECK-NEXT: fix_lifetime
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'two_consecutive_users'
|
||||
sil [ossa] @two_consecutive_users : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
fix_lifetime %0
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @last_user_is_terminator :
|
||||
// CHECK: bb1(%2 : $()):
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: bb2(%5 : @owned $any Error):
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'last_user_is_terminator'
|
||||
sil [ossa] @last_user_is_terminator : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
try_apply undef(%0) : $(@guaranteed String) -> @error Error, normal bb1, error bb2
|
||||
bb1(%2 : $()):
|
||||
br bb3
|
||||
bb2(%3 : @owned $Error):
|
||||
apply undef(%3) : $(@owned Error) -> ()
|
||||
br bb3
|
||||
bb3:
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @liverange_exit :
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'liverange_exit'
|
||||
sil [ossa] @liverange_exit : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
debug_step
|
||||
destroy_value %0
|
||||
br bb3
|
||||
bb2:
|
||||
apply undef(%0) : $(@owned String) -> ()
|
||||
br bb3
|
||||
bb3:
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @non_overlapping_access_scope :
|
||||
// CHECK: fix_lifetime
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: begin_access
|
||||
// CHECK: } // end sil function 'non_overlapping_access_scope'
|
||||
sil [ossa] @non_overlapping_access_scope : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
%2 = begin_access [modify] [dynamic] undef : $*Int
|
||||
end_access %2
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @overlapping_access_scope :
|
||||
// CHECK: end_access
|
||||
// CHECK-NEXT: destroy_value %2
|
||||
// CHECK: } // end sil function 'overlapping_access_scope'
|
||||
sil [ossa] @overlapping_access_scope : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = begin_access [modify] [dynamic] undef : $*Int
|
||||
%2 = move_value %0
|
||||
fix_lifetime %2
|
||||
end_access %1
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @interleaved_overlapping_access_scope :
|
||||
// CHECK: end_access %1
|
||||
// CHECK-NEXT: end_access %4
|
||||
// CHECK-NEXT: destroy_value %2
|
||||
// CHECK: } // end sil function 'interleaved_overlapping_access_scope'
|
||||
sil [ossa] @interleaved_overlapping_access_scope : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = begin_access [modify] [dynamic] undef : $*Int
|
||||
%2 = move_value %0
|
||||
fix_lifetime %2
|
||||
%4 = begin_access [modify] [dynamic] undef : $*Int
|
||||
end_access %1
|
||||
end_access %4
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @interleaved_overlapping_access_scope_reverse_order :
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: end_access %4
|
||||
// CHECK-NEXT: destroy_value %2
|
||||
// CHECK: } // end sil function 'interleaved_overlapping_access_scope_reverse_order'
|
||||
sil [ossa] @interleaved_overlapping_access_scope_reverse_order : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = begin_access [modify] [dynamic] undef : $*Int
|
||||
%2 = move_value %0
|
||||
fix_lifetime %2
|
||||
%4 = begin_access [modify] [dynamic] undef : $*Int
|
||||
br bb2
|
||||
bb1:
|
||||
end_access %4
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
bb2:
|
||||
end_access %1
|
||||
br bb1
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dead_end_block :
|
||||
// CHECK: bb0(%0 : @owned $String):
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'dead_end_block'
|
||||
sil [ossa] @dead_end_block : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
bb2:
|
||||
unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dead_end_block_with_use :
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'dead_end_block_with_use'
|
||||
sil [ossa] @dead_end_block_with_use : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
bb2:
|
||||
fix_lifetime %0
|
||||
unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dead_end_block_with_inner_use :
|
||||
// CHECK: end_borrow %1
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'dead_end_block_with_inner_use'
|
||||
sil [ossa] @dead_end_block_with_inner_use : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = begin_borrow %0
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
end_borrow %1
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
bb2:
|
||||
fix_lifetime %1
|
||||
unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dead_end_with_no_destroy :
|
||||
// CHECK: bb0(%0 : @owned $String):
|
||||
// CHECK-NEXT: fix_lifetime
|
||||
// CHECK-NEXT: unreachable
|
||||
// CHECK: } // end sil function 'dead_end_with_no_destroy'
|
||||
sil [ossa] @dead_end_with_no_destroy : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
fix_lifetime %0
|
||||
unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dead_end_with_no_use :
|
||||
// CHECK: bb0(%0 : @owned $String):
|
||||
// CHECK-NEXT: unreachable
|
||||
// CHECK: } // end sil function 'dead_end_with_no_use'
|
||||
sil [ossa] @dead_end_with_no_use : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
unreachable
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @borrow_scope :
|
||||
// CHECK: end_borrow
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'borrow_scope'
|
||||
sil [ossa] @borrow_scope : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = begin_borrow %0
|
||||
end_borrow %1
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @partial_apply :
|
||||
// CHECK: destroy_value %1
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'partial_apply'
|
||||
sil [ossa] @partial_apply : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = partial_apply [on_stack] undef(%0) : $(@guaranteed String) -> ()
|
||||
destroy_value %1
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @mark_dependence :
|
||||
// CHECK: destroy_value %2
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'mark_dependence'
|
||||
sil [ossa] @mark_dependence : $@convention(thin) (@owned String, @owned String) -> () {
|
||||
bb0(%0 : @owned $String, %1 : @owned $String):
|
||||
%2 = mark_dependence [nonescaping] %1 on %0
|
||||
destroy_value %2
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @reborrow :
|
||||
// CHECK: end_borrow
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK: } // end sil function 'reborrow'
|
||||
sil [ossa] @reborrow : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
cond_br undef, bb1, bb2
|
||||
bb1:
|
||||
%1 = begin_borrow %0
|
||||
br bb3(%1)
|
||||
bb2:
|
||||
%3 = begin_borrow %0
|
||||
br bb3(%3)
|
||||
bb3(%5 : @reborrow $String):
|
||||
%6 = borrowed %5 from (%0)
|
||||
end_borrow %6
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @lexical :
|
||||
// CHECK: debug_step
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'lexical'
|
||||
sil [ossa] @lexical : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = move_value [lexical] %0
|
||||
debug_step
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
class C {}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @lexical_in_class_initializer :
|
||||
// CHECK: debug_step
|
||||
// CHECK-NEXT: destroy_value %2
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'lexical_in_class_initializer'
|
||||
sil [ossa] @lexical_in_class_initializer : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = alloc_ref $C
|
||||
%1 = move_value [lexical] %0
|
||||
%2 = end_init_let_ref %1
|
||||
debug_step
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @copy_of_lexical :
|
||||
// CHECK: %2 = copy_value %1
|
||||
// CHECK-NEXT: destroy_value %2
|
||||
// CHECK: } // end sil function 'copy_of_lexical'
|
||||
sil [ossa] @copy_of_lexical : $@convention(thin) (@owned String) -> () {
|
||||
bb0(%0 : @owned $String):
|
||||
%1 = move_value [lexical] %0
|
||||
%2 = copy_value %1
|
||||
destroy_value %1
|
||||
debug_step
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
struct NC: ~Copyable {}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @not_copyable_arg :
|
||||
// CHECK: debug_step
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'not_copyable_arg'
|
||||
sil [ossa] @not_copyable_arg : $@convention(thin) (@owned NC) -> () {
|
||||
bb0(%0 : @owned $NC):
|
||||
fix_lifetime %0
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @not_copyable_struct :
|
||||
// CHECK: debug_step
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK: } // end sil function 'not_copyable_struct'
|
||||
sil [ossa] @not_copyable_struct : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = struct $NC ()
|
||||
fix_lifetime %0
|
||||
debug_step
|
||||
destroy_value %0
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// RUN: %target-swift-frontend -Osize -import-objc-header %S/Inputs/Outliner.h %s -Xllvm -sil-print-types -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -Osize -g -import-objc-header %S/Inputs/Outliner.h %s -Xllvm -sil-print-types -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=mandatory-destroy-hoisting -Osize -import-objc-header %S/Inputs/Outliner.h %s -Xllvm -sil-print-types -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=mandatory-destroy-hoisting -Osize -g -import-objc-header %S/Inputs/Outliner.h %s -Xllvm -sil-print-types -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
// REQUIRES: optimized_stdlib
|
||||
|
||||
@@ -805,8 +805,9 @@ bb3:
|
||||
return %9999 : $()
|
||||
}
|
||||
|
||||
// TODO: check why the copy cannot be eliminated here
|
||||
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_8 : $@convention(thin) () -> () {
|
||||
// CHECK-NOT: copy_value
|
||||
// xCHECK-NOT: copy_value
|
||||
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_8'
|
||||
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_8 : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
|
||||
Reference in New Issue
Block a user