Skip to content

Commit 14cda1a

Browse files
authored
Merge pull request #28799 from NobodyNada/master
[SILOptimizer] Generalize optimization of static keypaths
2 parents 6c19d6f + 1feead8 commit 14cda1a

File tree

8 files changed

+1222
-121
lines changed

8 files changed

+1222
-121
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//===-- KeyPathProjector.h - Project a static key path ----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
/// \file
14+
/// Utility class to project a statically known key path
15+
/// expression to a direct property access sequence.
16+
///
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_SILOPTIMIZER_UTILS_KEYPATHPROJECTOR_H
20+
#define SWIFT_SILOPTIMIZER_UTILS_KEYPATHPROJECTOR_H
21+
22+
#include "swift/SIL/SILBuilder.h"
23+
#include <memory>
24+
25+
namespace swift {
26+
27+
/// Projects a statically known key path expression to
28+
/// a direct property access.
29+
class KeyPathProjector {
30+
public:
31+
/// The type of a key path access.
32+
enum class AccessType {
33+
/// A get-only access (i.e. swift_getAtKeyPath).
34+
Get,
35+
36+
/// A set-only access (i.e. swift_setAtWritableKeyPath).
37+
Set,
38+
39+
/// A modification (i.e. swift_modifyAtWritableKeyPath).
40+
Modify
41+
};
42+
43+
/// Creates a key path projector for a key path.
44+
///
45+
/// Returns nullptr if \p keyPath is not a keypath instruction or if there is
46+
/// any other reason why the optimization cannot be done.
47+
///
48+
/// \param keyPath The key path to project. Must be the result of either
49+
/// a keypath instruction or an upcast of a key path instruction.
50+
/// \param root The address of the object the key path is applied to.
51+
/// \param loc The location of the key path application.
52+
/// \param builder The SILBuilder to use.
53+
static std::unique_ptr<KeyPathProjector>
54+
create(SILValue keyPath, SILValue root, SILLocation loc, SILBuilder &builder);
55+
56+
/// Projects the key path to an address. Sets up the projection,
57+
/// invokes the callback, then tears down the projection.
58+
/// \param accessType The access type of the projected address.
59+
/// \param callback A callback to invoke with the projected adddress.
60+
/// The projected address is only valid from within \p callback.
61+
/// If accessType is Get or Modify, the projected addres is an
62+
/// initialized address type. If accessType is set, the projected
63+
/// address points to uninitialized memory.
64+
virtual void project(AccessType accessType,
65+
std::function<void(SILValue addr)> callback) = 0;
66+
67+
virtual ~KeyPathProjector() {};
68+
69+
/// Whether this projection returns a struct.
70+
virtual bool isStruct() = 0;
71+
protected:
72+
KeyPathProjector(SILLocation loc, SILBuilder &builder)
73+
: loc(loc), builder(builder) {}
74+
75+
/// The location of the key path application.
76+
SILLocation loc;
77+
78+
/// The SILBuilder to use.
79+
SILBuilder &builder;
80+
};
81+
82+
} // end namespace swift
83+
84+
#endif /* KeyPathProjector_h */

lib/SILGen/SILGen.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ void SILGenModule::tryEmitPropertyDescriptor(AbstractStorageDecl *decl) {
14601460
baseOperand, needsGenericContext,
14611461
subs, decl, {},
14621462
baseTy->getCanonicalType(),
1463+
M.getSwiftModule(),
14631464
/*property descriptor*/ true);
14641465

14651466
(void)SILProperty::create(M, /*serialized*/ false, decl, component);

lib/SILGen/SILGen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
339339
AbstractStorageDecl *storage,
340340
ArrayRef<ProtocolConformanceRef> indexHashables,
341341
CanType baseTy,
342+
DeclContext *useDC,
342343
bool forPropertyDescriptor);
343344

344345
/// Known functions for bridging.

lib/SILGen/SILGenExpr.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
33833383
AbstractStorageDecl *storage,
33843384
ArrayRef<ProtocolConformanceRef> indexHashables,
33853385
CanType baseTy,
3386+
DeclContext *useDC,
33863387
bool forPropertyDescriptor) {
33873388
auto baseDecl = storage;
33883389

@@ -3452,8 +3453,8 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
34523453
// supply the settability if needed. We only reference it here if the
34533454
// setter is public.
34543455
if (shouldUseExternalKeyPathComponent())
3455-
return storage->isSettable(M.getSwiftModule())
3456-
&& storage->isSetterAccessibleFrom(M.getSwiftModule());
3456+
return storage->isSettable(useDC)
3457+
&& storage->isSetterAccessibleFrom(useDC);
34573458
return storage->isSettable(storage->getDeclContext());
34583459
};
34593460

