Skip to content

Commit abc1c4a

Browse files
authored
Merge pull request #72388 from eeckstein/simplify_addr_casts
Simplify `checked_cast_addr_br` to enable a shortcut check in embedded swift's `Array.append(contentsOf:)`
2 parents 6d2e87f + f6a28d8 commit abc1c4a

File tree

12 files changed

+325
-54
lines changed

12 files changed

+325
-54
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ swift_compiler_sources(Optimizer
1212
SimplifyBeginCOWMutation.swift
1313
SimplifyBranch.swift
1414
SimplifyBuiltin.swift
15+
SimplifyCheckedCast.swift
1516
SimplifyCondBranch.swift
1617
SimplifyCondFail.swift
1718
SimplifyConvertEscapeToNoEscape.swift

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -285,61 +285,21 @@ private func typesOfValuesAreEqual(_ lhs: Value, _ rhs: Value, in function: Func
285285
let typesAreExact = lhsExistential.metatype is MetatypeInst &&
286286
rhsExistential.metatype is MetatypeInst
287287

288-
switch (lhsTy.typeKind, rhsTy.typeKind) {
289-
case (_, .unknown), (.unknown, _):
290-
return nil
291-
case (let leftKind, let rightKind) where leftKind != rightKind:
292-
// E.g. a function type is always different than a struct, regardless of what archetypes
293-
// the two types may contain.
294-
return false
295-
case (.struct, .struct), (.enum, .enum):
296-
// Two different structs/enums are always not equal, regardless of what archetypes
297-
// the two types may contain.
298-
if lhsTy.nominal != rhsTy.nominal {
299-
return false
288+
if typesAreExact {
289+
if lhsTy == rhsTy {
290+
return true
300291
}
301-
case (.class, .class):
302-
// In case of classes this only holds if we know the exact types.
303-
// Otherwise one class could be a sub-class of the other class.
304-
if typesAreExact && lhsTy.nominal != rhsTy.nominal {
292+
// Comparing types of different classes which are in a sub-class relation is not handled by the
293+
// cast optimizer (below).
294+
if lhsTy.isClass && rhsTy.isClass && lhsTy.nominal != rhsTy.nominal {
305295
return false
306296
}
307-
default:
308-
break
309297
}
310298

311-
if !typesAreExact {
312-
// Types which e.g. come from type parameters may differ at runtime while the declared AST types are the same.
313-
return nil
314-
}
315-
316-
if lhsTy.hasArchetype || rhsTy.hasArchetype {
317-
// We don't know anything about archetypes. They may be identical at runtime or not.
318-
// We could do something more sophisticated here, e.g. look at conformances. But for simplicity,
319-
// we are just conservative.
320-
return nil
321-
}
322-
323-
// Generic ObjectiveC class, which are specialized for different NSObject types have different AST types
324-
// but the same runtime metatype.
325-
if lhsTy.isOrContainsObjectiveCClass || rhsTy.isOrContainsObjectiveCClass {
326-
return nil
327-
}
328-
329-
return lhsTy == rhsTy
330-
}
331-
332-
private extension Type {
333-
enum TypeKind {
334-
case `struct`, `class`, `enum`, tuple, function, unknown
335-
}
336-
337-
var typeKind: TypeKind {
338-
if isStruct { return .struct }
339-
if isClass { return .class }
340-
if isEnum { return .enum }
341-
if isTuple { return .tuple }
342-
if isFunction { return .function }
343-
return .unknown
299+
// If casting in either direction doesn't work, the types cannot be equal.
300+
if !(canDynamicallyCast(from: lhsTy, to: rhsTy, in: function, sourceTypeIsExact: typesAreExact) ?? true) ||
301+
!(canDynamicallyCast(from: rhsTy, to: lhsTy, in: function, sourceTypeIsExact: typesAreExact) ?? true) {
302+
return false
344303
}
304+
return nil
345305
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===--- SimplifyCheckedCast.swift ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
extension CheckedCastAddrBranchInst : OnoneSimplifyable {
16+
func simplify(_ context: SimplifyContext) {
17+
guard let castWillSucceed = self.dynamicCastResult else {
18+
return
19+
}
20+
if castWillSucceed {
21+
replaceSuccess(context)
22+
} else {
23+
replaceFailure(context)
24+
}
25+
}
26+
}
27+
28+
private extension CheckedCastAddrBranchInst {
29+
func replaceSuccess(_ context: SimplifyContext) {
30+
let builder = Builder(before: self, context)
31+
switch consumptionKind {
32+
case .TakeAlways, .TakeOnSuccess:
33+
builder.createCopyAddr(from: source, to: destination, takeSource: true, initializeDest: true)
34+
case .CopyOnSuccess:
35+
builder.createCopyAddr(from: source, to: destination, takeSource: false, initializeDest: true)
36+
}
37+
builder.createBranch(to: successBlock)
38+
context.erase(instruction: self)
39+
}
40+
41+
func replaceFailure(_ context: SimplifyContext) {
42+
let builder = Builder(before: self, context)
43+
switch consumptionKind {
44+
case .TakeAlways:
45+
builder.createDestroyAddr(address: source)
46+
case .CopyOnSuccess, .TakeOnSuccess:
47+
break
48+
}
49+
builder.createBranch(to: failureBlock)
50+
context.erase(instruction: self)
51+
}
52+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,3 +695,23 @@ func getGlobalInitialization(
695695
}
696696
return nil
697697
}
698+
699+
func canDynamicallyCast(from sourceType: Type, to destType: Type, in function: Function, sourceTypeIsExact: Bool) -> Bool? {
700+
switch classifyDynamicCastBridged(sourceType.bridged, destType.bridged, function.bridged, sourceTypeIsExact) {
701+
case .willSucceed: return true
702+
case .maySucceed: return nil
703+
case .willFail: return false
704+
default: fatalError("unknown result from classifyDynamicCastBridged")
705+
}
706+
}
707+
708+
extension CheckedCastAddrBranchInst {
709+
var dynamicCastResult: Bool? {
710+
switch classifyDynamicCastBridged(bridged) {
711+
case .willSucceed: return true
712+
case .maySucceed: return nil
713+
case .willFail: return false
714+
default: fatalError("unknown result from classifyDynamicCastBridged")
715+
}
716+
}
717+
}

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1481,5 +1481,35 @@ final public class CheckedCastBranchInst : TermInst, UnaryInstruction {
14811481
public var failureBlock: BasicBlock { bridged.CheckedCastBranch_getFailureBlock().block }
14821482
}
14831483

1484-
final public class CheckedCastAddrBranchInst : TermInst, UnaryInstruction {
1484+
final public class CheckedCastAddrBranchInst : TermInst {
1485+
public var source: Value { operands[0].value }
1486+
public var destination: Value { operands[1].value }
1487+
1488+
public var successBlock: BasicBlock { bridged.CheckedCastAddrBranch_getSuccessBlock().block }
1489+
public var failureBlock: BasicBlock { bridged.CheckedCastAddrBranch_getFailureBlock().block }
1490+
1491+
public enum CastConsumptionKind {
1492+
/// The source value is always taken, regardless of whether the cast
1493+
/// succeeds. That is, if the cast fails, the source value is
1494+
/// destroyed.
1495+
case TakeAlways
1496+
1497+
/// The source value is taken only on a successful cast; otherwise,
1498+
/// it is left in place.
1499+
case TakeOnSuccess
1500+
1501+
/// The source value is always left in place, and the destination
1502+
/// value is copied into on success.
1503+
case CopyOnSuccess
1504+
}
1505+
1506+
public var consumptionKind: CastConsumptionKind {
1507+
switch bridged.CheckedCastAddrBranch_getConsumptionKind() {
1508+
case .TakeAlways: return .TakeAlways
1509+
case .TakeOnSuccess: return .TakeOnSuccess
1510+
case .CopyOnSuccess: return .CopyOnSuccess
1511+
default:
1512+
fatalError("invalid cast consumption kind")
1513+
}
1514+
}
14851515
}

include/swift/SIL/SILBridging.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,12 @@ struct BridgedInstruction {
823823
SwiftInt numFunctions;
824824
};
825825

826+
enum class CastConsumptionKind {
827+
TakeAlways,
828+
TakeOnSuccess,
829+
CopyOnSuccess
830+
};
831+
826832
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef CondFailInst_getMessage() const;
827833
BRIDGED_INLINE SwiftInt LoadInst_getLoadOwnership() const ;
828834
BRIDGED_INLINE BuiltinValueKind BuiltinInst_getID() const;
@@ -904,6 +910,9 @@ struct BridgedInstruction {
904910
BRIDGED_INLINE void LoadInst_setOwnership(SwiftInt ownership) const;
905911
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedBasicBlock CheckedCastBranch_getSuccessBlock() const;
906912
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedBasicBlock CheckedCastBranch_getFailureBlock() const;
913+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedBasicBlock CheckedCastAddrBranch_getSuccessBlock() const;
914+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedBasicBlock CheckedCastAddrBranch_getFailureBlock() const;
915+
BRIDGED_INLINE CastConsumptionKind CheckedCastAddrBranch_getConsumptionKind() const;
907916
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSubstitutionMap ApplySite_getSubstitutionMap() const;
908917
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType ApplySite_getSubstitutedCalleeType() const;
909918
BRIDGED_INLINE SwiftInt ApplySite_getNumArguments() const;

include/swift/SIL/SILBridgingImpl.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,27 @@ BridgedBasicBlock BridgedInstruction::CheckedCastBranch_getFailureBlock() const
12441244
return {getAs<swift::CheckedCastBranchInst>()->getFailureBB()};
12451245
}
12461246

1247+
BridgedBasicBlock BridgedInstruction::CheckedCastAddrBranch_getSuccessBlock() const {
1248+
return {getAs<swift::CheckedCastAddrBranchInst>()->getSuccessBB()};
1249+
}
1250+
1251+
BridgedBasicBlock BridgedInstruction::CheckedCastAddrBranch_getFailureBlock() const {
1252+
return {getAs<swift::CheckedCastAddrBranchInst>()->getFailureBB()};
1253+
}
1254+
1255+
BridgedInstruction::CastConsumptionKind BridgedInstruction::CheckedCastAddrBranch_getConsumptionKind() const {
1256+
static_assert((int)BridgedInstruction::CastConsumptionKind::TakeAlways ==
1257+
(int)swift::CastConsumptionKind::TakeAlways);
1258+
static_assert((int)BridgedInstruction::CastConsumptionKind::TakeOnSuccess ==
1259+
(int)swift::CastConsumptionKind::TakeOnSuccess);
1260+
static_assert((int)BridgedInstruction::CastConsumptionKind::CopyOnSuccess ==
1261+
(int)swift::CastConsumptionKind::CopyOnSuccess);
1262+
1263+
return static_cast<BridgedInstruction::CastConsumptionKind>(
1264+
getAs<swift::CheckedCastAddrBranchInst>()->getConsumptionKind());
1265+
}
1266+
1267+
12471268
BridgedSubstitutionMap BridgedInstruction::ApplySite_getSubstitutionMap() const {
12481269
auto as = swift::ApplySite(unbridged());
12491270
return as.getSubstitutionMap();

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ struct BridgedPassContext {
331331

332332
bool FullApplySite_canInline(BridgedInstruction apply);
333333

334+
enum class BridgedDynamicCastResult {
335+
willSucceed,
336+
maySucceed,
337+
willFail
338+
};
339+
340+
BridgedDynamicCastResult classifyDynamicCastBridged(BridgedType sourceTy, BridgedType destTy,
341+
BridgedFunction function,
342+
bool sourceTypeIsExact);
343+
344+
BridgedDynamicCastResult classifyDynamicCastBridged(BridgedInstruction inst);
345+
334346
//===----------------------------------------------------------------------===//
335347
// Pass registration
336348
//===----------------------------------------------------------------------===//

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/Demangling/Demangle.h"
1919
#include "../../IRGen/IRGenModule.h"
2020
#include "swift/SIL/ApplySite.h"
21+
#include "swift/SIL/DynamicCasts.h"
2122
#include "swift/SIL/SILCloner.h"
2223
#include "swift/SIL/SILFunction.h"
2324
#include "swift/SIL/SILModule.h"
@@ -1838,6 +1839,25 @@ bool FullApplySite_canInline(BridgedInstruction apply) {
18381839
swift::FullApplySite(apply.unbridged()));
18391840
}
18401841

1842+
BridgedDynamicCastResult classifyDynamicCastBridged(BridgedType sourceTy, BridgedType destTy,
1843+
BridgedFunction function,
1844+
bool sourceTypeIsExact) {
1845+
static_assert((int)DynamicCastFeasibility::WillSucceed == (int)BridgedDynamicCastResult::willSucceed);
1846+
static_assert((int)DynamicCastFeasibility::MaySucceed == (int)BridgedDynamicCastResult::maySucceed);
1847+
static_assert((int)DynamicCastFeasibility::WillFail == (int)BridgedDynamicCastResult::willFail);
1848+
1849+
return static_cast<BridgedDynamicCastResult>(
1850+
classifyDynamicCast(function.getFunction()->getModule().getSwiftModule(),
1851+
sourceTy.unbridged().getASTType(),
1852+
destTy.unbridged().getASTType(),
1853+
sourceTypeIsExact));
1854+
}
1855+
1856+
BridgedDynamicCastResult classifyDynamicCastBridged(BridgedInstruction inst) {
1857+
SILDynamicCastInst castInst(inst.unbridged());
1858+
return static_cast<BridgedDynamicCastResult>(castInst.classifyFeasibility(/*allowWholeModule=*/ false));
1859+
}
1860+
18411861
// TODO: can't be inlined to work around https://github.com/apple/swift/issues/64502
18421862
BridgedCalleeAnalysis::CalleeList BridgedCalleeAnalysis::getCallees(BridgedValue callee) const {
18431863
return ca->getCalleeListOfValue(callee.getSILValue());

stdlib/public/core/Array.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,6 @@ extension Array: RangeReplaceableCollection {
12541254

12551255
if _slowPath(writtenUpTo == buf.endIndex) {
12561256

1257-
#if !$Embedded
12581257
// A shortcut for appending an Array: If newElements is an Array then it's
12591258
// guaranteed that buf.initialize(from: newElements) already appended all
12601259
// elements. It reduces code size, because the following code
@@ -1264,7 +1263,6 @@ extension Array: RangeReplaceableCollection {
12641263
_internalInvariant(remainder.next() == nil)
12651264
return
12661265
}
1267-
#endif
12681266

12691267
// there may be elements that didn't fit in the existing buffer,
12701268
// append them in slow sequence-only mode

0 commit comments

Comments
 (0)