Files
swift-mirror/lib/SIL/IR/Linker.h
Erik Eckstein 4d2fe9690a SIL linker: delay function verification to end of linking
The SIL linker de-serializes functions. Immediately after de-serialization some `[serialized]` flags of referenced functions may not be set correctly, yet.
This is fixed by the linker. But it also means that the SIL is only valid after the linker has finished processing all functions.

Fixes a SIL verifier error.
2023-12-01 16:09:30 +01:00

162 lines
5.7 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;
llvm::SmallVector<SILFunction *, 32> toVerify;
/// 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);
void visitGlobalAddrInst(GlobalAddrInst *i);
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 ||
Mod.getASTContext().LangOpts.hasFeature(Feature::Embedded);
}
void linkInVTable(ClassDecl *D);
// Main loop of the visitor. Called by one of the other *visit* methods.
void process();
};
} // end namespace swift
#endif