@@ -3607,6 +3608,7 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
36073608
decl,
36083609
component.getSubscriptIndexHashableConformances(),
36093610
baseTy,
3611+
SGF.FunctionDC,
36103612
/*for descriptor*/ false));
36113613
baseTy = loweredComponents.back().getComponentType();
36123614
if (kind == KeyPathExpr::Component::Kind::Property)

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 39 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
2929
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
3030
#include "swift/SILOptimizer/Utils/Existential.h"
31+
#include "swift/SILOptimizer/Utils/KeyPathProjector.h"
3132
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
3233
#include "llvm/ADT/DenseMap.h"
3334
#include "llvm/ADT/SmallPtrSet.h"
3435
#include "llvm/ADT/SmallVector.h"
3536
#include "llvm/ADT/Statistic.h"
37+
#include <utility>
3638

3739
using namespace swift;
3840
using namespace swift::PatternMatch;
@@ -207,92 +209,6 @@ SILCombiner::optimizeApplyOfConvertFunctionInst(FullApplySite AI,
207209
return NAI;
208210
}
209211

210-
/// Ends the begin_access "scope" if a begin_access was inserted for optimizing
211-
/// a keypath pattern.
212-
static void insertEndAccess(BeginAccessInst *&beginAccess, bool isModify,
213-
SILBuilder &builder) {
214-
if (beginAccess) {
215-
builder.createEndAccess(beginAccess->getLoc(), beginAccess,
216-
/*aborted*/ false);
217-
if (isModify)
218-
beginAccess->setAccessKind(SILAccessKind::Modify);
219-
beginAccess = nullptr;
220-
}
221-
}
222-
223-
/// Creates the projection pattern for a keypath instruction.
224-
///
225-
/// Currently only the StoredProperty pattern is handled.
226-
/// TODO: handle other patterns, like getters/setters, optional chaining, etc.
227-
///
228-
/// Returns false if \p keyPath is not a keypath instruction or if there is any
229-
/// other reason why the optimization cannot be done.
230-
static SILValue createKeypathProjections(SILValue keyPath, SILValue root,
231-
SILLocation loc,
232-
BeginAccessInst *&beginAccess,
233-
SILBuilder &builder) {
234-
if (auto *upCast = dyn_cast<UpcastInst>(keyPath))
235-
keyPath = upCast->getOperand();
236-
237-
// Is it a keypath instruction at all?
238-
auto *kpInst = dyn_cast<KeyPathInst>(keyPath);
239-
if (!kpInst || !kpInst->hasPattern())
240-
return SILValue();
241-
242-
auto components = kpInst->getPattern()->getComponents();
243-
244-
// Check if the keypath only contains patterns which we support.
245-
for (const KeyPathPatternComponent &comp : components) {
246-
if (comp.getKind() != KeyPathPatternComponent::Kind::StoredProperty)
247-
return SILValue();
248-
}
249-
250-
SILValue addr = root;
251-
for (const KeyPathPatternComponent &comp : components) {
252-
assert(comp.getKind() == KeyPathPatternComponent::Kind::StoredProperty);
253-
VarDecl *storedProperty = comp.getStoredPropertyDecl();
254-
SILValue elementAddr;
255-
if (addr->getType().getStructOrBoundGenericStruct()) {
256-
addr = builder.createStructElementAddr(loc, addr, storedProperty);
257-
} else if (addr->getType().getClassOrBoundGenericClass()) {
258-
SingleValueInstruction *Ref = builder.createLoad(loc, addr,
259-
LoadOwnershipQualifier::Unqualified);
260-
insertEndAccess(beginAccess, /*isModify*/ false, builder);
261-
262-
// Handle the case where the storedProperty is in a super class.
263-
while (Ref->getType().getClassOrBoundGenericClass() !=
264-
storedProperty->getDeclContext()) {
265-
SILType superCl = Ref->getType().getSuperclass();
266-
if (!superCl) {
267-
// This should never happen, because the property should be in the
268-
// decl or in a superclass of it. Just handle this to be on the safe
269-
// side.
270-
return SILValue();
271-
}
272-
Ref = builder.createUpcast(loc, Ref, superCl);
273-
}
274-
275-
addr = builder.createRefElementAddr(loc, Ref, storedProperty);
276-
277-
// Class members need access enforcement.
278-
if (builder.getModule().getOptions().EnforceExclusivityDynamic) {
279-
beginAccess = builder.createBeginAccess(loc, addr, SILAccessKind::Read,
280-
SILAccessEnforcement::Dynamic,
281-
/*noNestedConflict*/ false,
282-
/*fromBuiltin*/ false);
283-
addr = beginAccess;
284-
}
285-
} else {
286-
// This should never happen, as a stored-property pattern can only be
287-
// applied to classes and structs. But to be safe - and future prove -
288-
// let's handle this case and bail.
289-
insertEndAccess(beginAccess, /*isModify*/ false, builder);
290-
return SILValue();
291-
}
292-
}
293-
return addr;
294-
}
295-
296212
/// Try to optimize a keypath application with an apply instruction.
297213
///
298214
/// Replaces (simplified SIL):
@@ -311,36 +227,40 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
311227
return false;
312228

