Skip to content

Disable fix_lifetime DCE. #36183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion lib/SILOptimizer/Transforms/DeadCodeElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace {

// Without any complex analysis, does this instruction seem like
// something that we need to keep?
//
// FIXME: Reconcile the similarities between this and
// isInstructionTriviallyDead.
static bool seemsUseful(SILInstruction *I) {
Expand Down Expand Up @@ -243,9 +244,17 @@ void DCE::markLive() {
}
break;
}
// The side-effects of fix_lifetime effect all references to the same
// object. It must be preserved to keep alive any potentially aliasing
// references. fix_lifetime can only be removed by proving that its
// operand is both a unique and a dead reference, but this makes more
// sense in DeadObjectElimination.
case SILInstructionKind::FixLifetimeInst: {
// If the operand is a trivial scalar value, then it has no aliases or
// side-effects. Consider handling this as an instruction
// canonicalization instead.
SILValue Op = I.getOperand(0);
if (!Op->getType().isAddress()) {
if (!Op->getType().isAddress() && Op->getType().isTrivial(*F)) {
addReverseDependency(Op, &I);
} else {
markInstructionLive(&I);
Expand Down
71 changes: 71 additions & 0 deletions test/SILOptimizer/dead_code_elimination_ossa.sil
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ sil_stage canonical
import Builtin
import Swift

public struct S {
var o: AnyObject
}
sil @dummy : $@convention(thin) () -> ()

// CHECK-LABEL: sil [ossa] @dead1 :
// CHECK: bb0
// CHECK-NEXT: return %0
Expand Down Expand Up @@ -289,3 +294,69 @@ bb0(%0 : @owned $(String, (Int, Int, String))):
return %1 : $String
}

// Test fix_lifetime of a struct and tuple. It cannot be removed
// without proving reference uniqueness first.
// rdar://36038096 ([SR-6608] DeadCodeElimination removes fix_lifetime instructions.)
//
// CHECK-LABEL: @testFixLifetimeTuple : $@convention(thin) (@owned S, @owned AnyObject) -> () {
// CHECK: bb0(%0 : $S, %1 : $AnyObject):
// CHECK: [[TUPLE:%.*]] = tuple (%0 : $S, %1 : $AnyObject)
// CHECK: apply
// CHECK: fix_lifetime [[TUPLE]] : $(S, AnyObject)
// CHECK: strong_release %1 : $AnyObject
// CHECK: strong_release %{{.*}} : $AnyObject
// CHECK-LABEL: } // end sil function 'testFixLifetimeTuple'
sil @testFixLifetimeTuple : $@convention(thin) (@owned S, @owned AnyObject) -> () {
bb0(%0 : $S, %1 : $AnyObject):
%4 = struct_extract %0 : $S, #S.o
%7 = tuple (%0 : $S, %1 : $AnyObject)
%8 = function_ref @dummy : $@convention(thin) () -> ()
%9 = apply %8() : $@convention(thin) () -> ()
fix_lifetime %7 : $(S, AnyObject)
strong_release %1 : $AnyObject
strong_release %4 : $AnyObject
%15 = tuple ()
return %15 : $()
}

// Test fix_lifetime of a load_borrow. It cannot be removed without
// proving reference uniqueness first.
// rdar://74759728 (The compiler/optimizer seems to be shortening the lifetime too early)
//
// Test case derived from $defer #1 <A><A1>() in _ArrayBuffer.withUnsafeBufferPointer<A>(_:)
//
// Original source that exposed the issue with -Ounchecked -sanitize=address:
//
// public func makeRawData(name: String) -> UnsafeMutableRawPointer {
// var array: [UInt8] = []
// array.append(contentsOf: name.utf8)
// array.append(0)
//
// let rawData = array.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> UnsafeMutableRawPointer in
// let rawData = malloc(Int(buffer.count))!
// rawData.copyMemory(from: buffer.baseAddress!, byteCount: buffer.count)
// return rawData
// }
// return rawData
// }
//
// CHECK-LABEL: sil [ossa] @testDCEFixLifetime : $@convention(thin) (@guaranteed S) -> () {
// %0 "self" // users: %3, %1
// CHECK: bb0(%0 : @guaranteed $S):
// CHECK: [[LD:%.*]] = load_borrow
// CHECK: fix_lifetime [[LD]] : $S
// CHECK: end_borrow [[LD]] : $S
// CHECK-LABEL: } // end sil function 'testDCEFixLifetime'
sil [ossa] @testDCEFixLifetime : $@convention(thin) (@guaranteed S) -> () {
// %0 "self"
bb0(%0 : @guaranteed $S):
debug_value %0 : $S, let, name "self", argno 1
%2 = alloc_stack $S
%3 = store_borrow %0 to %2 : $*S
%4 = load_borrow %2 : $*S
fix_lifetime %4 : $S
end_borrow %4 : $S
dealloc_stack %2 : $*S
%8 = tuple ()
return %8 : $()
}