//===- 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 reevaluate 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 or multiple LLVM-IR attributes already in the helper struct // IRAttribute. In the simplest case, a subclass inherits from IRAttribute with // a proper Attribute::AttrKind as template parameter. 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 AbstractAttribute or IRAttribute 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 // {Inc,Dec,Bit}IntegerState if they fit your needs, e.g., you require only a // number tracking or 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/ADT/DenseSet.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/iterator.h" #include "llvm/Analysis/AssumeBundleQueries.h" #include "llvm/Analysis/CFG.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/MemoryLocation.h" #include "llvm/Analysis/MustExecute.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/AbstractCallSite.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Constants.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Value.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DOTGraphTraits.h" #include "llvm/Support/DebugCounter.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ModRef.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/TypeSize.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Utils/CallGraphUpdater.h" #include <limits> #include <map> #include <optional> llvm // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H