Files
swift-mirror/include/swift/SIL/Notifications.h
Michael Gottesman 872bf40e17 [sil-optimizer] Centralize how we send out serialization notifications.
Previously SILModule contained two different pathways for the deserializer to
send notifications that it had created functions:

1. A list of function pointers that were called when a function's body was
deserialized. This was added recently so that access enforcement elimination is
run on newly deserialized SIL code if we have already eliminated access
enforcement from the module.

2. SILModule::SerializationCallback. This is an implementation of the full
callback interface and is used by the SILModule to update linkage and other
sorts of book keeping.

To fix the pass manager notification infrastructure, I need to be able to send
notifications to a SILPassManager when deserializing. I also need to be able to
eliminate these callbacks when a SILPassManager is destroyed. These requirements
are incompatible with the current two implementations since: (2) is an
implementation detail of SILModule and (1) only notifies on function bodies
being deserialized instead of the creation of new declarations (what the caller
analysis wants).

Rather than adding a third group of callbacks, this commit refactors the
infrastructure in such a way that all of these use cases can use one
implementation. This is done by:

1. Lifting the interface of SerializedSILLoader::Callback into a base
notification protocol for deserialization called
DeserializationNotificationHandlerBase and its base no-op implementation into an
implementation of the aforementioned protocol:
DeserializationNotificationHandler.

2. Changing SILModule::SerializationCallback to implement
DeserializationNotificationHandler.

3. Creating a class called FunctionBodyDeserializationNotificationHandler that
takes in a function pointer and uses that to just override the
didDeserializeFunctionBody. This eliminates the need for the specific function
body deserialization list.

4. Replacing the state associated with the two other pathways with a single
DeserializationNotificationHandlerSet class that contains a set of
DeserializationNotificationHandler and chains notifications to them. This set
implements DeserializationNotificationHandlerBase so we know that its
implementation will always be in sync with DeserializationNotificationHandler.

rdar://42301529
2018-08-15 15:49:15 -07:00

266 lines
10 KiB
C++

