Skip to content
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
6 changes: 3 additions & 3 deletions SwiftCompilerSources/Sources/Basic/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,19 @@ public typealias SwiftObject = UnsafeMutablePointer<BridgedSwiftObject>

extension UnsafeMutablePointer where Pointee == BridgedSwiftObject {
public init<T: AnyObject>(_ object: T) {
let ptr = Unmanaged.passUnretained(object).toOpaque()
let ptr = unsafeBitCast(object, to: UnsafeMutableRawPointer.self)
self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1)
}

public func getAs<T: AnyObject>(_ objectType: T.Type) -> T {
return Unmanaged<T>.fromOpaque(self).takeUnretainedValue()
return unsafeBitCast(self, to: T.self)
}
}

extension Optional where Wrapped == UnsafeMutablePointer<BridgedSwiftObject> {
public func getAs<T: AnyObject>(_ objectType: T.Type) -> T? {
if let pointer = self {
return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
return pointer.getAs(objectType)
}
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ extension StrongReleaseInst : Simplifyable, SILCombineSimplifyable {
/// Returns true if \p value is something where reference counting instructions
/// don't have any effect.
private func isNotReferenceCounted(value: Value) -> Bool {
if value.type.isMarkedAsImmortal {
return true
}
switch value {
case let cfi as ConvertFunctionInst:
return isNotReferenceCounted(value: cfi.fromFunction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,14 +819,14 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
}

private func hasRelevantType(_ value: Value, at path: SmallProjectionPath) -> Bool {
if !value.hasTrivialNonPointerType {
return true
}
if visitor.followTrivialTypes &&
// When part of a class field only need to follow non-trivial types
!path.hasClassProjection {
return true
}
if !value.hasTrivialNonPointerType {
return true
}
return false
}

Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/BasicBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Basic
import SILBridging

@_semantics("arc.immortal")
final public class BasicBlock : CustomStringConvertible, HasShortDescription {
public var next: BasicBlock? { SILBasicBlock_next(bridged).block }
public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block }
Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Basic
import SILBridging

@_semantics("arc.immortal")
final public class Function : CustomStringConvertible, HasShortDescription, Hashable {
public private(set) var effects = FunctionEffects()

Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import SILBridging
// Instruction base classes
//===----------------------------------------------------------------------===//

@_semantics("arc.immortal")
public class Instruction : CustomStringConvertible, Hashable {
final public var next: Instruction? {
SILInstruction_next(bridged).instruction
Expand Down
4 changes: 3 additions & 1 deletion SwiftCompilerSources/Sources/SIL/Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
}

public var isCalleeConsumedFunction: Bool { SILType_isCalleeConsumedFunction(bridged) }


public var isMarkedAsImmortal: Bool { SILType_isMarkedAsImmortal(bridged) }

public func getIndexOfEnumCase(withName name: String) -> Int? {
let idx = name._withStringRef {
SILType_getCaseIdxOfEnumType(bridged, $0)
Expand Down
26 changes: 15 additions & 11 deletions SwiftCompilerSources/Sources/SIL/Value.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Basic
import SILBridging

@_semantics("arc.immortal")
public protocol Value : AnyObject, CustomStringConvertible {
var uses: UseList { get }
var type: Type { get }
Expand Down Expand Up @@ -157,18 +158,21 @@ extension Value {
extension BridgedValue {
public func getAs<T: AnyObject>(_ valueType: T.Type) -> T { obj.getAs(T.self) }

public var value: Value { getAs(AnyObject.self) as! Value }
}

extension BridgedClassifiedValue {
public var value: Value {
// This is much faster than a conformance lookup with `as! Value`.
let v = getAs(AnyObject.self)
switch v {
case let inst as SingleValueInstruction:
return inst
case let arg as Argument:
return arg
case let mvr as MultipleValueInstructionResult:
return mvr
case let undef as Undef:
return undef
// Doing the type check in C++ is much faster than a conformance lookup with `as! Value`.
switch kind {
case .SingleValueInstruction:
return obj.getAs(SingleValueInstruction.self)
case .Argument:
return obj.getAs(Argument.self)
case .MultipleValueInstructionResult:
return obj.getAs(MultipleValueInstructionResult.self)
case .Undef:
return obj.getAs(Undef.self)
default:
fatalError("unknown Value type")
}
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/SemanticAttrs.def
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER,
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER,
"optimize.sil.specialize.owned2guarantee.never")

// To be used on a nominal type declaration.
// Assumes that a class (or class references inside a nominal type) are immortal.
// ARC operations on such types can be eliminated.
// If specified on a protocol declaration, all types which conform to that protocol
// are assumed to be immortal.
SEMANTICS_ATTR(ARC_IMMORTAL, "arc.immortal")

SEMANTICS_ATTR(OSLOG_MESSAGE_TYPE, "oslog.message.type")
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_INTERPOLATION, "oslog.message.init_interpolation")
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_STRING_LITERAL, "oslog.message.init_stringliteral")
Expand Down
16 changes: 15 additions & 1 deletion include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ typedef struct {
SwiftObject obj;
} BridgedValue;

// For fast SILValue -> Value briding.
// This is doing the type checks in C++ rather than in Swift.
// It's used for getting the value of an Operand, which is a time critical function.
typedef struct {
SwiftObject obj;
enum class Kind {
SingleValueInstruction,
Argument,
MultipleValueInstructionResult,
Undef
} kind;
} BridgedClassifiedValue;

typedef struct {
OptionalSwiftObject obj;
} OptionalBridgedValue;
Expand Down Expand Up @@ -305,7 +318,7 @@ OptionalBridgedSuccessor SILSuccessor_getNext(BridgedSuccessor succ);
BridgedBasicBlock SILSuccessor_getTargetBlock(BridgedSuccessor succ);
BridgedInstruction SILSuccessor_getContainingInst(BridgedSuccessor succ);

BridgedValue Operand_getValue(BridgedOperand);
BridgedClassifiedValue Operand_getValue(BridgedOperand);
OptionalBridgedOperand Operand_nextUse(BridgedOperand);
BridgedInstruction Operand_getUser(BridgedOperand);
SwiftInt Operand_isTypeDependent(BridgedOperand);
Expand All @@ -332,6 +345,7 @@ BridgedType SILType_instanceTypeOfMetatype(BridgedType type, BridgedFunction fun
BridgedDecl SILType_getNominal(BridgedType type);
bool SILType_isOrContainsObjectiveCClass(BridgedType type);
bool SILType_isCalleeConsumedFunction(BridgedType type);
bool SILType_isMarkedAsImmortal(BridgedType type);
SwiftInt SILType_getNumTupleElements(BridgedType type);
BridgedType SILType_getTupleElementType(BridgedType type, SwiftInt elementIdx);
SwiftInt SILType_getNumNominalFields(BridgedType type);
Expand Down
61 changes: 59 additions & 2 deletions lib/SIL/Utils/SILBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//

#include "swift/Basic/BridgingUtils.h"
#include "swift/AST/Attr.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/SIL/SILNode.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/SILBridgingUtils.h"
Expand Down Expand Up @@ -447,8 +449,21 @@ static Operand *castToOperand(BridgedOperand operand) {
return const_cast<Operand *>(static_cast<const Operand *>(operand.op));
}

BridgedValue Operand_getValue(BridgedOperand operand) {
return {castToOperand(operand)->get()};
BridgedClassifiedValue Operand_getValue(BridgedOperand operand) {
SILValue v = castToOperand(operand)->get();
BridgedClassifiedValue::Kind k;
if (isa<SingleValueInstruction>(v)) {
k = BridgedClassifiedValue::Kind::SingleValueInstruction;
} else if (isa<SILArgument>(v)) {
k = BridgedClassifiedValue::Kind::Argument;
} else if (isa<MultipleValueInstructionResult>(v)) {
k = BridgedClassifiedValue::Kind::MultipleValueInstructionResult;
} else if (isa<SILUndef>(v)) {
k = BridgedClassifiedValue::Kind::Undef;
} else {
llvm_unreachable("unknown SILValue");
}
return {castToOperand(operand)->get(), k};
}

OptionalBridgedOperand Operand_nextUse(BridgedOperand operand) {
Expand Down Expand Up @@ -574,6 +589,48 @@ bool SILType_isCalleeConsumedFunction(BridgedType type) {
return funcTy->isCalleeConsumed() && !funcTy->isNoEscape();
}

static bool hasImmortalAttr(NominalTypeDecl *nominal) {
if (auto *semAttr = nominal->getAttrs().getAttribute<SemanticsAttr>()) {
if (semAttr->Value == semantics::ARC_IMMORTAL) {
return true;
}
}
return false;
}

static bool isMarkedAsImmortal(NominalTypeDecl *nominal) {
if (hasImmortalAttr(nominal))
return true;

if (!isa<ProtocolDecl>(nominal)) {
for (ProtocolDecl *p : nominal->getAllProtocols()) {
if (hasImmortalAttr(p))
return true;
}
}
return false;
}

bool SILType_isMarkedAsImmortal(BridgedType type) {
SILType ty = castToSILType(type);
NominalTypeDecl *nominal = ty.getNominalOrBoundGenericNominal();
if (!nominal)
return false;

if (isMarkedAsImmortal(nominal))
return true;

if (ClassDecl *cl = dyn_cast<ClassDecl>(nominal)) {
cl = cl->getSuperclassDecl();
while (cl) {
if (isMarkedAsImmortal(cl))
return true;
cl = cl->getSuperclassDecl();
}
}
return false;
}

SwiftInt SILType_getNumTupleElements(BridgedType type) {
TupleType *tupleTy = castToSILType(type).castTo<TupleType>();
return tupleTy->getNumElements();
Expand Down
52 changes: 52 additions & 0 deletions test/SILOptimizer/immortal-arc-elimination.sil
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,55 @@ bb0:
return %1 : $Builtin.BridgeObject
}

@_semantics("arc.immortal") protocol P : AnyObject {
}

@_semantics("arc.immortal") class C {
init()
}

@_inheritsConvenienceInitializers class D : C {
override init()
}

class E : P {
init()
}

// CHECK-LABEL: sil @testSemanticsOnClass
// CHECK-NOT: retain
// CHECK: } // end sil function 'testSemanticsOnClass'
sil @testSemanticsOnClass : $@convention(thin) (@guaranteed C) -> @owned C {
bb0(%0 : $C):
strong_retain %0 : $C
return %0 : $C // id: %3
}

// CHECK-LABEL: sil @testSemanticsOnDerivedClass
// CHECK-NOT: retain
// CHECK: } // end sil function 'testSemanticsOnDerivedClass'
sil @testSemanticsOnDerivedClass : $@convention(thin) (@guaranteed D) -> @owned D {
bb0(%0 : $D):
strong_retain %0 : $D
return %0 : $D
}

// CHECK-LABEL: sil @testSemanticsOnConformingClass
// CHECK-NOT: retain
// CHECK: } // end sil function 'testSemanticsOnConformingClass'
sil @testSemanticsOnConformingClass : $@convention(thin) (@guaranteed E) -> @owned E {
bb0(%0 : $E):
strong_retain %0 : $E
return %0 : $E
}

// CHECK-LABEL: sil @testSemanticsOnProtocol
// CHECK-NOT: retain
// CHECK: } // end sil function 'testSemanticsOnProtocol'
sil @testSemanticsOnProtocol : $@convention(thin) (@guaranteed any P) -> @owned any P {
bb0(%0 : $any P):
strong_retain %0 : $any P
return %0 : $any P
}