1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
|
//===--- LetPropertiesOpts.cpp - Optimize let properties ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// Promote values of non-static let properties initialized by means
// of constant values of simple types into their uses.
//
// For any given non-static let property this optimization is only possible
// if this pass can prove that it has analyzed all assignments of an initial
// value to this property and all those assignments assign the same value
// to this property.
//
// FIXME:
//
// This pass makes assumptions about the visibility of a type's memory
// based on the visibility of its properties. This is the wrong way to think
// about memory visibility.
//
// This pass wants assume that the contents of a property is known based on
// whether the property is declared as a 'let' and the visibility of the
// initializers that access the property. For example:
//
// public struct X<T> {
// public let hidden: T
//
// init(t: T) { self.hidden = t }
// }
//
// The pass currently assumes that `X` only takes on values that are
// assigned by the invocations of `X.init`, which is only visible in `X`s
// module. This is wrong if the layout of `Impl` is exposed to other
// modules. A struct's memory may be initialized by any module with
// access to the struct's layout.
//
// In fact, this assumption is wrong even if the struct, and it's let
// property cannot be accessed externally by name. In this next example,
// external modules cannot access `Impl` or `Impl.hidden` by name, but
// can still access the memory because the layout is exposed via a public type
// that contains it.
//
// ```
// internal struct Impl<T> {
// let hidden: T
//
// init(t: T) { self.hidden = t }
// }
//
// public struct Wrapper<T> {
// var impl: Impl<T>
//
// public var property: T {
// get {
// return impl.hidden
// }
// }
// }
// ```
//
// As long as `Wrapper`s layout is exposed to other modules, the contents of
// `Wrapper`, `Impl`, and `hidden' can all be initialized in another
// module. This following code is legal if Wrapper's home module is *not*
// built with library evolution (or if Wrapper is declared `@frozen`).
//
// func inExternalModule(buffer: UnsafeRawPointer) -> Wrapper<Int64> {
// return buffer.load(as: Wrapper<Int64>.self)
// }
//
// If library evolution is enabled and a `public` struct is not declared
// `@frozen` then external modules cannot assume its layout, and therefore
// cannot initialize the struct memory. In that case, it is possible to optimize
// `X.hidden` and `Impl.hidden` as if the properties are only initialized inside
// their home module.
//
// The right way to view a type's memory visibility is to consider whether
// external modules have access to the layout of the type. If not, then the
// property can still be optimized As long as a struct is never enclosed in a
// public effectively-`@frozen` type. However, finding all places where a struct
// is explicitly created is still insufficient. Instead, the optimization needs
// to find all uses of enclosing types and determine if every use has a known
// constant initialization, or is simply copied from another value. If an
// escaping unsafe pointer to any enclosing type is created, then the
// optimization is not valid.
//
// When viewed this way, the fact that a property is declared 'let' is mostly
// irrelevant to this optimization--it can be expanded to handle non-'let'
// properties. The more salient feature is whether the property has a public
// setter.
//
// For now, this optimization only recognizes class properties because class
// properties are only accessibly via a ref_element_addr instruction. This is a
// side effect of the fact that accessing a class property requires a "formal
// access". This means that begin_access marker must be emitted directly on the
// address produced by a ref_element_addr. Struct properties are not handled, as
// explained above, because they can be indirectly accessed via addresses of
// outer types.
//
// Note: Propagating the initialized constants of non-addressable aggregate
// values (formation of 'struct's and 'tuple's) is a significantly different
// problem. It can be done better in a separate constant-propagation pass that
// propagates partial-constants into call arguments and out of returned values.
// ===---------------------------------------------------------------------===//
#define DEBUG_TYPE "let-properties-opt"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLinkage.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
using namespace swift;
namespace {
using InstructionList = SmallVector<SILInstruction *, 8>;
struct InitSequence {
InstructionList Instructions;
SILValue Result;
bool isValid() const {
return (bool) Result;
}
};
/// Promote values of non-static let properties initialized by means
/// of constant values of simple types into their uses.
///
/// TODO: Don't occupy any storage for such let properties with constant
/// initializers.
///
/// Note: Storage from a 'let' property can only be removed if this property if
/// the type is resilient (not fixed-layout) and the property cannot be read
/// from another module.
class LetPropertiesOpt {
SILModule *Module;
typedef SmallVector<VarDecl *, 4> Properties;
llvm::SetVector<SILFunction *> ChangedFunctions;
// Map each let property to a set of instructions accessing it.
llvm::MapVector<VarDecl *, InstructionList> AccessMap;
// Map each let property to the instruction sequence which initializes it.
llvm::MapVector<VarDecl *, InitSequence> InitMap;
// Properties in this set should not be processed by this pass
// anymore.
llvm::SmallPtrSet<VarDecl *, 16> SkipProcessing;
// Types in this set should not be processed by this pass
// anymore.
llvm::SmallPtrSet<NominalTypeDecl *, 16> SkipTypeProcessing;
// Properties in this set cannot be removed.
llvm::SmallPtrSet<VarDecl *, 16> CannotRemove;
// Set of let properties in a given nominal type.
llvm::MapVector<NominalTypeDecl *, Properties> NominalTypeLetProperties;
// Set of properties which already fulfill all conditions, except
// the available of constant, statically known initializer.
llvm::SmallPtrSet<VarDecl *, 16> PotentialConstantLetProperty;
public:
LetPropertiesOpt(SILModule *M): Module(M) {}
void run(SILModuleTransform *T);
protected:
bool isConstantLetProperty(VarDecl *Property);
void collectPropertyAccess(SingleValueInstruction *I, VarDecl *Property,
bool NonRemovable);
void optimizeLetPropertyAccess(VarDecl *SILG, const InitSequence &Init);
bool analyzeInitValue(SILInstruction *I, VarDecl *Prop);
};
/// Helper class to copy only a set of SIL instructions providing in the
/// constructor.
class InitSequenceCloner : public SILClonerWithScopes<InitSequenceCloner> {
friend class SILInstructionVisitor<InitSequenceCloner>;
friend class SILCloner<InitSequenceCloner>;
const InitSequence &Init;
public:
InitSequenceCloner(const InitSequence &init, SILInstruction *destIP)
: SILClonerWithScopes(*destIP->getFunction()), Init(init) {
Builder.setInsertionPoint(destIP);
}
void process(SILInstruction *I) { visit(I); }
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
SILValue getMappedValue(SILValue Value) {
return SILCloner<InitSequenceCloner>::getMappedValue(Value);
}
/// Clone all the instructions from Insns into the destination function,
/// immediately before the destination block, and return the value of
/// the result.
SILValue clone() {
for (auto I : Init.Instructions)
process(I);
return getMappedValue(Init.Result);
}
};
} // end anonymous namespace
#ifndef NDEBUG
// For debugging only.
static raw_ostream &operator<<(raw_ostream &OS, const VarDecl &decl) {
auto *Ty = dyn_cast<NominalTypeDecl>(decl.getDeclContext());
if (Ty)
OS << Ty->getName() << "::";
OS << decl.getName();
return OS;
}
#endif
/// Optimize access to the let property, which is known
/// to have a constant value. Replace all loads from the
/// property by its constant value.
void LetPropertiesOpt::optimizeLetPropertyAccess(VarDecl *Property,
const InitSequence &init) {
assert(init.isValid());
if (SkipProcessing.count(Property))
return;
auto *Ty = dyn_cast<NominalTypeDecl>(Property->getDeclContext());
// Ty is null for properties declared inside an extension of an ObjC type.
if (!Ty || SkipTypeProcessing.count(Ty))
return;
LLVM_DEBUG(llvm::dbgs() << "Replacing access to property '" << *Property
<< "' by its constant initializer\n");
auto PropertyAccess = Property->getEffectiveAccess();
auto TypeAccess = Ty->getEffectiveAccess();
auto CanRemove = false;
// Check if a given let property can be removed, because it
// is not accessible elsewhere. This can happen if this property
// is private or if it is internal and WMO mode is used.
if (TypeAccess <= AccessLevel::FilePrivate ||
PropertyAccess <= AccessLevel::FilePrivate
|| ((TypeAccess <= AccessLevel::Internal ||
PropertyAccess <= AccessLevel::Internal) &&
Module->isWholeModule())) {
CanRemove = true;
LLVM_DEBUG(llvm::dbgs() << "Storage for property '" << *Property
<< "' can be eliminated\n");
}
if (CannotRemove.count(Property))
CanRemove = false;
if (!AccessMap.count(Property)) {
LLVM_DEBUG(llvm::dbgs() << "Property '" << *Property <<"' is never read\n");
if (CanRemove) {
// TODO: Remove the let property, because it is never accessed.
}
return;
}
InstructionDeleter deleter;
auto &Loads = AccessMap[Property];
unsigned NumReplaced = 0;
for (auto Load: Loads) {
SILFunction *F = Load->getFunction();
// A helper function to copy the initializer into the target function
// at the target insertion point.
auto cloneInitAt = [&](SILInstruction *insertionPoint) -> SILValue {
InitSequenceCloner cloner(init, insertionPoint);
return cloner.clone();
};
// Look for any instructions accessing let properties.
auto *proj = cast<RefElementAddrInst>(Load);
// Copy the initializer into the function
// Replace the access to a let property by the value
// computed by this initializer.
SILValue clonedInit = cloneInitAt(proj);
for (auto UI = proj->use_begin(), E = proj->use_end(); UI != E;) {
auto *User = UI->getUser();
++UI;
if (!canReplaceLoadSequence(User))
continue;
replaceLoadSequence(User, clonedInit);
deleter.forceDeleteWithUsers(User);
++NumReplaced;
}
ChangedFunctions.insert(F);
}
deleter.cleanupDeadInstructions();
LLVM_DEBUG(llvm::dbgs() << "Access to " << *Property << " was replaced "
<< NumReplaced << " time(s)\n");
if (CanRemove) {
// TODO: Remove the let property, because it is never accessed.
}
}
/// Compare two SILValues structurally.
static bool isStructurallyIdentical(SILValue LHS, SILValue RHS) {
if (LHS == RHS)
return true;
if (LHS->getType() != RHS->getType())
return false;
auto lResult = LHS->getDefiningInstructionResult();
auto rResult = RHS->getDefiningInstructionResult();
assert(lResult && rResult &&
"operands of instructions approved by analyzeStaticInitializer "
"should always be defined by instructions");
return (lResult->ResultIndex == rResult->ResultIndex &&
lResult->Instruction->isIdenticalTo(rResult->Instruction,
isStructurallyIdentical));
}
/// Compare two sequences of SIL instructions. They should be structurally
/// equivalent.
static bool isSameInitSequence(const InitSequence &LHS,
const InitSequence &RHS) {
assert(LHS.isValid() && RHS.isValid());
// This will recursively check all the instructions. It's possible
// that they'll be composed slightly differently, but it shouldn't matter.
return isStructurallyIdentical(LHS.Result, RHS.Result);
}
/// Check if a given let property can be assigned externally.
static bool isAssignableExternally(VarDecl *Property, SILModule *Module) {
if (Module->isVisibleExternally(Property)) {
// If at least one of the properties of the enclosing type cannot be
// used externally, then no initializer can be implemented externally as
// it wouldn't be able to initialize such a property.
// More over, for classes, only the class itself can initialize its
// let properties. Subclasses and extensions cannot do it.
// For structs, external extensions may initialize let properties. But to do
// that they need to be able to initialize all properties, i.e. all
// properties should be accessible by the extension.
auto *Ty = dyn_cast<NominalTypeDecl>(Property->getDeclContext());
// Check for "unusual" decl contexts, e.g. ObjC extensions.
if (!Ty)
return true;
// Initializer for a let property of a class cannot exist externally.
// It cannot be defined by an extension or a derived class.
if (isa<ClassDecl>(Ty))
return false;
// Check if there are any private properties or any internal properties and
// it is a whole module compilation. In this case, no external initializer
// may exist.
for (auto SP : Ty->getStoredProperties()) {
auto storedPropertyAccess = SP->getEffectiveAccess();
if (storedPropertyAccess <= AccessLevel::FilePrivate ||
(storedPropertyAccess <= AccessLevel::Internal &&
Module->isWholeModule())) {
LLVM_DEBUG(llvm::dbgs() << "Property " << *Property
<< " cannot be set externally\n");
return false;
}
}
LLVM_DEBUG(llvm::dbgs() << "Property " << *Property
<< " can be used externally\n");
return true;
}
return false;
}
// Checks if a given property may have any unknown uses which cannot
// be analyzed by this pass.
static bool mayHaveUnknownUses(VarDecl *Property, SILModule *Module) {
if (Property->getDeclContext()->getParentModule() !=
Module->getSwiftModule()) {
LLVM_DEBUG(llvm::dbgs() << "Property " << *Property
<< " is defined in a different module\n");
// We don't see the bodies of initializers from a different module
// unless all of them are fragile.
// TODO: Support fragile initializers.
return true;
}
// If let properties can be assigned externally, we don't know
// the values they may get.
if (isAssignableExternally(Property, Module)) {
return true;
}
return false;
}
/// Check if a given property is a non-static let property
/// with known constant value.
bool LetPropertiesOpt::isConstantLetProperty(VarDecl *Property) {
// Process only non-static let properties here.
if (!Property->isLet() || Property->isStatic())
return false;
// Do not re-process already known properties.
if (SkipProcessing.count(Property))
return false;
// If these checks were performed already, no need to
// repeat them.
if (PotentialConstantLetProperty.count(Property))
return true;
// Check the visibility of this property. If its visibility
// implies that this optimization pass cannot analyze all uses,
// don't process it.
if (mayHaveUnknownUses(Property, Module)) {
LLVM_DEBUG(llvm::dbgs() << "Property '" << *Property
<< "' may have unknown uses\n");
SkipProcessing.insert(Property);
return false;
}
LLVM_DEBUG(llvm::dbgs() << "Property '" << *Property
<< "' has no unknown uses\n");
PotentialConstantLetProperty.insert(Property);
return true;
}
static bool isProjectionOfProperty(SILValue addr, VarDecl *Property) {
addr = stripAccessMarkers(addr);
if (auto *REA = dyn_cast<RefElementAddrInst>(addr)) {
return REA->getField() == Property;
}
return false;
}
// Analyze the init value being stored by the instruction into a property.
bool
LetPropertiesOpt::analyzeInitValue(SILInstruction *I, VarDecl *Property) {
SILValue value;
SILValue dest;
if (auto SI = dyn_cast<StoreInst>(I)) {
dest = stripAccessMarkers(SI->getDest());
value = SI->getSrc();
} else if (auto *copyAddr = dyn_cast<CopyAddrInst>(I)) {
dest = stripAccessMarkers(copyAddr->getDest());
value = copyAddr->getSrc();
} else {
return false;
}
assert(isProjectionOfProperty(dest, Property)
&& "Store instruction should store into a proper let property");
(void)dest;
// Check if it's just a copy from another instance of the struct.
if (auto *LI = dyn_cast<LoadInst>(value)) {
SILValue addr = LI->getOperand();
if (isProjectionOfProperty(addr, Property))
return true;
}
// Bail if a value of a property is not a statically known constant init.
InitSequence sequence;
sequence.Result = value;
if (!analyzeStaticInitializer(value, sequence.Instructions))
return false;
auto &cachedSequence = InitMap[Property];
if (cachedSequence.isValid() &&
!isSameInitSequence(cachedSequence, sequence)) {
// The found init value is different from the already seen init value.
return false;
} else {
LLVM_DEBUG(llvm::dbgs() << "The value of property '" << *Property
<< "' is statically known so far\n");
// Remember the statically known value.
cachedSequence = std::move(sequence);
return true;
}
}
/// Check if I is a sequence of projections followed by a load.
/// Since it is supposed to be a load from a let property with
/// statically known constant initializer, only struct_element_addr
/// and tuple_element_addr projections are considered.
static bool isValidPropertyLoad(SILInstruction *I) {
if (isa<LoadInst>(I))
return true;
if (isa<StructElementAddrInst>(I) || isa<TupleElementAddrInst>(I)
|| isa<BeginAccessInst>(I)) {
auto projection = cast<SingleValueInstruction>(I);
for (auto Use : getNonDebugUses(projection)) {
if (isIncidentalUse(Use->getUser()))
continue;
if (!isValidPropertyLoad(Use->getUser()))
return false;
}
return true;
}
return false;
}
/// Remember where this property is accessed.
void LetPropertiesOpt::collectPropertyAccess(SingleValueInstruction *I,
VarDecl *Property,
bool NonRemovable) {
if (!isConstantLetProperty(Property))
return;
LLVM_DEBUG(llvm::dbgs() << "Collecting property access for property '"
<< *Property << "':\n";
llvm::dbgs() << "The instructions are:\n"; I->dumpInContext());
// Ignore the possibility of duplicate worklist entries. They cannot effect
// the SkipProcessing result, and we don't expect any exponential path
// explosion because none of the instructions have multiple address operands.
SmallVector<SingleValueInstruction *, 8> worklist = {I};
while (!worklist.empty()) {
// Check if there is a store to this property.
auto *projection = worklist.pop_back_val();
for (auto Use : getNonDebugUses(projection)) {
auto *User = Use->getUser();
if (isIncidentalUse(User)) {
continue;
}
if (auto *bai = dyn_cast<BeginAccessInst>(User)) {
worklist.push_back(bai);
continue;
}
if (auto *copyAddr = dyn_cast<CopyAddrInst>(User)) {
if (copyAddr->getDest() != projection ||
!analyzeInitValue(copyAddr, Property)) {
SkipProcessing.insert(Property);
return;
}
continue;
}
if (auto *SI = dyn_cast<StoreInst>(User)) {
// There is a store into this property.
// Analyze the assigned value and check if it is a constant
// statically known initializer.
if (SI->getDest() != projection || !analyzeInitValue(SI, Property)) {
SkipProcessing.insert(Property);
return;
}
continue;
}
// Follow the chain of projections and check if it ends up with a load.
// If this is not the case, it is potentially a store into sub-property
// of a property.
// We cannot handle such cases yet, so bail.
if (!isValidPropertyLoad(User)) {
SkipProcessing.insert(Property);
return;
}
}
}
AccessMap[Property].push_back(I);
// If any property is marked as non-removable, their initialization
// and storage cannot be completely removed. But their constant
// values can still be propagated into their uses whenever possible.
if (NonRemovable)
CannotRemove.insert(Property);
}
void LetPropertiesOpt::run(SILModuleTransform *T) {
// Collect property access information for the whole module.
for (auto &F : *Module) {
// Take into account even those functions that should not be
// optimized, because they may contain access to the let
// properties.
bool NonRemovable = !F.shouldOptimize();
for (auto &BB : F) {
for (auto &I : BB) {
if (auto *REAI = dyn_cast<RefElementAddrInst>(&I))
collectPropertyAccess(REAI, REAI->getField(), NonRemovable);
}
}
}
for (auto &Init: InitMap) {
optimizeLetPropertyAccess(Init.first, Init.second);
}
for (SILFunction *ChangedFn : ChangedFunctions) {
// Program flow is not changed by this pass.
T->invalidateAnalysis(ChangedFn,
SILAnalysis::InvalidationKind::Instructions);
}
}
namespace {
class LetPropertiesOptPass : public SILModuleTransform
{
void run() override {
LetPropertiesOpt(getModule()).run(this);
}
};
} // end anonymous namespace
SILTransform *swift::createLetPropertiesOpt() {
return new LetPropertiesOptPass();
}
|