//===--- Notifications.h - SIL Undef Value Representation -------*- 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_NOTIFICATIONS_H
#define SWIFT_SIL_NOTIFICATIONS_H
#include "swift/Basic/LLVM.h"
#include "swift/Basic/STLExtras.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include <memory>
namespace swift {
class SILNode;
class ModuleDecl;
class SILFunction;
class SILWitnessTable;
class SILDefaultWitnessTable;
class SILGlobalVariable;
class SILVTable;
/// An abstract class for handling SIL deserialization notifications.
///
/// This is an interface that should be implemented by clients that wish to
/// maintain a list of notification handlers. In contrast, handler
/// implementations should instead subclass DeserializationNotificationHandler
/// so that default no-op implementations can be inherited and so that it can be
/// passed into DeserializationNotificationHandlerSet.
class DeserializationNotificationHandlerBase {
public:
/// Observe that we deserialized a function declaration.
virtual void didDeserialize(ModuleDecl *mod, SILFunction *fn) = 0;
/// Observe that we successfully deserialized a function body.
virtual void didDeserializeFunctionBody(ModuleDecl *mod, SILFunction *fn) = 0;
/// Observe that we successfully deserialized a witness table's entries.
virtual void didDeserializeWitnessTableEntries(ModuleDecl *mod,
SILWitnessTable *wt) = 0;
/// Observe that we successfully deserialized a default witness table's
/// entries.
virtual void
didDeserializeDefaultWitnessTableEntries(ModuleDecl *mod,
SILDefaultWitnessTable *wt) = 0;
/// Observe that we deserialized a global variable declaration.
virtual void didDeserialize(ModuleDecl *mod, SILGlobalVariable *var) = 0;
/// Observe that we deserialized a v-table declaration.
virtual void didDeserialize(ModuleDecl *mod, SILVTable *vtable) = 0;
/// Observe that we deserialized a witness-table declaration.
virtual void didDeserialize(ModuleDecl *mod, SILWitnessTable *wtable) = 0;
/// Observe that we deserialized a default witness-table declaration.
virtual void didDeserialize(ModuleDecl *mod,
SILDefaultWitnessTable *wtable) = 0;
virtual ~DeserializationNotificationHandlerBase() = default;
};
/// A no-op implementation of DeserializationNotificationHandlerBase. Intended
/// to allow for users to implement only one of the relevant methods and have
/// all other methods be no-ops.
class DeserializationNotificationHandler
: public DeserializationNotificationHandlerBase {
public:
/// Observe that we deserialized a function declaration.
virtual void didDeserialize(ModuleDecl *mod, SILFunction *fn) override {}
/// Observe that we successfully deserialized a function body.
virtual void didDeserializeFunctionBody(ModuleDecl *mod,
SILFunction *fn) override {}
/// Observe that we successfully deserialized a witness table's entries.
virtual void didDeserializeWitnessTableEntries(ModuleDecl *mod,
SILWitnessTable *wt) override {
}
/// Observe that we successfully deserialized a default witness table's
/// entries.
virtual void didDeserializeDefaultWitnessTableEntries(
ModuleDecl *mod, SILDefaultWitnessTable *wt) override {}
/// Observe that we deserialized a global variable declaration.
virtual void didDeserialize(ModuleDecl *mod,
SILGlobalVariable *var) override {}
/// Observe that we deserialized a v-table declaration.
virtual void didDeserialize(ModuleDecl *mod, SILVTable *vtable) override {}
/// Observe that we deserialized a witness-table declaration.
virtual void didDeserialize(ModuleDecl *mod,
SILWitnessTable *wtable) override {}
/// Observe that we deserialized a default witness-table declaration.
virtual void didDeserialize(ModuleDecl *mod,
SILDefaultWitnessTable *wtable) override {}
virtual StringRef getName() const = 0;
virtual ~DeserializationNotificationHandler() = default;
};
/// A notification handler that only overrides didDeserializeFunctionBody and
/// calls the passed in function pointer.
class FunctionBodyDeserializationNotificationHandler final
: public DeserializationNotificationHandler {
public:
using Handler = void (*)(ModuleDecl *, SILFunction *);
private:
Handler handler;
public:
FunctionBodyDeserializationNotificationHandler(Handler handler)
: handler(handler) {}
virtual ~FunctionBodyDeserializationNotificationHandler() {}
void didDeserializeFunctionBody(ModuleDecl *mod, SILFunction *fn) override {
(*handler)(mod, fn);
}
StringRef getName() const override {
return "FunctionBodyDeserializationNotificationHandler";
}
};
/// A type that contains a set of unique DeserializationNotificationHandlers and
/// implements DeserializationNotificationHandlerBase by iterating over the
/// stored handlers and calling each handler's implementation.
class DeserializationNotificationHandlerSet final
: public DeserializationNotificationHandlerBase {
public:
using NotificationUniquePtr =
std::unique_ptr<DeserializationNotificationHandler>;
private:
/// A list of deserialization callbacks that update the SILModule and other
/// parts of SIL as deserialization occurs.
///
/// We use 3 here since that is the most that will ever be used today in the
/// compiler. If that changed, that number should be changed as well. The
/// specific users are:
///
/// 1. SILModule's serialization callback.
/// 2. SILPassManager notifications.
/// 3. Access Enforcement Stripping notification.
SmallVector<NotificationUniquePtr, 3> handlerSet;
public:
DeserializationNotificationHandlerSet() = default;
~DeserializationNotificationHandlerSet() = default;
bool erase(DeserializationNotificationHandler *handler) {
auto iter = find_if(handlerSet, [&](const NotificationUniquePtr &h) {
return handler == h.get();
});
if (iter == handlerSet.end())
return false;
handlerSet.erase(iter);
return true;
}
void add(NotificationUniquePtr &&handler) {
// Since we store unique_ptrs and are accepting a movable rvalue here, we
// should never have a case where we have a notification added twice. But
// just to be careful, lets use an assert.
assert(!count_if(handlerSet, [&](const NotificationUniquePtr &h) {
return handler.get() == h.get();
}) && "Two unique ptrs pointing at the same memory?!");
handlerSet.emplace_back(std::move(handler));
}
static DeserializationNotificationHandler *
getUnderlyingHandler(const NotificationUniquePtr &h) {
return h.get();
}
/// An iterator into the notification set that returns a bare
/// 'DeserializationNotificationHandler *' projected from one of the
/// underlying std::unique_ptr<DeserializationNotificationHandler>.
using iterator = llvm::mapped_iterator<
decltype(handlerSet)::const_iterator,
decltype(&DeserializationNotificationHandlerSet::getUnderlyingHandler)>;
using range = llvm::iterator_range<iterator>;
/// Returns an iterator to the first element of the handler set.
///
/// NOTE: This iterator has a value_type of
/// 'DeserializationNotificationHandler'.
iterator begin() const {
auto *fptr = &DeserializationNotificationHandlerSet::getUnderlyingHandler;
return llvm::map_iterator(handlerSet.begin(), fptr);
}
/// Returns an iterator to the end of the handler set.
///
/// NOTE: This iterator has a value_type of
/// 'DeserializationNotificationHandler'.
iterator end() const {
auto *fptr = &DeserializationNotificationHandlerSet::getUnderlyingHandler;
return llvm::map_iterator(handlerSet.end(), fptr);
}
/// Returns a range that iterates over bare pointers projected from the
/// internal owned memory pointers in the handlerSet.
///
/// NOTE: The underlying iterator value_type here is
/// 'DeserializationNotificationHandler *'.
auto getRange() const -> range { return llvm::make_range(begin(), end()); }
//===--------------------------------------------------------------------===//
// DeserializationNotificationHandler implementation via chaining to the
// handlers we contain.
//===--------------------------------------------------------------------===//
void didDeserialize(ModuleDecl *mod, SILFunction *fn) override;
void didDeserializeFunctionBody(ModuleDecl *mod, SILFunction *fn) override;
void didDeserializeWitnessTableEntries(ModuleDecl *mod,
SILWitnessTable *wt) override;
void
didDeserializeDefaultWitnessTableEntries(ModuleDecl *mod,
SILDefaultWitnessTable *wt) override;
void didDeserialize(ModuleDecl *mod, SILGlobalVariable *var) override;
void didDeserialize(ModuleDecl *mod, SILVTable *vtable) override;
void didDeserialize(ModuleDecl *mod, SILWitnessTable *wtable) override;
void didDeserialize(ModuleDecl *mod,
SILDefaultWitnessTable *wtable) override;
};
/// A protocol (or interface) for handling value deletion notifications.
///
/// This class is used as a base class for any class that need to accept
/// instruction deletion notification messages. This is used by passes and
/// analysis that need to invalidate data structures that contain pointers.
/// This is similar to LLVM's ValueHandle.
struct DeleteNotificationHandler {
DeleteNotificationHandler() { }
virtual ~DeleteNotificationHandler() {}
/// Handle the invalidation message for the value \p Value.
virtual void handleDeleteNotification(SILNode *value) { }
/// Returns True if the pass, analysis or other entity wants to receive
/// notifications. This callback is called once when the class is being
/// registered, and not once per notification. Entities that implement
/// this callback should always return a constant answer (true/false).
virtual bool needsNotifications() { return false; }
};
} // namespace swift
#endif