Files
swift-mirror/lib/SIL/IR/Linker.h
Erik Eckstein 6a020f8f15 Stabilize and simplify SIL linkage and serialization
The main point of this change is to make sure that a shared function always has a body: both, in the optimizer pipeline and in the swiftmodule file.
This is important because the compiler always needs to emit code for a shared function. Shared functions cannot be referenced from outside the module.
In several corner cases we missed to maintain this invariant which resulted in unresolved-symbol linker errors.

As side-effect of this change we can drop the shared_external SIL linkage and the IsSerializable flag, which simplifies the serialization and linkage concept.
2022-03-09 15:28:05 +01:00

156 lines
5.5 KiB
C++

//===--- Linker.h -----------------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_LINKER_H
#define SWIFT_SIL_LINKER_H
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/SILModule.h"
#include <functional>
namespace swift {
/// Visits the call graph and makes sure that all required functions (according
/// to the linking `Mode`) are de-serialized and the `isSerialized` flag is set
/// correctly.
///
/// If the Mode is LinkNormal, just de-serialize the bare minimum of what's
/// required for a non-optimized compilation. That are all referenced shared
/// functions, because shared functions must have a body.
///
/// If the Mode is LinkAll, de-serialize the maximum amount of functions - for
/// optimized compilation.
///
/// Also make sure that shared functions which are referenced from "IsSerialized"
/// functions also have the "IsSerialized" flag set.
/// Usually this already is enforced by SILGen and all passes which create new
/// functions.
/// Only in case a de-serialized function references a shared function which
/// already exists in the module as "IsNotSerialized", it's required to explicitly
/// set the "IsSerialized" flag.
///
/// Example:
///
/// Before de-serialization:
/// \code
/// sil @foo {
/// function_ref @publicFuncInOtherModule
/// function_ref @specializedFunction
/// }
/// sil shared @specializedFunction { // IsNotSerialized
/// function_ref @otherSpecializedFunction
/// }
/// sil shared @otherSpecializedFunction { // IsNotSerialized
/// }
/// \endcode
///
/// After de-serialization:
/// \code
/// sil @foo {
/// function_ref @publicFuncInOtherModule
/// function_ref @specializedFunction
/// }
/// sil public_external [serialized] @publicFuncInOtherModule {
/// function_ref @specializedFunction
/// }
/// // Need to be changed to "IsSerialized"
/// sil shared [serialized] @specializedFunction {
/// function_ref @otherSpecializedFunction
/// }
/// sil shared [serialized] @otherSpecializedFunction {
/// }
/// \endcode
///
class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, void> {
using LinkingMode = SILModule::LinkingMode;
/// The SILModule that we are loading from.
SILModule &Mod;
/// Break cycles visiting recursive protocol conformances.
llvm::DenseSet<ProtocolConformance *> VisitedConformances;
/// Worklist of SILFunctions we are processing.
llvm::SmallVector<SILFunction *, 128> Worklist;
/// The current linking mode.
LinkingMode Mode;
/// Whether any functions were deserialized.
bool Changed;
public:
SILLinkerVisitor(SILModule &M, SILModule::LinkingMode LinkingMode)
: Mod(M), Worklist(), Mode(LinkingMode), Changed(false) {}
/// Process F, recursively deserializing any thing F may reference.
/// Returns true if any deserialization was performed.
bool processFunction(SILFunction *F);
/// Process the witnesstable of \p conformanceRef.
/// Returns true if any deserialization was performed.
bool processConformance(ProtocolConformanceRef conformanceRef);
/// Deserialize the VTable mapped to C if it exists and all SIL the VTable
/// transitively references.
///
/// This method assumes that the caller made sure that no vtable existed in
/// Mod.
SILVTable *processClassDecl(const ClassDecl *C);
/// We do not want to visit callee functions if we just have a value base.
void visitSILInstruction(SILInstruction *I) { }
void visitApplyInst(ApplyInst *AI);
void visitTryApplyInst(TryApplyInst *TAI);
void visitPartialApplyInst(PartialApplyInst *PAI);
void visitFunctionRefInst(FunctionRefInst *FRI);
void visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI);
void visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI);
void visitProtocolConformance(ProtocolConformanceRef C);
void visitApplySubstitutions(SubstitutionMap subs);
void visitWitnessMethodInst(WitnessMethodInst *WMI) {
visitProtocolConformance(WMI->getConformance());
}
void visitInitExistentialAddrInst(InitExistentialAddrInst *IEI);
void visitInitExistentialRefInst(InitExistentialRefInst *IERI);
void visitAllocRefInst(AllocRefInst *ARI);
void visitAllocRefDynamicInst(AllocRefDynamicInst *ARI);
void visitMetatypeInst(MetatypeInst *MI);
private:
/// Cause a function to be deserialized, and visit all other functions
/// referenced from this function according to the linking mode.
void deserializeAndPushToWorklist(SILFunction *F);
/// Consider a function for deserialization if the current linking mode
/// requires it.
///
/// If `setToSerializable` is true than all shared functions which are referenced
/// from `F` are set to
void maybeAddFunctionToWorklist(SILFunction *F, bool setToSerializable);
/// Is the current mode link all? Link all implies we should try and link
/// everything, not just transparent/shared functions.
bool isLinkAll() const { return Mode == LinkingMode::LinkAll; }
void linkInVTable(ClassDecl *D);
// Main loop of the visitor. Called by one of the other *visit* methods.
void process();
};
} // end namespace swift
#endif