313229
SILValue keyPath, rootAddr, valueAddr;
314-
bool isModify = false;
230+
bool isSet = false;
315231
if (callee->getName() == "swift_setAtWritableKeyPath" ||
316232
callee->getName() == "swift_setAtReferenceWritableKeyPath") {
317233
keyPath = AI->getArgument(1);
318234
rootAddr = AI->getArgument(0);
319235
valueAddr = AI->getArgument(2);
320-
isModify = true;
236+
isSet = true;
321237
} else if (callee->getName() == "swift_getAtKeyPath") {
322238
keyPath = AI->getArgument(2);
323239
rootAddr = AI->getArgument(1);
324240
valueAddr = AI->getArgument(0);
325241
} else {
326242
return false;
327243
}
328-
329-
BeginAccessInst *beginAccess = nullptr;
330-
SILValue projectedAddr = createKeypathProjections(keyPath, rootAddr,
331-
AI->getLoc(), beginAccess,
332-
Builder);
333-
if (!projectedAddr)
244+
245+
auto projector = KeyPathProjector::create(keyPath, rootAddr,
246+
AI->getLoc(), Builder);
247+
if (!projector)
334248
return false;
335-
336-
if (isModify) {
337-
Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr,
338-
IsTake, IsNotInitialization);
339-
} else {
340-
Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr,
341-
IsNotTake, IsInitialization);
342-
}
343-
insertEndAccess(beginAccess, isModify, Builder);
249+
250+
KeyPathProjector::AccessType accessType;
251+
if (isSet) accessType = KeyPathProjector::AccessType::Set;
252+
else accessType = KeyPathProjector::AccessType::Get;
253+
254+
projector->project(accessType, [&](SILValue projectedAddr) {
255+
if (isSet) {
256+
Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr,
257+
IsTake, IsInitialization);
258+
} else {
259+
Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr,
260+
IsNotTake, IsInitialization);
261+
}
262+
});
263+
344264
eraseInstFromFunction(*AI);
345265
++NumOptimizedKeypaths;
346266
return true;
@@ -385,19 +305,24 @@ bool SILCombiner::tryOptimizeInoutKeypath(BeginApplyInst *AI) {
385305
EndApplyInst *endApply = dyn_cast<EndApplyInst>(AIUse->getUser());
386306
if (!endApply)
387307
return false;
388-
389-
BeginAccessInst *beginAccess = nullptr;
390-
SILValue projectedAddr = createKeypathProjections(keyPath, rootAddr,
391-
AI->getLoc(), beginAccess,
392-
Builder);
393-
if (!projectedAddr)
308+
309+
auto projector = KeyPathProjector::create(keyPath, rootAddr,
310+
AI->getLoc(), Builder);
311+
if (!projector)
394312
return false;
313+
314+
KeyPathProjector::AccessType accessType;
315+
if (isModify) accessType = KeyPathProjector::AccessType::Modify;
316+
else accessType = KeyPathProjector::AccessType::Get;
317+
318+
projector->project(accessType, [&](SILValue projectedAddr) {
319+
// Replace the projected address.
320+
valueAddr->replaceAllUsesWith(projectedAddr);
321+
322+
// Skip to the end of the key path application before cleaning up.
323+
Builder.setInsertionPoint(endApply);
324+
});
395325

396-
// Replace the projected address.
397-
valueAddr->replaceAllUsesWith(projectedAddr);
398-
399-
Builder.setInsertionPoint(endApply);
400-
insertEndAccess(beginAccess, isModify, Builder);
401326
eraseInstFromFunction(*endApply);
402327
eraseInstFromFunction(*AI);
403328
++NumOptimizedKeypaths;

lib/SILOptimizer/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ silopt_register_sources(
1111
GenericCloner.cpp
1212
Generics.cpp
1313
InstOptUtils.cpp
14+
KeyPathProjector.cpp
1415
LoadStoreOptUtils.cpp
1516
LoopUtils.cpp
1617
OptimizerStatsUtils.cpp

0 commit comments

Comments
 (0)