Skip to content

Commit 89cd626

Browse files
authored
Merge pull request #72472 from kubamracek/embedded-keypaths
[embedded] Compile-time (literal) KeyPaths for Embedded Swift
2 parents 423cfcf + 595ea99 commit 89cd626

File tree

22 files changed

+332
-122
lines changed

22 files changed

+332
-122
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ swift_compiler_sources(Optimizer
2222
SimplifyDestructure.swift
2323
SimplifyGlobalValue.swift
2424
SimplifyInitEnumDataAddr.swift
25+
SimplifyKeyPath.swift
2526
SimplifyLoad.swift
2627
SimplifyPartialApply.swift
2728
SimplifyPointerToAddress.swift

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ extension ApplyInst : OnoneSimplifyable {
1717
if tryTransformThickToThinCallee(of: self, context) {
1818
return
1919
}
20+
if context.tryOptimizeKeypath(apply: self) {
21+
context.erase(instruction: self)
22+
return
23+
}
2024
_ = context.tryDevirtualize(apply: self, isMandatory: false)
2125
}
2226
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===--- SimplifyKeyPath.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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 KeyPathInst : OnoneSimplifyable {
16+
func simplify(_ context: SimplifyContext) {
17+
if allUsesRemovable(instruction: self) {
18+
if parentFunction.hasOwnership {
19+
let builder = Builder(after: self, context)
20+
for operand in self.operands {
21+
if !operand.value.type.isTrivial(in: parentFunction) {
22+
builder.createDestroyValue(operand: operand.value)
23+
}
24+
}
25+
}
26+
context.erase(instructionIncludingAllUsers: self)
27+
}
28+
}
29+
}
30+
31+
fileprivate func allUsesRemovable(instruction: Instruction) -> Bool {
32+
for result in instruction.results {
33+
for use in result.uses {
34+
if !(use.instruction is UpcastInst || use.instruction is DestroyValueInst || use.instruction is BeginBorrowInst || use.instruction is EndBorrowInst) {
35+
return false
36+
}
37+
if !allUsesRemovable(instruction: use.instruction) {
38+
return false
39+
}
40+
}
41+
}
42+
return true
43+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ extension MutatingContext {
170170
}
171171
return nil
172172
}
173+
174+
func tryOptimizeKeypath(apply: FullApplySite) -> Bool {
175+
return _bridged.tryOptimizeKeypath(apply.bridged)
176+
}
173177

174178
func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) {
175179
// This is only a best-effort attempt to notity the new cloned instructions as changed.

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ ERROR(embedded_swift_metatype_type,none,
379379
"cannot use metatype of type %0 in embedded Swift", (Type))
380380
ERROR(embedded_swift_metatype,none,
381381
"cannot use metatype in embedded Swift", ())
382+
ERROR(embedded_swift_keypath,none,
383+
"cannot use key path in embedded Swift", ())
382384
ERROR(embedded_swift_allocating_type,none,
383385
"cannot use allocating type %0 in -no-allocations mode", (Type))
384386
ERROR(embedded_swift_allocating,none,

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ struct BridgedPassContext {
224224
bool tryOptimizeApplyOfPartialApply(BridgedInstruction closure) const;
225225
bool tryDeleteDeadClosure(BridgedInstruction closure, bool needKeepArgsAlive) const;
226226
SWIFT_IMPORT_UNSAFE DevirtResult tryDevirtualizeApply(BridgedInstruction apply, bool isMandatory) const;
227+
bool tryOptimizeKeypath(BridgedInstruction apply) const;
227228
SWIFT_IMPORT_UNSAFE OptionalBridgedValue constantFoldBuiltin(BridgedInstruction builtin) const;
228229
SWIFT_IMPORT_UNSAFE swift::SILVTable * _Nullable specializeVTableForType(BridgedType type,
229230
BridgedFunction function) const;

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,13 @@ bool specializeAppliesInFunction(SILFunction &F,
599599
SILTransform *transform,
600600
bool isMandatory);
601601

602+
bool tryOptimizeKeypath(ApplyInst *AI, SILBuilder Builder);
603+
bool tryOptimizeKeypathApplication(ApplyInst *AI, SILFunction *callee, SILBuilder Builder);
604+
bool tryOptimizeKeypathOffsetOf(ApplyInst *AI, FuncDecl *calleeFn,
605+
KeyPathInst *kp, SILBuilder Builder);
606+
bool tryOptimizeKeypathKVCString(ApplyInst *AI, FuncDecl *calleeFn,
607+
KeyPathInst *kp, SILBuilder Builder);
608+
602609
/// Instantiate the specified type by recursively tupling and structing the
603610
/// unique instances of the empty types and undef "instances" of the non-empty
604611
/// types aggregated together at each level.

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,10 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
519519
diagnose(loc, diag::embedded_swift_value_deinit, impactType.getASTType());
520520
return true;
521521
}
522+
if (isa<KeyPathInst>(inst)) {
523+
diagnose(loc, diag::embedded_swift_keypath);
524+
return true;
525+
}
522526
if (!allowedMetadataUseInEmbeddedSwift(inst)) {
523527
PrettyStackTracePerformanceDiagnostics stackTrace("metatype", inst);
524528
if (impactType) {

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,6 +1628,11 @@ BridgedPassContext::DevirtResult BridgedPassContext::tryDevirtualizeApply(Bridge
16281628
return {{nullptr}, false};
16291629
}
16301630

1631+
bool BridgedPassContext::tryOptimizeKeypath(BridgedInstruction apply) const {
1632+
SILBuilder builder(apply.unbridged());
1633+
return ::tryOptimizeKeypath(apply.getAs<ApplyInst>(), builder);
1634+
}
1635+
16311636
OptionalBridgedValue BridgedPassContext::constantFoldBuiltin(BridgedInstruction builtin) const {
16321637
auto bi = builtin.getAs<BuiltinInst>();
16331638
std::optional<bool> resultsInError;

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,7 @@ class SILCombiner :
330330
SILInstruction *optimizeApplyOfConvertFunctionInst(FullApplySite AI,
331331
ConvertFunctionInst *CFI);
332332

333-
bool tryOptimizeKeypath(ApplyInst *AI);
334333
bool tryOptimizeInoutKeypath(BeginApplyInst *AI);
335-
bool tryOptimizeKeypathApplication(ApplyInst *AI, SILFunction *callee);
336-
bool tryOptimizeKeypathOffsetOf(ApplyInst *AI, FuncDecl *calleeFn,
337-
KeyPathInst *kp);
338-
bool tryOptimizeKeypathKVCString(ApplyInst *AI, FuncDecl *calleeFn,
339-
KeyPathInst *kp);
340334

341335
/// Sinks owned forwarding instructions to their uses if they do not have
342336
/// non-debug non-consuming uses. Deletes any debug_values and destroy_values

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI,
263263
/// %addr = struct_element_addr/ref_element_addr %root_object
264264
/// ...
265265
/// load/store %addr
266-
bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
267-
SILFunction *callee) {
266+
bool swift::tryOptimizeKeypathApplication(ApplyInst *AI,
267+
SILFunction *callee, SILBuilder Builder) {
268268
if (AI->getNumArguments() != 3)
269269
return false;
270270

@@ -303,7 +303,6 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
303303
}
304304
});
305305

306-
eraseInstFromFunction(*AI);
307306
++NumOptimizedKeypaths;
308307
return true;
309308
}
@@ -329,9 +328,9 @@ bool SILCombiner::tryOptimizeKeypathApplication(ApplyInst *AI,
329328
/// %offset_builtin_int = unchecked_trivial_bit_cast %offset_ptr
330329
/// %offset_int = struct $Int (%offset_builtin_int)
331330
/// %offset = enum $Optional<Int>, #Optional.some!enumelt, %offset_int
332-
bool SILCombiner::tryOptimizeKeypathOffsetOf(ApplyInst *AI,
331+
bool swift::tryOptimizeKeypathOffsetOf(ApplyInst *AI,
333332
FuncDecl *calleeFn,
334-
KeyPathInst *kp) {
333+
KeyPathInst *kp, SILBuilder Builder) {
335334
auto *accessor = dyn_cast<AccessorDecl>(calleeFn);
336335
if (!accessor || !accessor->isGetter())
337336
return false;
@@ -432,7 +431,6 @@ bool SILCombiner::tryOptimizeKeypathOffsetOf(ApplyInst *AI,
432431
result = Builder.createOptionalNone(loc, AI->getType());
433432
}
434433
AI->replaceAllUsesWith(result);
435-
eraseInstFromFunction(*AI);
436434
++NumOptimizedKeypaths;
437435
return true;
438436
}
@@ -444,9 +442,9 @@ bool SILCombiner::tryOptimizeKeypathOffsetOf(ApplyInst *AI,
444442
/// %string = apply %keypath_kvcString_method(%kp)
445443
/// With:
446444
/// %string = string_literal "blah"
447-
bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI,
445+
bool swift::tryOptimizeKeypathKVCString(ApplyInst *AI,
448446
FuncDecl *calleeFn,
449-
KeyPathInst *kp) {
447+
KeyPathInst *kp, SILBuilder Builder) {
450448
if (!calleeFn->getAttrs()
451449
.hasSemanticsAttr(semantics::KEYPATH_KVC_KEY_PATH_STRING))
452450
return false;
@@ -499,14 +497,13 @@ bool SILCombiner::tryOptimizeKeypathKVCString(ApplyInst *AI,
499497
}
500498

501499
AI->replaceAllUsesWith(literalValue);
502-
eraseInstFromFunction(*AI);
503500
++NumOptimizedKeypaths;
504501
return true;
505502
}
506503

507-
bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
504+
bool swift::tryOptimizeKeypath(ApplyInst *AI, SILBuilder Builder) {
508505
if (SILFunction *callee = AI->getReferencedFunctionOrNull()) {
509-
return tryOptimizeKeypathApplication(AI, callee);
506+
return tryOptimizeKeypathApplication(AI, callee, Builder);
510507
}
511508

512509
// Try optimize keypath method calls.
@@ -530,10 +527,10 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
530527
if (!kp || !kp->hasPattern())
531528
return false;
532529

533-
if (tryOptimizeKeypathOffsetOf(AI, calleeFn, kp))
530+
if (tryOptimizeKeypathOffsetOf(AI, calleeFn, kp, Builder))
534531
return true;
535532

536-
if (tryOptimizeKeypathKVCString(AI, calleeFn, kp))
533+
if (tryOptimizeKeypathKVCString(AI, calleeFn, kp, Builder))
537534
return true;
538535

539536
return false;
@@ -1465,8 +1462,10 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
14651462
if (auto *CFI = dyn_cast<ConvertFunctionInst>(callee))
14661463
return optimizeApplyOfConvertFunctionInst(AI, CFI);
14671464

1468-
if (tryOptimizeKeypath(AI))
1465+
if (tryOptimizeKeypath(AI, Builder)) {
1466+
eraseInstFromFunction(*AI);
14691467
return nullptr;
1468+
}
14701469

14711470
// Optimize readonly functions with no meaningful users.
14721471
SILFunction *SF = AI->getReferencedFunctionOrNull();

lib/SILOptimizer/Utils/KeyPathProjector.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SILOptimizer/Utils/KeyPathProjector.h"
2121

2222
#include "swift/SIL/SILInstruction.h"
23+
#include "swift/SIL/InstructionUtils.h"
2324

2425
using namespace swift;
2526

@@ -119,8 +120,13 @@ class StoredPropertyProjector : public ComponentProjector {
119120
} else {
120121
// Accessing a class member -> reading the class
121122
parent->project(AccessType::Get, [&](SILValue parentValue) {
122-
SingleValueInstruction *Ref = builder.createLoad(loc, parentValue,
123-
LoadOwnershipQualifier::Unqualified);
123+
SingleValueInstruction *Borrow = nullptr;
124+
SingleValueInstruction *Ref;
125+
if (builder.hasOwnership()) {
126+
Ref = Borrow = builder.createLoadBorrow(loc, parentValue);
127+
} else {
128+
Ref = builder.createLoad(loc, parentValue, LoadOwnershipQualifier::Unqualified);
129+
}
124130

125131
// If we were previously accessing a class member, we're done now.
126132
insertEndAccess(beginAccess, builder);
@@ -134,6 +140,9 @@ class StoredPropertyProjector : public ComponentProjector {
134140
// decl or in a superclass of it. Just handle this to be on the safe
135141
// side.
136142
callback(SILValue());
143+
if (Borrow) {
144+
builder.createEndBorrow(loc, Borrow);
145+
}
137146
return;
138147
}
139148
Ref = builder.createUpcast(loc, Ref, superCl);
@@ -162,6 +171,10 @@ class StoredPropertyProjector : public ComponentProjector {
162171
if (beginAccess == addr) {
163172
insertEndAccess(beginAccess, builder);
164173
}
174+
175+
if (Borrow) {
176+
builder.createEndBorrow(loc, Borrow);
177+
}
165178
});
166179
}
167180
}
@@ -669,9 +682,10 @@ class CompleteKeyPathProjector : public KeyPathProjector {
669682

670683
KeyPathInst *
671684
KeyPathProjector::getLiteralKeyPath(SILValue keyPath) {
672-
if (auto *upCast = dyn_cast<UpcastInst>(keyPath))
673-
keyPath = upCast->getOperand();
674-
// TODO: Look through other conversions, copies, etc.?
685+
while (auto *upCast = dyn_cast<UpcastInst>(keyPath)) {
686+
keyPath = lookThroughOwnershipInsts(upCast->getOperand());
687+
}
688+
675689
return dyn_cast<KeyPathInst>(keyPath);
676690
}
677691

stdlib/public/core/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ split_embedded_sources(
9797
NORMAL IntegerParsing.swift
9898
EMBEDDED Integers.swift
9999
NORMAL Join.swift
100-
NORMAL KeyPath.swift
100+
EMBEDDED KeyPath.swift
101101
NORMAL KeyValuePairs.swift
102102
EMBEDDED LazyCollection.swift
103103
EMBEDDED LazySequence.swift
@@ -125,7 +125,7 @@ split_embedded_sources(
125125
NORMAL PrefixWhile.swift
126126
NORMAL Prespecialize.swift
127127
NORMAL Print.swift
128-
NORMAL PtrAuth.swift
128+
EMBEDDED PtrAuth.swift
129129
EMBEDDED Random.swift
130130
EMBEDDED RandomAccessCollection.swift
131131
EMBEDDED Range.swift

stdlib/public/core/EmbeddedStubs.swift

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SwiftShims
1818
public struct String: Hashable {
1919
public var utf8CString: ContiguousArray<CChar> { fatalError() }
2020
public init() {}
21+
public init(validatingCString: UnsafePointer<CChar>) { fatalError() }
2122
}
2223

2324
@_unavailableInEmbedded
@@ -242,20 +243,3 @@ public enum DecodingError: Error {
242243
case keyNotFound(any CodingKey, Context)
243244
case dataCorrupted(Context)
244245
}
245-
246-
/// KeyPath
247-
248-
@_unavailableInEmbedded
249-
public class AnyKeyPath {
250-
@usableFromInline
251-
internal var _storedInlineOffset: Int? { fatalError() }
252-
}
253-
254-
@_unavailableInEmbedded
255-
public class PartialKeyPath<Root>: AnyKeyPath { }
256-
257-
@_unavailableInEmbedded
258-
public class KeyPath<Root, Value>: PartialKeyPath<Root> { }
259-
260-
@_unavailableInEmbedded
261-
public class WritableKeyPath<Root, Value>: KeyPath<Root, Value> { }

0 commit comments

Comments
 (0)