Skip to content

[NFC] SILGlobalVariable utilities. #16870

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

Merged
merged 4 commits into from
Jun 26, 2018
Merged
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
6 changes: 1 addition & 5 deletions include/swift/Parse/ParseSILSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ namespace swift {
Parser &P;
public:
explicit PrettyStackTraceParser(Parser &P) : P(P) {}
void print(llvm::raw_ostream &out) const override {
out << "With parser at source location: ";
P.Tok.getLoc().print(out, P.Context.SourceMgr);
out << '\n';
}
void print(llvm::raw_ostream &out) const override;
};
} // end namespace swift

Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/MemAccessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class AccessedStorage {
}
}

/// Return true if the storage is guaranteed local.
bool isLocal() const {
switch (getKind()) {
case Box:
Expand Down
34 changes: 34 additions & 0 deletions include/swift/SIL/SILGlobalVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,38 @@ public ilist_default_traits<::swift::SILGlobalVariable> {

} // end llvm namespace

//===----------------------------------------------------------------------===//
// Utilities for verification and optimization.
//===----------------------------------------------------------------------===//

namespace swift {

/// Given an addressor, AddrF, return the global variable being addressed, or
/// return nullptr if the addressor isn't a recognized pattern.
SILGlobalVariable *getVariableOfGlobalInit(SILFunction *AddrF);

/// Return the callee of a once call.
SILFunction *getCalleeOfOnceCall(BuiltinInst *BI);

/// Helper for getVariableOfGlobalInit(), so GlobalOpts can deeply inspect and
/// rewrite the initialization pattern.
///
/// Given an addressor, AddrF, find the call to the global initializer if
/// present, otherwise return null. If an initializer is returned, then
/// `CallsToOnce` is initialized to the corresponding builtin "once" call.
SILFunction *findInitializer(SILModule *Module, SILFunction *AddrF,
BuiltinInst *&CallToOnce);

/// Helper for getVariableOfGlobalInit(), so GlobalOpts can deeply inspect and
/// rewrite the initialization pattern.
///
/// Given a global initializer, InitFunc, return the GlobalVariable that it
/// statically initializes or return nullptr if it isn't an obvious static
/// initializer. If a global variable is returned, InitVal is initialized to the
/// the instruction producing the global's initial value.
SILGlobalVariable *getVariableOfStaticInitializer(
SILFunction *InitFunc, SingleValueInstruction *&InitVal);

} // namespace swift

#endif
4 changes: 4 additions & 0 deletions include/swift/SIL/SILLinkage.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace swift {

class ValueDecl;

/// Linkage for a SIL object. This concept combines the notions
/// of symbol linkage and visibility.
///
Expand Down Expand Up @@ -176,6 +178,8 @@ inline bool isPossiblyUsedExternally(SILLinkage linkage, bool wholeModule) {
return linkage <= SILLinkage::Hidden;
}

SILLinkage getDeclSILLinkage(const ValueDecl *decl);

inline bool hasPublicVisibility(SILLinkage linkage) {
switch (linkage) {
case SILLinkage::Public:
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,14 @@ class SILModule {
void setOptRecordStream(std::unique_ptr<llvm::yaml::Output> &&Stream,
std::unique_ptr<llvm::raw_ostream> &&RawStream);

// This is currently limited to VarDecl because the visibility of global
// variables and class properties is straightforward, while the visibility of
// class methods (ValueDecls) depends on the subclass scope. "Visiblity" has
// a different meaning when vtable layout is at stake.
bool isVisibleExternally(const VarDecl *decl) {
return isPossiblyUsedExternally(getDeclSILLinkage(decl), isWholeModule());
}

PropertyListType &getPropertyList() { return properties; }
const PropertyListType &getPropertyList() const { return properties; }

Expand Down
73 changes: 66 additions & 7 deletions lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Timer.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/Parser.h"
#include "swift/Parse/ParseSILSupport.h"
#include "swift/Parse/Parser.h"
#include "swift/SIL/AbstractionPattern.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILArgument.h"
Expand All @@ -28,8 +29,8 @@
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SIL/TypeLowering.h"
#include "swift/Syntax/SyntaxKind.h"
#include "swift/Subsystems.h"
#include "swift/Syntax/SyntaxKind.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/SaveAndRestore.h"

Expand Down Expand Up @@ -99,6 +100,12 @@ SILParserState::SILParserState(SILModule *M)

SILParserState::~SILParserState() = default;

void PrettyStackTraceParser::print(llvm::raw_ostream &out) const {
out << "With parser at source location: ";
P.Tok.getLoc().print(out, P.Context.SourceMgr);
out << '\n';
}

bool swift::parseIntoSourceFile(SourceFile &SF,
unsigned BufferID,
bool *Done,
Expand Down Expand Up @@ -5372,6 +5379,53 @@ bool SILParserTUState::parseDeclSILStage(Parser &P) {
return false;
}

/// Lookup a global variable declaration from its demangled name.
///
/// A variable declaration exists for all sil_global variables defined in
/// Swift. A Swift global defined outside this module will be exposed
/// via an addressor rather than as a sil_global. Globals imported
/// from clang will produce a sil_global but will not have any corresponding
/// VarDecl.
///
/// FIXME: lookupGlobalDecl() can handle collisions between private or
/// fileprivate global variables in the same SIL Module, but the typechecker
/// will still incorrectly diagnose this as an "invalid redeclaration" and give
/// all but the first declaration an error type.
static Optional<VarDecl *> lookupGlobalDecl(Identifier GlobalName,
SILLinkage GlobalLinkage,
SILType GlobalType, Parser &P) {
// Create a set of DemangleOptions to produce the global variable's
// identifier, which is used as a search key in the declaration context.
Demangle::DemangleOptions demangleOpts;
demangleOpts.QualifyEntities = false;
demangleOpts.ShowPrivateDiscriminators = false;
demangleOpts.DisplayEntityTypes = false;
std::string GlobalDeclName = Demangle::demangleSymbolAsString(
GlobalName.str(), demangleOpts);

SmallVector<ValueDecl *, 4> CurModuleResults;
P.SF.getParentModule()->lookupValue(
{}, P.Context.getIdentifier(GlobalDeclName), NLKind::UnqualifiedLookup,
CurModuleResults);
// Bail-out on clang-imported globals.
if (CurModuleResults.empty())
return nullptr;

// private and fileprivate globals of the same name may be merged into a
// single SIL module. Find the declaration with the correct type and
// linkage. (If multiple globals have the same type and linkage then it
// doesn't matter which declaration we use).
for (ValueDecl *ValDecl : CurModuleResults) {
auto *VD = cast<VarDecl>(ValDecl);
CanType DeclTy = VD->getType()->getCanonicalType();
if (DeclTy == GlobalType.getASTType()
&& getDeclSILLinkage(VD) == GlobalLinkage) {
return VD;
}
}
return None;
}

/// decl-sil-global: [[only in SIL mode]]
/// 'sil_global' sil-linkage @name : sil-type [external]
bool SILParserTUState::parseSILGlobal(Parser &P) {
Expand Down Expand Up @@ -5404,11 +5458,16 @@ bool SILParserTUState::parseSILGlobal(Parser &P) {
if (!GlobalLinkage.hasValue())
GlobalLinkage = SILLinkage::DefaultForDefinition;

// FIXME: check for existing global variable?
auto *GV = SILGlobalVariable::create(M, GlobalLinkage.getValue(),
isSerialized,
GlobalName.str(),GlobalType,
RegularLocation(NameLoc));
// Lookup the global variable declaration for this sil_global.
auto VD =
lookupGlobalDecl(GlobalName, GlobalLinkage.getValue(), GlobalType, P);
if (!VD) {
P.diagnose(NameLoc, diag::sil_global_variable_not_found, GlobalName);
return true;
}
auto *GV = SILGlobalVariable::create(
M, GlobalLinkage.getValue(), isSerialized, GlobalName.str(), GlobalType,
RegularLocation(NameLoc), VD.getValue());

GV->setLet(isLet);
// Parse static initializer if exists.
Expand Down
131 changes: 131 additions & 0 deletions lib/SIL/SILGlobalVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILGlobalVariable.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLinkage.h"
#include "swift/SIL/SILModule.h"

Expand Down Expand Up @@ -137,3 +139,132 @@ ClangNode SILGlobalVariable::getClangNode() const {
const clang::Decl *SILGlobalVariable::getClangDecl() const {
return (VDecl ? VDecl->getClangDecl() : nullptr);
}

//===----------------------------------------------------------------------===//
// Utilities for verification and optimization.
//===----------------------------------------------------------------------===//

static SILGlobalVariable *getStaticallyInitializedVariable(SILFunction *AddrF) {
if (AddrF->isExternalDeclaration())
return nullptr;

auto *RetInst = cast<ReturnInst>(AddrF->findReturnBB()->getTerminator());
auto *API = dyn_cast<AddressToPointerInst>(RetInst->getOperand());
if (!API)
return nullptr;
auto *GAI = dyn_cast<GlobalAddrInst>(API->getOperand());
if (!GAI)
return nullptr;

return GAI->getReferencedGlobal();
}

SILGlobalVariable *swift::getVariableOfGlobalInit(SILFunction *AddrF) {
if (!AddrF->isGlobalInit())
return nullptr;

if (auto *SILG = getStaticallyInitializedVariable(AddrF))
return SILG;

// If the addressor contains a single "once" call, it calls globalinit_func,
// and the globalinit_func is called by "once" from a single location,
// continue; otherwise bail.
BuiltinInst *CallToOnce;
auto *InitF = findInitializer(&AddrF->getModule(), AddrF, CallToOnce);

if (!InitF || !InitF->getName().startswith("globalinit_"))
return nullptr;

// If the globalinit_func is trivial, continue; otherwise bail.
SingleValueInstruction *dummyInitVal;
auto *SILG = getVariableOfStaticInitializer(InitF, dummyInitVal);

return SILG;
}

SILFunction *swift::getCalleeOfOnceCall(BuiltinInst *BI) {
assert(BI->getNumOperands() == 2 && "once call should have 2 operands.");

Choose a reason for hiding this comment

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

Can you please assert based on getBuiltinInfo().ID of the BuiltinInst ? cleaner and safer assertion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do. This is all super old code that I just move out of SILOptimizer so it can be used to verify well-formed SIL.


auto Callee = BI->getOperand(1);
assert(Callee->getType().castTo<SILFunctionType>()->getRepresentation()
== SILFunctionTypeRepresentation::CFunctionPointer &&
"Expected C function representation!");

if (auto *FR = dyn_cast<FunctionRefInst>(Callee))
return FR->getReferencedFunction();

return nullptr;
}

// Find the globalinit_func by analyzing the body of the addressor.
SILFunction *swift::findInitializer(SILModule *Module, SILFunction *AddrF,
BuiltinInst *&CallToOnce) {
// We only handle a single SILBasicBlock for now.
if (AddrF->size() != 1)
return nullptr;

CallToOnce = nullptr;
SILBasicBlock *BB = &AddrF->front();
for (auto &I : *BB) {
// Find the builtin "once" call.
if (auto *BI = dyn_cast<BuiltinInst>(&I)) {
const BuiltinInfo &Builtin =
BI->getModule().getBuiltinInfo(BI->getName());
if (Builtin.ID != BuiltinValueKind::Once)
continue;

// Bail if we have multiple "once" calls in the addressor.
if (CallToOnce)
return nullptr;

CallToOnce = BI;
}
}
if (!CallToOnce)
return nullptr;
return getCalleeOfOnceCall(CallToOnce);
}

SILGlobalVariable *
swift::getVariableOfStaticInitializer(SILFunction *InitFunc,
SingleValueInstruction *&InitVal) {
InitVal = nullptr;
SILGlobalVariable *GVar = nullptr;
// We only handle a single SILBasicBlock for now.

Choose a reason for hiding this comment

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

I see this comment and this check repeated here and in findInitializer, maybe we can outline it to a single static bool function?

if (InitFunc->size() != 1)
return nullptr;

SILBasicBlock *BB = &InitFunc->front();
GlobalAddrInst *SGA = nullptr;
bool HasStore = false;
for (auto &I : *BB) {
// Make sure we have a single GlobalAddrInst and a single StoreInst.
// And the StoreInst writes to the GlobalAddrInst.
if (isa<AllocGlobalInst>(&I) || isa<ReturnInst>(&I)
|| isa<DebugValueInst>(&I)) {
continue;
} else if (auto *sga = dyn_cast<GlobalAddrInst>(&I)) {
if (SGA)
return nullptr;
SGA = sga;
GVar = SGA->getReferencedGlobal();
} else if (auto *SI = dyn_cast<StoreInst>(&I)) {
if (HasStore || SI->getDest() != SGA)
return nullptr;
HasStore = true;
SILValue value = SI->getSrc();

// We only handle StructInst and TupleInst being stored to a
// global variable for now.
if (!isa<StructInst>(value) && !isa<TupleInst>(value))
return nullptr;
InitVal = cast<SingleValueInstruction>(value);
} else if (!SILGlobalVariable::isValidStaticInitializerInst(&I,
I.getModule())) {
return nullptr;
}
}
if (!InitVal)
return nullptr;
return GVar;
}
19 changes: 19 additions & 0 deletions lib/SIL/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -855,3 +855,22 @@ SILProperty *SILProperty::create(SILModule &M,
return prop;
}

// Definition from SILLinkage.h.
SILLinkage swift::getDeclSILLinkage(const ValueDecl *decl) {
AccessLevel access = decl->getEffectiveAccess();
SILLinkage linkage;
switch (access) {
case AccessLevel::Private:
case AccessLevel::FilePrivate:
linkage = SILLinkage::Private;
break;
case AccessLevel::Internal:
linkage = SILLinkage::Hidden;
break;
case AccessLevel::Public:
case AccessLevel::Open:
linkage = SILLinkage::Public;
break;
}
return linkage;
}
11 changes: 8 additions & 3 deletions lib/SILOptimizer/IPO/DeadFunctionElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ class FunctionLivenessComputation {
}

/// Retrieve the visibility information from the AST.
///
/// This differs from SILModule::isVisibleExternally(VarDecl *) because of
/// it's handling of class methods. It returns true for methods whose
/// declarations are not directly visible externally, but have been imported
/// from another module. This ensures that entries aren't deleted from vtables
/// imported from the stdlib.
/// FIXME: Passes should not embed special logic for handling linkage.
bool isVisibleExternally(const ValueDecl *decl) {
AccessLevel access = decl->getEffectiveAccess();
SILLinkage linkage;
Expand All @@ -390,9 +397,7 @@ class FunctionLivenessComputation {
if (isPossiblyUsedExternally(linkage, Module->isWholeModule()))
return true;

// If a vtable or witness table (method) is only visible in another module
// it can be accessed inside that module and we don't see this access.
// We hit this case e.g. if a table is imported from the stdlib.
// Special case for vtable visibility.
if (decl->getDeclContext()->getParentModule() != Module->getSwiftModule())
return true;

Expand Down
Loading