Skip to content

Improve sil_combiner using subtyping information from CHA #16739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions include/swift/SILOptimizer/Analysis/Analysis.def
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ANALYSIS(BasicCallee)
ANALYSIS(Caller)
ANALYSIS(ClassHierarchy)
ANALYSIS(ClosureScope)
ANALYSIS(ConcreteType)
ANALYSIS(Destructor)
ANALYSIS(Dominance)
ANALYSIS(EpilogueARC)
Expand Down
70 changes: 70 additions & 0 deletions include/swift/SILOptimizer/Analysis/ConcreteTypeAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_CONCRETETYPE_H
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a file header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

#define SWIFT_SILOPTIMIZER_ANALYSIS_CONCRETETYPE_H

#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILValue.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"

namespace swift {

class SILModule;
class NominalTypeDecl;
class ProtocolDecl;

class ConcreteTypeAnalysis : public SILAnalysis {
public:
typedef SmallVector<NominalTypeDecl *, 8> NominalTypeList;
typedef llvm::DenseMap<ProtocolDecl *, NominalTypeDecl *>
ProtocolSoleTypeImplementation;
typedef llvm::DenseMap<ProtocolDecl *, NominalTypeList>
ProtocolImplementations;

ConcreteTypeAnalysis(SILModule *Mod)
: SILAnalysis(AnalysisKind::ConcreteType), M(Mod) {
init();
}

~ConcreteTypeAnalysis();

static bool classof(const SILAnalysis *S) {
return S->getKind() == AnalysisKind::ConcreteType;
}

/// Invalidate all information in this analysis.
virtual void invalidate() override {}

/// Invalidate all of the information for a specific function.
virtual void invalidate(SILFunction *F, InvalidationKind K) override {}

/// Notify the analysis about a newly created function.
virtual void notifyAddFunction(SILFunction *F) override {}

/// Notify the analysis about a function which will be deleted from the
/// module.
virtual void notifyDeleteFunction(SILFunction *F) override {}

/// Notify the analysis about changed witness or vtables.
virtual void invalidateFunctionTables() override {}

/// Get the sole class that implements a protocol.
NominalTypeDecl *getSoleTypeImplementingProtocol(ProtocolDecl *P) {
return ProtocolSoleTypeImplementationCache[P];
}

private:
/// Compute inheritance properties.
void init();

/// The module.
SILModule *M;

/// A cache that maps a protocol to its sole class conforming to it.
ProtocolSoleTypeImplementation ProtocolSoleTypeImplementationCache;
};

} // namespace swift
#endif
5 changes: 5 additions & 0 deletions lib/SILOptimizer/Analysis/Analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/SILOptimizer/Analysis/IVAnalysis.h"
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/ConcreteTypeAnalysis.h"
#include "swift/AST/Module.h"
#include "swift/AST/SILOptions.h"
#include "swift/SIL/SILModule.h"
Expand Down Expand Up @@ -55,3 +56,7 @@ SILAnalysis *swift::createClassHierarchyAnalysis(SILModule *M) {
SILAnalysis *swift::createBasicCalleeAnalysis(SILModule *M) {
return new BasicCalleeAnalysis(M);
}

SILAnalysis *swift::createConcreteTypeAnalysis(SILModule *M) {
return new ConcreteTypeAnalysis(M);
}
1 change: 1 addition & 0 deletions lib/SILOptimizer/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(ANALYSIS_SOURCES
Analysis/ClassHierarchyAnalysis.cpp
Analysis/ClosureScope.cpp
Analysis/ColdBlockInfo.cpp
Analysis/ConcreteTypeAnalysis.cpp
Analysis/DestructorAnalysis.cpp
Analysis/EscapeAnalysis.cpp
Analysis/EpilogueARCAnalysis.cpp
Expand Down
98 changes: 98 additions & 0 deletions lib/SILOptimizer/Analysis/ConcreteTypeAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//===--- ConcreteTypeAnalysis.cpp - Protocol to Class inheritance ---------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Word "Class" should not appear here, and maybe rename this to "ProtocolConfomanceAnalysis"

in general we use "X conforms to Y" instead of "X implements Y"

//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//

#include "swift/SILOptimizer/Analysis/ConcreteTypeAnalysis.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Module.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILValue.h"

using namespace swift;

namespace {
/// A helper class to collect all nominal type declarations.
class NominalTypeWalker : public ASTWalker {
ConcreteTypeAnalysis::ProtocolImplementations &ProtocolImplementationsCache;

public:
NominalTypeWalker(ConcreteTypeAnalysis::ProtocolImplementations
&ProtocolImplementationsCache)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line this up with the (

: ProtocolImplementationsCache(ProtocolImplementationsCache) {}

bool walkToDeclPre(Decl *D) override {
auto *NTD = dyn_cast<NominalTypeDecl>(D);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to write if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) { .. }

if (!NTD || !NTD->hasInterfaceType())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NTD should have an interface type

return true;
auto Protocols = NTD->getAllProtocols();
// We are only interested in types implementing protocols.
if (!Protocols.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the protocols are empty, the loop does nothing, so we can remove this if

for (auto &Protocol : Protocols) {
auto &K = ProtocolImplementationsCache[Protocol];
K.push_back(NTD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine this line with the previous

}
}
return true;
}
};
} // end anonymous namespace

void ConcreteTypeAnalysis::init() {

// We only do this in Whole-Module compilation mode.
if (!(M->isWholeModule()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra parens

return;

// Process all types implementing protocols.
SmallVector<Decl *, 32> Decls;

// Store protocols and thier implementations in a dense map.
ProtocolImplementations ProtocolImplementationsCache;

M->getSwiftModule()->getTopLevelDecls(Decls);

/// This operation is quadratic and should only be performed
/// in whole module compilation.
NominalTypeWalker Walker(ProtocolImplementationsCache);
for (auto *D : Decls) {
D->walk(Walker);
}

for (auto *D : Decls) {
auto ProtoDecl = dyn_cast<ProtocolDecl>(D);
/// Check for protocols that are either internal or lower with whole module
/// compilation enabled.
if (ProtoDecl && ProtoDecl->hasAccess() &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasAccess() will always be true in WMO

ProtocolImplementationsCache.count(ProtoDecl) &&
(ProtoDecl->getEffectiveAccess() <= AccessLevel::Internal)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn this into a negated condition with a 'continue'


/// Make sure one class/enum/struct implements this protocol.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reverse mapping should store all conforming types, not just one

SmallVector<NominalTypeDecl *, 8> ImplementedDeclList =
ProtocolImplementationsCache[ProtoDecl];
/// Bail if more than one type implements the protocol.
if (ImplementedDeclList.size() > 1)
continue;
auto NTD = *(ImplementedDeclList.begin());
/// Check the access level.
if (NTD->getEffectiveAccess() > AccessLevel::Internal)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unnecessary restriction

continue;

/// Check if it is a class/struct/enum.
if (isa<ClassDecl>(NTD) || isa<StructDecl>(NTD) || isa<EnumDecl>(NTD)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary

ProtocolSoleTypeImplementationCache[ProtoDecl] = NTD;
}
}
}
}

ConcreteTypeAnalysis::~ConcreteTypeAnalysis() {}
7 changes: 6 additions & 1 deletion lib/SILOptimizer/SILCombiner/SILCombine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/ConcreteTypeAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Local.h"
Expand Down Expand Up @@ -345,11 +347,14 @@ class SILCombine : public SILFunctionTransform {
void run() override {
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *DA = PM->getAnalysis<DominanceAnalysis>();
auto *CTA = PM->getAnalysis<ConcreteTypeAnalysis>();
auto *CHA = PM->getAnalysis<ClassHierarchyAnalysis>();

// Create a SILBuilder with a tracking list for newly added
// instructions, which we will periodically move to our worklist.
SILBuilder B(*getFunction(), &TrackingList);
SILCombiner Combiner(B, AA, DA, getOptions().RemoveRuntimeAsserts);
SILCombiner Combiner(B, AA, DA, CTA, CHA,
getOptions().RemoveRuntimeAsserts);
bool Changed = Combiner.runOnFunction(*getFunction());
assert(TrackingList.empty() &&
"TrackingList should be fully processed by SILCombiner");
Expand Down
19 changes: 18 additions & 1 deletion lib/SILOptimizer/SILCombiner/SILCombiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "swift/SIL/SILValue.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
#include "swift/SILOptimizer/Analysis/ConcreteTypeAnalysis.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -117,6 +119,10 @@ class SILCombiner :

DominanceAnalysis *DA;

ConcreteTypeAnalysis *CTA;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add comments here explaining why these are used?


ClassHierarchyAnalysis *CHA;

/// Worklist containing all of the instructions primed for simplification.
SILCombineWorklist Worklist;

Expand All @@ -135,10 +141,14 @@ class SILCombiner :
/// Cast optimizer
CastOptimizer CastOpt;

/// Set of ApplySites instrumented by concrete type analysis.
llvm::DenseSet<FullApplySite> VisitedAIForCTA;

public:
SILCombiner(SILBuilder &B, AliasAnalysis *AA, DominanceAnalysis *DA,
ConcreteTypeAnalysis *CTA, ClassHierarchyAnalysis *CHA,
bool removeCondFails)
: AA(AA), DA(DA), Worklist(), MadeChange(false),
: AA(AA), DA(DA), CTA(CTA), CHA(CHA), Worklist(), MadeChange(false),
RemoveCondFails(removeCondFails), Iteration(0), Builder(B),
CastOpt(/* ReplaceInstUsesAction */
[&](SingleValueInstruction *I, ValueBase *V) {
Expand Down Expand Up @@ -295,6 +305,13 @@ class SILCombiner :
WitnessMethodInst *WMI);
SILInstruction *propagateConcreteTypeOfInitExistential(FullApplySite AI);

/// Propagate concrete types from concrete type analysis.
SILInstruction *propagateConcreteTypeFromCTA(FullApplySite AI);
bool propagateConcreteTypeFromCTAInternal(
FullApplySite Apply, ProtocolDecl *Protocol, SILValue &Arg,
SILValue &NewArg, Optional<ProtocolConformanceRef> &ConformanceRef,
ArchetypeType *OpenedArchetype, CanType &ConcreteType);

/// Perform one SILCombine iteration.
bool doOneIteration(SILFunction &F, unsigned Iteration);

Expand Down
Loading