--- /dev/null
+//===- Attributor.h --- Module-wide attribute deduction ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Attributor: An inter procedural (abstract) "attribute" deduction framework.
+//
+// The Attributor framework is an inter procedural abstract analysis (fixpoint
+// iteration analysis). The goal is to allow easy deduction of new attributes as
+// well as information exchange between abstract attributes in-flight.
+//
+// The Attributor class is the driver and the link between the various abstract
+// attributes. The Attributor will iterate until a fixpoint state is reached by
+// all abstract attributes in-flight, or until it will enforce a pessimistic fix
+// point because an iteration limit is reached.
+//
+// Abstract attributes, derived from the AbstractAttribute class, actually
+// describe properties of the code. They can correspond to actual LLVM-IR
+// attributes, or they can be more general, ultimately unrelated to LLVM-IR
+// attributes. The latter is useful when an abstract attributes provides
+// information to other abstract attributes in-flight but we might not want to
+// manifest the information. The Attributor allows to query in-flight abstract
+// attributes through the `Attributor::getAAFor` method (see the method
+// description for an example). If the method is used by an abstract attribute
+// P, and it results in an abstract attribute Q, the Attributor will
+// automatically capture a potential dependence from Q to P. This dependence
+// will cause P to be reevaluated whenever Q changes in the future.
+//
+// The Attributor will only reevaluated abstract attributes that might have
+// changed since the last iteration. That means that the Attribute will not
+// revisit all instructions/blocks/functions in the module but only query
+// an update from a subset of the abstract attributes.
+//
+// The update method `AbstractAttribute::updateImpl` is implemented by the
+// specific "abstract attribute" subclasses. The method is invoked whenever the
+// currently assumed state (see the AbstractState class) might not be valid
+// anymore. This can, for example, happen if the state was dependent on another
+// abstract attribute that changed. In every invocation, the update method has
+// to adjust the internal state of an abstract attribute to a point that is
+// justifiable by the underlying IR and the current state of abstract attributes
+// in-flight. Since the IR is given and assumed to be valid, the information
+// derived from it can be assumed to hold. However, information derived from
+// other abstract attributes is conditional on various things. If the justifying
+// state changed, the `updateImpl` has to revisit the situation and potentially
+// find another justification or limit the optimistic assumes made.
+//
+// Change is the key in this framework. Until a state of no-change, thus a
+// fixpoint, is reached, the Attributor will query the abstract attributes
+// in-flight to re-evaluate their state. If the (current) state is too
+// optimistic, hence it cannot be justified anymore through other abstract
+// attributes or the state of the IR, the state of the abstract attribute will
+// have to change. Generally, we assume abstract attribute state to be a finite
+// height lattice and the update function to be monotone. However, these
+// conditions are not enforced because the iteration limit will guarantee
+// termination. If an optimistic fixpoint is reached, or a pessimistic fix
+// point is enforced after a timeout, the abstract attributes are tasked to
+// manifest their result in the IR for passes to come.
+//
+// Attribute manifestation is not mandatory. If desired, there is support to
+// generate a single LLVM-IR attribute already in the AbstractAttribute base
+// class. In the simplest case, a subclass overloads
+// `AbstractAttribute::getManifestPosition()` and
+// `AbstractAttribute::getAttrKind()` to return the appropriate values. The
+// Attributor manifestation framework will then create and place a new attribute
+// if it is allowed to do so (based on the abstract state). Other use cases can
+// be achieved by overloading other abstract attribute methods.
+//
+//
+// The "mechanics" of adding a new "abstract attribute":
+// - Define a class (transitively) inheriting from AbstractAttribute and one
+// (which could be the same) that (transitively) inherits from AbstractState.
+// For the latter, consider the already available BooleanState and
+// IntegerState if they fit your needs, e.g., you require only a bit-encoding.
+// - Implement all pure methods. Also use overloading if the attribute is not
+// conforming with the "default" behavior: A (set of) LLVM-IR attribute(s) for
+// an argument, call site argument, function return value, or function. See
+// the class and method descriptions for more information on the two
+// "Abstract" classes and their respective methods.
+// - Register opportunities for the new abstract attribute in the
+// `Attributor::identifyDefaultAbstractAttributes` method if it should be
+// counted as a 'default' attribute.
+// - Add sufficient tests.
+// - Add a Statistics object for bookkeeping. If it is a simple (set of)
+// attribute(s) manifested through the Attributor manifestation framework, see
+// the bookkeeping function in Attributor.cpp.
+// - If instructions with a certain opcode are interesting to the attribute, add
+// that opcode to the switch in `Attributor::identifyAbstractAttributes`. This
+// will make it possible to query all those instructions through the
+// `InformationCache::getOpcodeInstMapForFunction` interface and eliminate the
+// need to traverse the IR repeatedly.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H
+#define LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H
+
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/IR/CallSite.h"
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct AbstractAttribute;
+struct InformationCache;
+
+class Function;
+
+/// Simple enum class that forces the status to be spelled out explicitly.
+///
+///{
+enum class ChangeStatus {
+ CHANGED,
+ UNCHANGED,
+};
+
+ChangeStatus operator|(ChangeStatus l, ChangeStatus r);
+ChangeStatus operator&(ChangeStatus l, ChangeStatus r);
+///}
+
+/// The fixpoint analysis framework that orchestrates the attribute deduction.
+///
+/// The Attributor provides a general abstract analysis framework (guided
+/// fixpoint iteration) as well as helper functions for the deduction of
+/// (LLVM-IR) attributes. However, also other code properties can be deduced,
+/// propagated, and ultimately manifested through the Attributor framework. This
+/// is particularly useful if these properties interact with attributes and a
+/// co-scheduled deduction allows to improve the solution. Even if not, thus if
+/// attributes/properties are completely isolated, they should use the
+/// Attributor framework to reduce the number of fixpoint iteration frameworks
+/// in the code base. Note that the Attributor design makes sure that isolated
+/// attributes are not impacted, in any way, by others derived at the same time
+/// if there is no cross-reasoning performed.
+///
+/// The public facing interface of the Attributor is kept simple and basically
+/// allows abstract attributes to one thing, query abstract attributes
+/// in-flight. There are two reasons to do this:
+/// a) The optimistic state of one abstract attribute can justify an
+/// optimistic state of another, allowing to framework to end up with an
+/// optimistic (=best possible) fixpoint instead of one based solely on
+/// information in the IR.
+/// b) This avoids reimplementing various kinds of lookups, e.g., to check
+/// for existing IR attributes, in favor of a single lookups interface
+/// provided by an abstract attribute subclass.
+///
+/// NOTE: The mechanics of adding a new "concrete" abstract attribute are
+/// described in the file comment.
+struct Attributor {
+ ~Attributor() { DeleteContainerPointers(AllAbstractAttributes); }
+
+ /// Run the analyses until a fixpoint is reached or enforced (timeout).
+ ///
+ /// The attributes registered with this Attributor can be used after as long
+ /// as the Attributor is not destroyed (it owns the attributes now).
+ ///
+ /// \Returns CHANGED if the IR was changed, otherwise UNCHANGED.
+ ChangeStatus run();
+
+ /// Lookup an abstract attribute of type \p AAType anchored at value \p V and
+ /// argument number \p ArgNo. If no attribute is found and \p V is a call base
+ /// instruction, the called function is tried as a value next. Thus, the
+ /// returned abstract attribute might be anchored at the callee of \p V.
+ ///
+ /// This method is the only (supported) way an abstract attribute can retrieve
+ /// information from another abstract attribute. As an example, take an
+ /// abstract attribute that determines the memory access behavior for a
+ /// argument (readnone, readonly, ...). It should use `getAAFor` to get the
+ /// most optimistic information for other abstract attributes in-flight, e.g.
+ /// the one reasoning about the "captured" state for the argument or the one
+ /// reasoning on the memory access behavior of the function as a whole.
+ template <typename AAType>
+ const AAType *getAAFor(AbstractAttribute &QueryingAA, const Value &V,
+ int ArgNo = -1) {
+ static_assert(std::is_base_of<AbstractAttribute, AAType>::value,
+ "Cannot query an attribute with a type not derived from "
+ "'AbstractAttribute'!");
+ assert(AAType::ID != Attribute::None &&
+ "Cannot lookup generic abstract attributes!");
+
+ // Determine the argument number automatically for llvm::Arguments.
+ if (auto *Arg = dyn_cast<Argument>(&V))
+ ArgNo = Arg->getArgNo();
+
+ // If a function was given together with an argument number, perform the
+ // lookup for the actual argument instead. Don't do it for variadic
+ // arguments.
+ if (ArgNo >= 0 && isa<Function>(&V) &&
+ cast<Function>(&V)->arg_size() > (size_t)ArgNo)
+ return getAAFor<AAType>(
+ QueryingAA, *(cast<Function>(&V)->arg_begin() + ArgNo), ArgNo);
+
+ // Lookup the abstract attribute of type AAType. If found, return it after
+ // registering a dependence of QueryingAA on the one returned attribute.
+ const auto &KindToAbstractAttributeMap = AAMap.lookup({&V, ArgNo});
+ if (AAType *AA = static_cast<AAType *>(
+ KindToAbstractAttributeMap.lookup(AAType::ID))) {
+ QueryMap[AA].insert(&QueryingAA);
+ return AA;
+ }
+
+ // If no abstract attribute was found and we look for a call site argument,
+ // defer to the actual argument instead.
+ ImmutableCallSite ICS(&V);
+ if (ICS && ICS.getCalledValue())
+ return getAAFor<AAType>(QueryingAA, *ICS.getCalledValue(), ArgNo);
+
+ // No matching attribute found
+ return nullptr;
+ }
+
+ /// Introduce a new abstract attribute into the fixpoint analysis.
+ ///
+ /// Note that ownership of the attribute is given to the Attributor. It will
+ /// invoke delete for the Attributor on destruction of the Attributor.
+ ///
+ /// Attributes are identified by
+ /// (1) their anchored value (see AA.getAnchoredValue()),
+ /// (2) their argument number (\p ArgNo, or Argument::getArgNo()), and
+ /// (3) their default attribute kind (see AAType::ID).
+ template <typename AAType> AAType ®isterAA(AAType &AA, int ArgNo = -1) {
+ static_assert(std::is_base_of<AbstractAttribute, AAType>::value,
+ "Cannot register an attribute with a type not derived from "
+ "'AbstractAttribute'!");
+
+ // Determine the anchor value and the argument number which are used to
+ // lookup the attribute together with AAType::ID.
+ Value &AnchoredVal = AA.getAnchoredValue();
+ if (auto *Arg = dyn_cast<Argument>(&AnchoredVal))
+ ArgNo = Arg->getArgNo();
+
+ // Put the attribute in the lookup map structure and the container we use to
+ // keep track of all attributes.
+ AAMap[{&AnchoredVal, ArgNo}][AAType::ID] = &AA;
+ AllAbstractAttributes.push_back(&AA);
+ return AA;
+ }
+
+ /// Determine opportunities to derive 'default' attributes in \p F and create
+ /// abstract attribute objects for them.
+ ///
+ /// \param F The function that is checked for attribute opportunities.
+ /// \param InfoCache A cache for information queryable by the new attributes.
+ /// \param Whitelist If not null, a set limiting the attribute opportunities.
+ ///
+ /// Note that abstract attribute instances are generally created even if the
+ /// IR already contains the information they would deduce. The most important
+ /// reason for this is the single interface, the one of the abstract attribute
+ /// instance, which can be queried without the need to look at the IR in
+ /// various places.
+ void identifyDefaultAbstractAttributes(
+ Function &F, InformationCache &InfoCache,
+ DenseSet</* Attribute::AttrKind */ unsigned> *Whitelist = nullptr);
+
+private:
+ /// The set of all abstract attributes.
+ ///{
+ using AAVector = SmallVector<AbstractAttribute *, 64>;
+ AAVector AllAbstractAttributes;
+ ///}
+
+ /// A nested map to lookup abstract attributes based on the anchored value and
+ /// an argument positions (or -1) on the outer level, and attribute kinds
+ /// (Attribute::AttrKind) on the inner level.
+ ///{
+ using KindToAbstractAttributeMap = DenseMap<unsigned, AbstractAttribute *>;
+ DenseMap<std::pair<const Value *, int>, KindToAbstractAttributeMap> AAMap;
+ ///}
+
+ /// A map from abstract attributes to the ones that queried them through calls
+ /// to the getAAFor<...>(...) method.
+ ///{
+ using QueryMapTy =
+ DenseMap<AbstractAttribute *, SetVector<AbstractAttribute *>>;
+ QueryMapTy QueryMap;
+ ///}
+};
+
+/// Data structure to hold cached (LLVM-IR) information.
+///
+/// All attributes are given an InformationCache object at creation time to
+/// avoid inspection of the IR by all of them individually. This default
+/// InformationCache will hold information required by 'default' attributes,
+/// thus the ones deduced when Attributor::identifyDefaultAbstractAttributes(..)
+/// is called.
+///
+/// If custom abstract attributes, registered manually through
+/// Attributor::registerAA(...), need more information, especially if it is not
+/// reusable, it is advised to inherit from the InformationCache and cast the
+/// instance down in the abstract attributes.
+struct InformationCache {
+ /// A map type from opcodes to instructions with this opcode.
+ using OpcodeInstMapTy = DenseMap<unsigned, SmallVector<Instruction *, 32>>;
+
+ /// Return the map that relates "interesting" opcodes with all instructions
+ /// with that opcode in \p F.
+ OpcodeInstMapTy &getOpcodeInstMapForFunction(Function &F) {
+ return FuncInstOpcodeMap[&F];
+ }
+
+ /// A vector type to hold instructions.
+ using InstructionVectorTy = std::vector<Instruction *>;
+
+ /// Return the instructions in \p F that may read or write memory.
+ InstructionVectorTy &getReadOrWriteInstsForFunction(Function &F) {
+ return FuncRWInstsMap[&F];
+ }
+
+private:
+ /// A map type from functions to opcode to instruction maps.
+ using FuncInstOpcodeMapTy = DenseMap<Function *, OpcodeInstMapTy>;
+
+ /// A map type from functions to their read or write instructions.
+ using FuncRWInstsMapTy = DenseMap<Function *, InstructionVectorTy>;
+
+ /// A nested map that remembers all instructions in a function with a certain
+ /// instruction opcode (Instruction::getOpcode()).
+ FuncInstOpcodeMapTy FuncInstOpcodeMap;
+
+ /// A map from functions to their instructions that may read or write memory.
+ FuncRWInstsMapTy FuncRWInstsMap;
+
+ /// Give the Attributor access to the members so
+ /// Attributor::identifyDefaultAbstractAttributes(...) can initialize them.
+ friend struct Attributor;
+};
+
+/// An interface to query the internal state of an abstract attribute.
+///
+/// The abstract state is a minimal interface that allows the Attributor to
+/// communicate with the abstract attributes about their internal state without
+/// enforcing or exposing implementation details, e.g., the (existence of an)
+/// underlying lattice.
+///
+/// It is sufficient to be able to query if a state is (1) valid or invalid, (2)
+/// at a fixpoint, and to indicate to the state that (3) an optimistic fixpoint
+/// was reached or (4) a pessimistic fixpoint was enforced.
+///
+/// All methods need to be implemented by the subclass. For the common use case,
+/// a single boolean state or a bit-encoded state, the BooleanState and
+/// IntegerState classes are already provided. An abstract attribute can inherit
+/// from them to get the abstract state interface and additional methods to
+/// directly modify the state based if needed. See the class comments for help.
+struct AbstractState {
+ virtual ~AbstractState() {}
+
+ /// Return if this abstract state is in a valid state. If false, no
+ /// information provided should be used.
+ virtual bool isValidState() const = 0;
+
+ /// Return if this abstract state is fixed, thus does not need to be updated
+ /// if information changes as it cannot change itself.
+ virtual bool isAtFixpoint() const = 0;
+
+ /// Indicate that the abstract state should converge to the optimistic state.
+ ///
+ /// This will usually make the optimistically assumed state the known to be
+ /// true state.
+ virtual void indicateOptimisticFixpoint() = 0;
+
+ /// Indicate that the abstract state should converge to the pessimistic state.
+ ///
+ /// This will usually revert the optimistically assumed state to the known to
+ /// be true state.
+ virtual void indicatePessimisticFixpoint() = 0;
+};
+
+/// Base struct for all "concrete attribute" deductions.
+///
+/// The abstract attribute is a minimal interface that allows the Attributor to
+/// orchestrate the abstract/fixpoint analysis. The design allows to hide away
+/// implementation choices made for the subclasses but also to structure their
+/// implementation and simplify the use of other abstract attributes in-flight.
+///
+/// To allow easy creation of new attributes, most methods have default
+/// implementations. The ones that do not are generally straight forward, except
+/// `AbstractAttribute::updateImpl` which is the location of most reasoning
+/// associated with the abstract attribute. The update is invoked by the
+/// Attributor in case the situation used to justify the current optimistic
+/// state might have changed. The Attributor determines this automatically
+/// by monitoring the `Attributor::getAAFor` calls made by abstract attributes.
+///
+/// The `updateImpl` method should inspect the IR and other abstract attributes
+/// in-flight to justify the best possible (=optimistic) state. The actual
+/// implementation is, similar to the underlying abstract state encoding, not
+/// exposed. In the most common case, the `updateImpl` will go through a list of
+/// reasons why its optimistic state is valid given the current information. If
+/// any combination of them holds and is sufficient to justify the current
+/// optimistic state, the method shall return UNCHAGED. If not, the optimistic
+/// state is adjusted to the situation and the method shall return CHANGED.
+///
+/// If the manifestation of the "concrete attribute" deduced by the subclass
+/// differs from the "default" behavior, which is a (set of) LLVM-IR
+/// attribute(s) for an argument, call site argument, function return value, or
+/// function, the `AbstractAttribute::manifest` method should be overloaded.
+///
+/// NOTE: If the state obtained via getState() is INVALID, thus if
+/// AbstractAttribute::getState().isValidState() returns false, no
+/// information provided by the methods of this class should be used.
+/// NOTE: The Attributor currently runs as a call graph SCC pass. Partially to
+/// this *current* choice there are certain limitations to what we can do.
+/// As a general rule of thumb, "concrete" abstract attributes should *for
+/// now* only perform "backward" information propagation. That means
+/// optimistic information obtained through abstract attributes should
+/// only be used at positions that precede the origin of the information
+/// with regards to the program flow. More practically, information can
+/// *now* be propagated from instructions to their enclosing function, but
+/// *not* from call sites to the called function. The mechanisms to allow
+/// both directions will be added in the future.
+/// NOTE: The mechanics of adding a new "concrete" abstract attribute are
+/// described in the file comment.
+struct AbstractAttribute {
+
+ /// The positions attributes can be manifested in.
+ enum ManifestPosition {
+ MP_ARGUMENT, ///< An attribute for a function argument.
+ MP_CALL_SITE_ARGUMENT, ///< An attribute for a call site argument.
+ MP_FUNCTION, ///< An attribute for a function as a whole.
+ MP_RETURNED, ///< An attribute for the function return value.
+ };
+
+ /// An abstract attribute associated with \p AssociatedVal and anchored at
+ /// \p AnchoredVal.
+ ///
+ /// \param AssociatedVal The value this abstract attribute is associated with.
+ /// \param AnchoredVal The value this abstract attributes is anchored at.
+ /// \param InfoCache Cached information accessible to the abstract attribute.
+ AbstractAttribute(Value *AssociatedVal, Value &AnchoredVal,
+ InformationCache &InfoCache)
+ : AssociatedVal(AssociatedVal), AnchoredVal(AnchoredVal),
+ InfoCache(InfoCache) {}
+
+ /// An abstract attribute associated with and anchored at \p V.
+ AbstractAttribute(Value &V, InformationCache &InfoCache)
+ : AbstractAttribute(&V, V, InfoCache) {}
+
+ /// Virtual destructor.
+ virtual ~AbstractAttribute() {}
+
+ /// Initialize the state with the information in the Attributor \p A.
+ ///
+ /// This function is called by the Attributor once all abstract attributes
+ /// have been identified. It can and shall be used for task like:
+ /// - identify existing knowledge in the IR and use it for the "known state"
+ /// - perform any work that is not going to change over time, e.g., determine
+ /// a subset of the IR, or attributes in-flight, that have to be looked at
+ /// in the `updateImpl` method.
+ virtual void initialize(Attributor &A) {}
+
+ /// Return the internal abstract state for inspection.
+ virtual const AbstractState &getState() const = 0;
+
+ /// Return the value this abstract attribute is anchored with.
+ ///
+ /// The anchored value might not be the associated value if the latter is not
+ /// sufficient to determine where arguments will be manifested. This is mostly
+ /// the case for call site arguments as the value is not sufficient to
+ /// pinpoint them. Instead, we can use the call site as an anchor.
+ ///
+ ///{
+ Value &getAnchoredValue() { return AnchoredVal; }
+ const Value &getAnchoredValue() const { return AnchoredVal; }
+ ///}
+
+ /// Return the llvm::Function surrounding the anchored value.
+ ///
+ ///{
+ Function &getAnchorScope();
+ const Function &getAnchorScope() const;
+ ///}
+
+ /// Return the value this abstract attribute is associated with.
+ ///
+ /// The abstract state usually represents this value.
+ ///
+ ///{
+ virtual Value *getAssociatedValue() { return AssociatedVal; }
+ virtual const Value *getAssociatedValue() const { return AssociatedVal; }
+ ///}
+
+ /// Return the position this abstract state is manifested in.
+ virtual ManifestPosition getManifestPosition() const = 0;
+
+ /// Return the kind that identifies the abstract attribute implementation.
+ virtual Attribute::AttrKind getAttrKind() const = 0;
+
+ /// Return the deduced attributes in \p Attrs.
+ virtual void getDeducedAttributes(SmallVectorImpl<Attribute> &Attrs) const {
+ LLVMContext &Ctx = AnchoredVal.getContext();
+ Attrs.emplace_back(Attribute::get(Ctx, getAttrKind()));
+ }
+
+ /// Helper functions, for debug purposes only.
+ ///{
+ virtual void print(raw_ostream &OS) const;
+ void dump() const { print(dbgs()); }
+
+ /// This function should return the "summarized" assumed state as string.
+ virtual const std::string getAsStr() const = 0;
+ ///}
+
+ /// Allow the Attributor access to the protected methods.
+ friend struct Attributor;
+
+protected:
+ /// Hook for the Attributor to trigger an update of the internal state.
+ ///
+ /// If this attribute is already fixed, this method will return UNCHANGED,
+ /// otherwise it delegates to `AbstractAttribute::updateImpl`.
+ ///
+ /// \Return CHANGED if the internal state changed, otherwise UNCHANGED.
+ ChangeStatus update(Attributor &A);
+
+ /// Hook for the Attributor to trigger the manifestation of the information
+ /// represented by the abstract attribute in the LLVM-IR.
+ ///
+ /// \Return CHANGED if the IR was altered, otherwise UNCHANGED.
+ virtual ChangeStatus manifest(Attributor &A);
+
+ /// Return the internal abstract state for careful modification.
+ virtual AbstractState &getState() = 0;
+
+ /// The actual update/transfer function which has to be implemented by the
+ /// derived classes.
+ ///
+ /// If it is called, the environment has changed and we have to determine if
+ /// the current information is still valid or adjust it otherwise.
+ ///
+ /// \Return CHANGED if the internal state changed, otherwise UNCHANGED.
+ virtual ChangeStatus updateImpl(Attributor &A) = 0;
+
+ /// The value this abstract attribute is associated with.
+ Value *AssociatedVal;
+
+ /// The value this abstract attribute is anchored at.
+ Value &AnchoredVal;
+
+ /// The information cache accessible to this abstract attribute.
+ InformationCache &InfoCache;
+};
+
+/// Forward declarations of output streams for debug purposes.
+///
+///{
+raw_ostream &operator<<(raw_ostream &OS, const AbstractAttribute &AA);
+raw_ostream &operator<<(raw_ostream &OS, ChangeStatus S);
+raw_ostream &operator<<(raw_ostream &OS, AbstractAttribute::ManifestPosition);
+raw_ostream &operator<<(raw_ostream &OS, const AbstractState &State);
+///}
+
+struct AttributorPass : public PassInfoMixin<AttributorPass> {
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+Pass *createAttributorLegacyPass();
+
+/// ----------------------------------------------------------------------------
+/// Abstract Attribute Classes
+/// ----------------------------------------------------------------------------
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
--- /dev/null
+//===- Attributor.cpp - Module-wide attribute deduction -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements an inter procedural pass that deduces and/or propagating
+// attributes. This is done in an abstract interpretation style fixpoint
+// iteration. See the Attributor.h file comment and the class descriptions in
+// that file for more information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/Attributor.h"
+
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/IR/Argument.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "attributor"
+
+STATISTIC(NumFnWithExactDefinition,
+ "Number of function with exact definitions");
+STATISTIC(NumFnWithoutExactDefinition,
+ "Number of function without exact definitions");
+STATISTIC(NumAttributesTimedOut,
+ "Number of abstract attributes timed out before fixpoint");
+STATISTIC(NumAttributesValidFixpoint,
+ "Number of abstract attributes in a valid fixpoint state");
+STATISTIC(NumAttributesManifested,
+ "Number of abstract attributes manifested in IR");
+
+// TODO: Determine a good default value.
+//
+// In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads
+// (when run with the first 5 abstract attributes). The results also indicate
+// that we never reach 32 iterations but always find a fixpoint sooner.
+//
+// This will become more evolved once we perform two interleaved fixpoint
+// iterations: bottom-up and top-down.
+static cl::opt<unsigned>
+ MaxFixpointIterations("attributor-max-iterations", cl::Hidden,
+ cl::desc("Maximal number of fixpoint iterations."),
+ cl::init(32));
+
+static cl::opt<bool> DisableAttributor(
+ "attributor-disable", cl::Hidden,
+ cl::desc("Disable the attributor inter-procedural deduction pass."),
+ cl::init(false));
+
+static cl::opt<bool> VerifyAttributor(
+ "attributor-verify", cl::Hidden,
+ cl::desc("Verify the Attributor deduction and "
+ "manifestation of attributes -- may issue false-positive errors"),
+ cl::init(false));
+
+/// Logic operators for the change status enum class.
+///
+///{
+ChangeStatus llvm::operator|(ChangeStatus l, ChangeStatus r) {
+ return l == ChangeStatus::CHANGED ? l : r;
+}
+ChangeStatus llvm::operator&(ChangeStatus l, ChangeStatus r) {
+ return l == ChangeStatus::UNCHANGED ? l : r;
+}
+///}
+
+/// Helper to adjust the statistics.
+static void bookkeeping(AbstractAttribute::ManifestPosition MP,
+ const Attribute &Attr) {
+ if (!AreStatisticsEnabled())
+ return;
+
+ if (!Attr.isEnumAttribute())
+ return;
+ switch (Attr.getKindAsEnum()) {
+ default:
+ return;
+ }
+}
+
+/// Helper to identify the correct offset into an attribute list.
+static unsigned getAttrIndex(AbstractAttribute::ManifestPosition MP,
+ unsigned ArgNo = 0) {
+ switch (MP) {
+ case AbstractAttribute::MP_ARGUMENT:
+ case AbstractAttribute::MP_CALL_SITE_ARGUMENT:
+ return ArgNo + AttributeList::FirstArgIndex;
+ case AbstractAttribute::MP_FUNCTION:
+ return AttributeList::FunctionIndex;
+ case AbstractAttribute::MP_RETURNED:
+ return AttributeList::ReturnIndex;
+ }
+}
+
+/// Return true if \p New is equal or worse than \p Old.
+static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) {
+ if (!Old.isIntAttribute())
+ return true;
+
+ return Old.getValueAsInt() >= New.getValueAsInt();
+}
+
+/// Return true if the information provided by \p Attr was added to the
+/// attribute list \p Attrs. This is only the case if it was not already present
+/// in \p Attrs at the position describe by \p MP and \p ArgNo.
+static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
+ AttributeList &Attrs,
+ AbstractAttribute::ManifestPosition MP,
+ unsigned ArgNo = 0) {
+ unsigned AttrIdx = getAttrIndex(MP, ArgNo);
+
+ if (Attr.isEnumAttribute()) {
+ Attribute::AttrKind Kind = Attr.getKindAsEnum();
+ if (Attrs.hasAttribute(AttrIdx, Kind))
+ if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
+ return false;
+ Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr);
+ return true;
+ }
+ if (Attr.isStringAttribute()) {
+ StringRef Kind = Attr.getKindAsString();
+ if (Attrs.hasAttribute(AttrIdx, Kind))
+ if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
+ return false;
+ Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr);
+ return true;
+ }
+
+ llvm_unreachable("Expected enum or string attribute!");
+}
+
+ChangeStatus AbstractAttribute::update(Attributor &A) {
+ ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
+ if (getState().isAtFixpoint())
+ return HasChanged;
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Update: " << *this << "\n");
+
+ HasChanged = updateImpl(A);
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Update " << HasChanged << " " << *this
+ << "\n");
+
+ return HasChanged;
+}
+
+ChangeStatus AbstractAttribute::manifest(Attributor &A) {
+ assert(getState().isValidState() &&
+ "Attempted to manifest an invalid state!");
+ assert(getAssociatedValue() &&
+ "Attempted to manifest an attribute without associated value!");
+
+ ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
+ SmallVector<Attribute, 4> DeducedAttrs;
+ getDeducedAttributes(DeducedAttrs);
+
+ Function &ScopeFn = getAnchorScope();
+ LLVMContext &Ctx = ScopeFn.getContext();
+ ManifestPosition MP = getManifestPosition();
+
+ AttributeList Attrs;
+ SmallVector<unsigned, 4> ArgNos;
+
+ // In the following some generic code that will manifest attributes in
+ // DeducedAttrs if they improve the current IR. Due to the different
+ // annotation positions we use the underlying AttributeList interface.
+ // Note that MP_CALL_SITE_ARGUMENT can annotate multiple locations.
+
+ switch (MP) {
+ case MP_ARGUMENT:
+ ArgNos.push_back(cast<Argument>(getAssociatedValue())->getArgNo());
+ Attrs = ScopeFn.getAttributes();
+ break;
+ case MP_FUNCTION:
+ case MP_RETURNED:
+ ArgNos.push_back(0);
+ Attrs = ScopeFn.getAttributes();
+ break;
+ case MP_CALL_SITE_ARGUMENT: {
+ CallSite CS(&getAnchoredValue());
+ for (unsigned u = 0, e = CS.getNumArgOperands(); u != e; u++)
+ if (CS.getArgOperand(u) == getAssociatedValue())
+ ArgNos.push_back(u);
+ Attrs = CS.getAttributes();
+ }
+ }
+
+ for (const Attribute &Attr : DeducedAttrs) {
+ for (unsigned ArgNo : ArgNos) {
+ if (!addIfNotExistent(Ctx, Attr, Attrs, MP, ArgNo))
+ continue;
+
+ HasChanged = ChangeStatus::CHANGED;
+ bookkeeping(MP, Attr);
+ }
+ }
+
+ if (HasChanged == ChangeStatus::UNCHANGED)
+ return HasChanged;
+
+ switch (MP) {
+ case MP_ARGUMENT:
+ case MP_FUNCTION:
+ case MP_RETURNED:
+ ScopeFn.setAttributes(Attrs);
+ break;
+ case MP_CALL_SITE_ARGUMENT:
+ CallSite(&getAnchoredValue()).setAttributes(Attrs);
+ }
+
+ return HasChanged;
+}
+
+Function &AbstractAttribute::getAnchorScope() {
+ Value &V = getAnchoredValue();
+ if (isa<Function>(V))
+ return cast<Function>(V);
+ if (isa<Argument>(V))
+ return *cast<Argument>(V).getParent();
+ if (isa<Instruction>(V))
+ return *cast<Instruction>(V).getFunction();
+ llvm_unreachable("No scope for anchored value found!");
+}
+
+const Function &AbstractAttribute::getAnchorScope() const {
+ return const_cast<AbstractAttribute *>(this)->getAnchorScope();
+}
+
+/// ----------------------------------------------------------------------------
+/// Attributor
+/// ----------------------------------------------------------------------------
+
+ChangeStatus Attributor::run() {
+ // Initialize all abstract attributes.
+ for (AbstractAttribute *AA : AllAbstractAttributes)
+ AA->initialize(*this);
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized "
+ << AllAbstractAttributes.size()
+ << " abstract attributes.\n");
+
+ // Now that all abstract attributes are collected and initialized we start the
+ // abstract analysis.
+
+ unsigned IterationCounter = 1;
+
+ SmallVector<AbstractAttribute *, 64> ChangedAAs;
+ SetVector<AbstractAttribute *> Worklist;
+ Worklist.insert(AllAbstractAttributes.begin(), AllAbstractAttributes.end());
+
+ do {
+ LLVM_DEBUG(dbgs() << "\n\n[Attributor] #Iteration: " << IterationCounter
+ << ", Worklist size: " << Worklist.size() << "\n");
+
+ // Add all abstract attributes that are potentially dependent on one that
+ // changed to the work list.
+ for (AbstractAttribute *ChangedAA : ChangedAAs) {
+ auto &QuerriedAAs = QueryMap[ChangedAA];
+ Worklist.insert(QuerriedAAs.begin(), QuerriedAAs.end());
+ }
+
+ // Reset the changed set.
+ ChangedAAs.clear();
+
+ // Update all abstract attribute in the work list and record the ones that
+ // changed.
+ for (AbstractAttribute *AA : Worklist)
+ if (AA->update(*this) == ChangeStatus::CHANGED)
+ ChangedAAs.push_back(AA);
+
+ // Reset the work list and repopulate with the changed abstract attributes.
+ // Note that dependent ones are added above.
+ Worklist.clear();
+ Worklist.insert(ChangedAAs.begin(), ChangedAAs.end());
+
+ } while (!Worklist.empty() && ++IterationCounter < MaxFixpointIterations);
+
+ LLVM_DEBUG(dbgs() << "\n[Attributor] Fixpoint iteration done after: "
+ << IterationCounter << "/" << MaxFixpointIterations
+ << " iterations\n");
+
+ bool FinishedAtFixpoint = Worklist.empty();
+
+ // Reset abstract arguments not settled in a sound fixpoint by now. This
+ // happens when we stopped the fixpoint iteration early. Note that only the
+ // ones marked as "changed" *and* the ones transitively depending on them
+ // need to be reverted to a pessimistic state. Others might not be in a
+ // fixpoint state but we can use the optimistic results for them anyway.
+ SmallPtrSet<AbstractAttribute *, 32> Visited;
+ for (unsigned u = 0; u < ChangedAAs.size(); u++) {
+ AbstractAttribute *ChangedAA = ChangedAAs[u];
+ if (!Visited.insert(ChangedAA).second)
+ continue;
+
+ AbstractState &State = ChangedAA->getState();
+ if (!State.isAtFixpoint()) {
+ State.indicatePessimisticFixpoint();
+
+ NumAttributesTimedOut++;
+ }
+
+ auto &QuerriedAAs = QueryMap[ChangedAA];
+ ChangedAAs.append(QuerriedAAs.begin(), QuerriedAAs.end());
+ }
+
+ LLVM_DEBUG({
+ if (!Visited.empty())
+ dbgs() << "\n[Attributor] Finalized " << Visited.size()
+ << " abstract attributes.\n";
+ });
+
+ unsigned NumManifested = 0;
+ unsigned NumAtFixpoint = 0;
+ ChangeStatus ManifestChange = ChangeStatus::UNCHANGED;
+ for (AbstractAttribute *AA : AllAbstractAttributes) {
+ AbstractState &State = AA->getState();
+
+ // If there is not already a fixpoint reached, we can now take the
+ // optimistic state. This is correct because we enforced a pessimistic one
+ // on abstract attributes that were transitively dependent on a changed one
+ // already above.
+ if (!State.isAtFixpoint())
+ State.indicateOptimisticFixpoint();
+
+ // If the state is invalid, we do not try to manifest it.
+ if (!State.isValidState())
+ continue;
+
+ // Manifest the state and record if we changed the IR.
+ ChangeStatus LocalChange = AA->manifest(*this);
+ ManifestChange = ManifestChange | LocalChange;
+
+ NumAtFixpoint++;
+ NumManifested += (LocalChange == ChangeStatus::CHANGED);
+ }
+
+ (void)NumManifested;
+ (void)NumAtFixpoint;
+ LLVM_DEBUG(dbgs() << "\n[Attributor] Manifested " << NumManifested
+ << " arguments while " << NumAtFixpoint
+ << " were in a valid fixpoint state\n");
+
+ // If verification is requested, we finished this run at a fixpoint, and the
+ // IR was changed, we re-run the whole fixpoint analysis, starting at
+ // re-initialization of the arguments. This re-run should not result in an IR
+ // change. Though, the (virtual) state of attributes at the end of the re-run
+ // might be more optimistic than the known state or the IR state if the better
+ // state cannot be manifested.
+ if (VerifyAttributor && FinishedAtFixpoint &&
+ ManifestChange == ChangeStatus::CHANGED) {
+ VerifyAttributor = false;
+ ChangeStatus VerifyStatus = run();
+ if (VerifyStatus != ChangeStatus::UNCHANGED)
+ llvm_unreachable(
+ "Attributor verification failed, re-run did result in an IR change "
+ "even after a fixpoint was reached in the original run. (False "
+ "positives possible!)");
+ VerifyAttributor = true;
+ }
+
+ NumAttributesManifested += NumManifested;
+ NumAttributesValidFixpoint += NumAtFixpoint;
+
+ return ManifestChange;
+}
+
+void Attributor::identifyDefaultAbstractAttributes(
+ Function &F, InformationCache &InfoCache,
+ DenseSet</* Attribute::AttrKind */ unsigned> *Whitelist) {
+
+ // Walk all instructions to find more attribute opportunities and also
+ // interesting instructions that might be queried by abstract attributes
+ // during their initialization or update.
+ auto &ReadOrWriteInsts = InfoCache.FuncRWInstsMap[&F];
+ auto &InstOpcodeMap = InfoCache.FuncInstOpcodeMap[&F];
+
+ for (Instruction &I : instructions(&F)) {
+ bool IsInterestingOpcode = false;
+
+ // To allow easy access to all instructions in a function with a given
+ // opcode we store them in the InfoCache. As not all opcodes are interesting
+ // to concrete attributes we only cache the ones that are as identified in
+ // the following switch.
+ // Note: There are no concrete attributes now so this is initially empty.
+ switch (I.getOpcode()) {
+ default:
+ break;
+ }
+ if (IsInterestingOpcode)
+ InstOpcodeMap[I.getOpcode()].push_back(&I);
+ if (I.mayReadOrWriteMemory())
+ ReadOrWriteInsts.push_back(&I);
+ }
+}
+
+/// Helpers to ease debugging through output streams and print calls.
+///
+///{
+raw_ostream &llvm::operator<<(raw_ostream &OS, ChangeStatus S) {
+ return OS << (S == ChangeStatus::CHANGED ? "changed" : "unchanged");
+}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS,
+ AbstractAttribute::ManifestPosition AP) {
+ switch (AP) {
+ case AbstractAttribute::MP_ARGUMENT:
+ return OS << "arg";
+ case AbstractAttribute::MP_CALL_SITE_ARGUMENT:
+ return OS << "cs_arg";
+ case AbstractAttribute::MP_FUNCTION:
+ return OS << "fn";
+ case AbstractAttribute::MP_RETURNED:
+ return OS << "fn_ret";
+ }
+ llvm_unreachable("Unknown attribute position!");
+}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractState &S) {
+ return OS << (!S.isValidState() ? "top" : (S.isAtFixpoint() ? "fix" : ""));
+}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractAttribute &AA) {
+ AA.print(OS);
+ return OS;
+}
+
+void AbstractAttribute::print(raw_ostream &OS) const {
+ OS << "[" << getManifestPosition() << "][" << getAsStr() << "]["
+ << AnchoredVal.getName() << "]";
+}
+///}
+
+/// ----------------------------------------------------------------------------
+/// Pass (Manager) Boilerplate
+/// ----------------------------------------------------------------------------
+
+static bool runAttributorOnModule(Module &M) {
+ if (DisableAttributor)
+ return false;
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << M.size()
+ << " functions.\n");
+
+ // Create an Attributor and initially empty information cache that is filled
+ // while we identify default attribute opportunities.
+ Attributor A;
+ InformationCache InfoCache;
+
+ for (Function &F : M) {
+ // TODO: Not all attributes require an exact definition. Find a way to
+ // enable deduction for some but not all attributes in case the
+ // definition might be changed at runtime, see also
+ // http://lists.llvm.org/pipermail/llvm-dev/2018-February/121275.html.
+ // TODO: We could always determine abstract attributes and if sufficient
+ // information was found we could duplicate the functions that do not
+ // have an exact definition.
+ if (!F.hasExactDefinition()) {
+ NumFnWithoutExactDefinition++;
+ continue;
+ }
+
+ // For now we ignore naked and optnone functions.
+ if (F.hasFnAttribute(Attribute::Naked) ||
+ F.hasFnAttribute(Attribute::OptimizeNone))
+ continue;
+
+ NumFnWithExactDefinition++;
+
+ // Populate the Attributor with abstract attribute opportunities in the
+ // function and the information cache with IR information.
+ A.identifyDefaultAbstractAttributes(F, InfoCache);
+ }
+
+ return A.run() == ChangeStatus::CHANGED;
+}
+
+PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
+ if (runAttributorOnModule(M)) {
+ // FIXME: Think about passes we will preserve and add them here.
+ return PreservedAnalyses::none();
+ }
+ return PreservedAnalyses::all();
+}
+
+namespace {
+
+struct AttributorLegacyPass : public ModulePass {
+ static char ID;
+
+ AttributorLegacyPass() : ModulePass(ID) {
+ initializeAttributorLegacyPassPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+ return runAttributorOnModule(M);
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ // FIXME: Think about passes we will preserve and add them here.
+ AU.setPreservesCFG();
+ }
+};
+
+} // end anonymous namespace
+
+Pass *llvm::createAttributorLegacyPass() { return new AttributorLegacyPass(); }
+
+char AttributorLegacyPass::ID = 0;
+INITIALIZE_PASS_BEGIN(AttributorLegacyPass, "attributor",
+ "Deduce and propagate attributes", false, false)
+INITIALIZE_PASS_END(AttributorLegacyPass, "attributor",
+ "Deduce and propagate attributes", false, false)
+