Skip to content

[SILGen] Unified Entry Point Emission #67747

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 8 commits into from
Aug 8, 2023
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
1 change: 1 addition & 0 deletions lib/SILGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ add_swift_host_library(swiftSILGen STATIC
SILGenRequests.cpp
SILGenStmt.cpp
SILGenThunk.cpp
SILGenTopLevel.cpp
SILGenType.cpp)
target_link_libraries(swiftSILGen PRIVATE
swiftSerialization
Expand Down
280 changes: 14 additions & 266 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "RValue.h"
#include "SILGenFunction.h"
#include "SILGenFunctionBuilder.h"
#include "SILGenTopLevel.h"
#include "Scope.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSIL.h"
Expand Down Expand Up @@ -51,7 +52,7 @@ using namespace Lowering;
//===----------------------------------------------------------------------===//

SILGenModule::SILGenModule(SILModule &M, ModuleDecl *SM)
: M(M), Types(M.Types), SwiftModule(SM), TopLevelSGF(nullptr),
: M(M), Types(M.Types), SwiftModule(SM),
FileIDsByFilePath(SM->computeFileIDMap(/*shouldDiagnose=*/true)) {
const SILOptions &Opts = M.getOptions();
if (!Opts.UseProfile.empty()) {
Expand All @@ -70,7 +71,6 @@ SILGenModule::SILGenModule(SILModule &M, ModuleDecl *SM)
}

SILGenModule::~SILGenModule() {
assert(!TopLevelSGF && "active source file lowering!?");

// Update the linkage of external private functions to public_external,
// because there is no private_external linkage. External private functions
Expand Down Expand Up @@ -962,7 +962,6 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
auto *pbd = var->getParentPatternBinding();
unsigned idx = pbd->getPatternEntryIndexForVarDecl(var);
auto *initDC = pbd->getInitContext(idx);
auto captureInfo = pbd->getCaptureInfo(idx);
auto *init = constant.getInitializationExpr();
assert(init);

Expand All @@ -971,17 +970,6 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
PrettyStackTraceSILFunction X("silgen emitStoredPropertyInitialization", f);
f->createProfiler(constant);
SILGenFunction SGF(*this, *f, initDC);

// If this is a stored property initializer inside a type at global scope,
// it may close over a global variable. If we're emitting top-level code,
// then emit a "mark_function_escape" that lists the captured global
// variables so that definite initialization can reason about this
// escape point.
if (!var->getDeclContext()->isLocalContext() && TopLevelSGF &&
TopLevelSGF->B.hasValidInsertionPoint()) {
emitMarkFunctionEscapeForTopLevelCodeGlobals(var, captureInfo);
}

SGF.emitGeneratorFunction(constant, init, /*EmitProfilerIncrement=*/true);
postEmitFunction(constant, f);
break;
Expand Down Expand Up @@ -1107,9 +1095,15 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
case SILDeclRef::Kind::AsyncEntryPoint:
case SILDeclRef::Kind::EntryPoint: {
f->setBare(IsBare);
if (constant.hasFileUnit()) {
auto *File = constant.getFileUnit();
// In script mode
assert(isa<SourceFile>(File) && "Emitting entry-point of non source file?!");
auto *SF = cast<SourceFile>(File);
emitEntryPoint(SF, f);
return;
}

// TODO: Handle main SourceFile emission (currently done by
// SourceFileScope).
auto loc = constant.getAsRegularLocation();
preEmitFunction(constant, f, loc);
auto *decl = constant.getDecl();
Expand Down Expand Up @@ -1363,41 +1357,10 @@ void SILGenModule::emitDifferentiabilityWitness(
vjp);
}

void SILGenModule::
emitMarkFunctionEscapeForTopLevelCodeGlobals(SILLocation loc,
CaptureInfo captureInfo) {
assert(TopLevelSGF && TopLevelSGF->B.hasValidInsertionPoint()
&& "no valid code generator for top-level function?!");

SmallVector<SILValue, 4> Captures;

for (auto capture : captureInfo.getCaptures()) {
// Decls captured by value don't escape.
auto It = TopLevelSGF->VarLocs.find(capture.getDecl());
if (It == TopLevelSGF->VarLocs.end() ||
!It->getSecond().value->getType().isAddress())
continue;

Captures.push_back(It->second.value);
}

if (!Captures.empty())
TopLevelSGF->B.createMarkFunctionEscape(loc, Captures);
}

void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
// Emit default arguments and property wrapper initializers.
emitArgumentGenerators(AFD, AFD->getParameters());

// If this is a function at global scope, it may close over a global variable.
// If we're emitting top-level code, then emit a "mark_function_escape" that
// lists the captured global variables so that definite initialization can
// reason about this escape point.
if (!AFD->getDeclContext()->isLocalContext() &&
TopLevelSGF && TopLevelSGF->B.hasValidInsertionPoint()) {
emitMarkFunctionEscapeForTopLevelCodeGlobals(AFD, AFD->getCaptureInfo());
}

// If the declaration is exported as a C function, emit its native-to-foreign
// thunk too, if it wasn't already forced.
if (AFD->getAttrs().hasAttribute<CDeclAttr>()) {
Expand Down Expand Up @@ -1770,7 +1733,6 @@ void SILGenModule::emitObjCDestructorThunk(DestructorDecl *destructor) {
}

void SILGenModule::visitPatternBindingDecl(PatternBindingDecl *pd) {
assert(!TopLevelSGF && "script mode PBDs should be in TopLevelCodeDecls");
for (auto i : range(pd->getNumPatternEntries()))
if (pd->getExecutableInit(i))
emitGlobalInitialization(pd, i);
Expand Down Expand Up @@ -1964,225 +1926,8 @@ void SILGenModule::visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) {
// Nothing to do for #error/#warning; they've already been emitted.
}

void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) {
assert(TopLevelSGF && "top-level code in a non-main source file!");

if (!TopLevelSGF->B.hasValidInsertionPoint())
return;

TopLevelSGF->emitProfilerIncrement(td->getBody());

DebugScope DS(*TopLevelSGF, CleanupLocation(td));

for (auto &ESD : td->getBody()->getElements()) {
if (!TopLevelSGF->B.hasValidInsertionPoint()) {
if (auto *S = ESD.dyn_cast<Stmt*>()) {
if (S->isImplicit())
continue;
} else if (auto *E = ESD.dyn_cast<Expr*>()) {
if (E->isImplicit())
continue;
}

diagnose(ESD.getStartLoc(), diag::unreachable_code);
// There's no point in trying to emit anything else.
return;
}

if (auto *S = ESD.dyn_cast<Stmt*>()) {
TopLevelSGF->emitStmt(S);
} else if (auto *E = ESD.dyn_cast<Expr*>()) {
TopLevelSGF->emitIgnoredExpr(E);
} else {
TopLevelSGF->visit(ESD.get<Decl*>());
}
}
}

namespace {

/// An RAII class to scope source file codegen.
class SourceFileScope {
SILGenModule &sgm;
SILDeclRef EntryRef;
llvm::Optional<Scope> scope;
bool isAsyncTopLevel = false;
public:
SourceFileScope(SILGenModule &sgm, SourceFile *sf) : sgm(sgm) {
// If this is the script-mode file for the module, create a toplevel.
// TODO: We need to unify emission of the entry point such that we walk
// all of the TopLevelCodeDecls in one shot. This will be needed in order
// to requestify entry point emission.
if (sf->isScriptMode()) {
assert(!sgm.TopLevelSGF && "already emitted toplevel?!");
assert(!sgm.M.lookUpFunction(
sgm.getASTContext().getEntryPointFunctionName()) &&
"already emitted toplevel?!");

auto mainEntryRef = SILDeclRef::getMainFileEntryPoint(sf);
SILFunction * toplevel = sgm.getFunction(mainEntryRef, ForDefinition);
toplevel->setBare(IsBare);
EntryRef = mainEntryRef;

if (sf->isAsyncContext()) {
isAsyncTopLevel = true;
auto asyncEntryRef = SILDeclRef::getAsyncMainFileEntryPoint(sf);
auto *asyncTopLevel = sgm.getFunction(asyncEntryRef, ForDefinition);
SILGenFunction(sgm, *toplevel, sf).emitAsyncMainThreadStart(asyncEntryRef);
toplevel = asyncTopLevel;
EntryRef = asyncEntryRef;
}

toplevel->createProfiler(EntryRef);

sgm.TopLevelSGF = new SILGenFunction(sgm, *toplevel, sf);
sgm.TopLevelSGF->MagicFunctionName = sgm.SwiftModule->getName();
auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation();

sgm.TopLevelSGF->prepareEpilog(llvm::None, true, moduleCleanupLoc);

auto prologueLoc = RegularLocation::getModuleLocation();
prologueLoc.markAsPrologue();
if (sf->isAsyncContext()) {
// emitAsyncMainThreadStart will create argc and argv.
// Just set the main actor as the expected executor; we should
// already be running on it.
SILValue executor = sgm.TopLevelSGF->emitMainExecutor(prologueLoc);
sgm.TopLevelSGF->ExpectedExecutor =
sgm.TopLevelSGF->B.createOptionalSome(
prologueLoc, executor,
SILType::getOptionalType(executor->getType()));
} else {
// Create the argc and argv arguments.
auto entry = sgm.TopLevelSGF->B.getInsertionBB();
auto context = sgm.TopLevelSGF->getTypeExpansionContext();
auto paramTypeIter = sgm.TopLevelSGF->F.getConventions()
.getParameterSILTypes(context)
.begin();

entry->createFunctionArgument(*paramTypeIter);
entry->createFunctionArgument(*std::next(paramTypeIter));
}
scope.emplace(sgm.TopLevelSGF->Cleanups, moduleCleanupLoc);
}
}

~SourceFileScope() {
if (sgm.TopLevelSGF) {
scope.reset();

// Unregister the top-level function emitter.
auto &SGF = *sgm.TopLevelSGF;
sgm.TopLevelSGF = nullptr;

// Write out the epilog.
auto moduleLoc = RegularLocation::getModuleLocation();
moduleLoc.markAutoGenerated();
auto returnInfo = SGF.emitEpilogBB(moduleLoc);
auto returnLoc = returnInfo.second;
returnLoc.markAutoGenerated();

SILFunction *exitFunc = nullptr;

SILType returnType;
if (isAsyncTopLevel) {
FuncDecl *exitFuncDecl = sgm.getExit();
assert(exitFuncDecl && "Failed to find exit function declaration");
exitFunc = sgm.getFunction(
SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/true),
NotForDefinition);
SILFunctionType & funcType = *exitFunc->getLoweredType().getAs<SILFunctionType>();
returnType = SILType::getPrimitiveObjectType(
funcType.getParameters().front().getInterfaceType());
} else {
returnType = SGF.F.getConventions().getSingleSILResultType(
SGF.getTypeExpansionContext());
}

auto emitTopLevelReturnValue = [&](unsigned value) -> SILValue {
// Create an integer literal for the value.
auto litType = SILType::getBuiltinIntegerType(32, sgm.getASTContext());
SILValue retValue =
SGF.B.createIntegerLiteral(moduleLoc, litType, value);

// Wrap that in a struct if necessary.
if (litType != returnType) {
retValue = SGF.B.createStruct(moduleLoc, returnType, retValue);
}
return retValue;
};

// Fallthrough should signal a normal exit by returning 0.
SILValue returnValue;
if (SGF.B.hasValidInsertionPoint())
returnValue = emitTopLevelReturnValue(0);

// Handle the implicit rethrow block.
auto rethrowBB = SGF.ThrowDest.getBlock();
SGF.ThrowDest = JumpDest::invalid();

// If the rethrow block wasn't actually used, just remove it.
if (rethrowBB->pred_empty()) {
SGF.eraseBasicBlock(rethrowBB);

// Otherwise, we need to produce a unified return block.
} else {
auto returnBB = SGF.createBasicBlock();
if (SGF.B.hasValidInsertionPoint())
SGF.B.createBranch(returnLoc, returnBB, returnValue);
returnValue =
returnBB->createPhiArgument(returnType, OwnershipKind::Owned);
SGF.B.emitBlock(returnBB);

// Emit the rethrow block.
SILGenSavedInsertionPoint savedIP(SGF, rethrowBB,
FunctionSection::Postmatter);

// Log the error.
SILValue error = rethrowBB->getArgument(0);
SGF.B.createBuiltin(moduleLoc,
sgm.getASTContext().getIdentifier("errorInMain"),
sgm.Types.getEmptyTupleType(), {}, {error});

// Then end the lifetime of the error.
//
// We do this to appease the ownership verifier. We do not care about
// actually destroying the value since we are going to immediately exit,
// so this saves us a slight bit of code-size since end_lifetime is
// stripped out after ownership is removed.
SGF.B.createEndLifetime(moduleLoc, error);

// Signal an abnormal exit by returning 1.
SGF.Cleanups.emitCleanupsForReturn(CleanupLocation(moduleLoc),
IsForUnwind);
SGF.B.createBranch(returnLoc, returnBB, emitTopLevelReturnValue(1));
}

// Return.
if (SGF.B.hasValidInsertionPoint()) {

if (isAsyncTopLevel) {
SILValue exitCall = SGF.B.createFunctionRef(moduleLoc, exitFunc);
SGF.B.createApply(moduleLoc, exitCall, {}, {returnValue});
SGF.B.createUnreachable(moduleLoc);
} else {
SGF.B.createReturn(returnLoc, returnValue);
}
}

// Okay, we're done emitting the top-level function; destroy the
// emitter and verify the result.
SILFunction *toplevel = &SGF.getFunction();
delete &SGF;

LLVM_DEBUG(llvm::dbgs() << "lowered toplevel sil:\n";
toplevel->print(llvm::dbgs()));
toplevel->verifyIncompleteOSSA();
sgm.emitLazyConformancesForFunction(toplevel);
}
}
};

// An RAII object that constructs a \c SILGenModule instance.
// On destruction, delayed definitions are automatically emitted.
class SILGenModuleRAII {
Expand All @@ -2193,7 +1938,10 @@ class SILGenModuleRAII {
// Type-check the file if we haven't already.
performTypeChecking(*sf);

SourceFileScope scope(SGM, sf);
if (sf->isScriptMode()) {
SGM.emitEntryPoint(sf);
}

for (auto *D : sf->getTopLevelDecls()) {
// Emit auxiliary decls.
D->visitAuxiliaryDecls([&](Decl *auxiliaryDecl) {
Expand Down
Loading