|
| 1 | +//===--- DifferentiableActivityAnalysis.h ---------------------*- C++ -*---===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2019 - 2020 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | +// |
| 13 | +// This file implements activity analysis: a dataflow analysis used for |
| 14 | +// automatic differentiation. |
| 15 | +// |
| 16 | +// In many real situations, the end-users of AD need only the derivatives of |
| 17 | +// some selected outputs of `P` with respect to some selected inputs of `P`. |
| 18 | +// Whatever the differentiation mode (tangent, reverse,...), these restrictions |
| 19 | +// allow the AD tool to produce a much more efficient differentiated program. |
| 20 | +// Essentially, fixing some inputs and neglecting some outputs allows AD to |
| 21 | +// just forget about several intermediate differentiated variables. |
| 22 | +// |
| 23 | +// Activity analysis is the specific analysis that detects these situations, |
| 24 | +// therefore allowing for a better differentiated code. Activity analysis is |
| 25 | +// present in all transformation-based AD tools. |
| 26 | +// |
| 27 | +// To begin with, the end-user specifies that only some output variables (the |
| 28 | +// “dependent”) must be differentiated with respect to only some input |
| 29 | +// variables (the “independent”). We say that variable `y` depends on `x` when |
| 30 | +// the derivative of `y` with respect to `x` is not trivially null. We say that |
| 31 | +// a variable is “varied” if it depends on at least one independent. Conversely |
| 32 | +// we say that a variable is “useful” if at least one dependent depends on it. |
| 33 | +// Finally, we say that a variable is “active” if it is at the same time varied |
| 34 | +// and useful. In the special case of the tangent mode, it is easy to check |
| 35 | +// that when variable `v` is not varied at some place in the program, then its |
| 36 | +// derivative `v̇` at this place is certainly null. Conversely when variable `v` |
| 37 | +// is not useful, then whatever the value of `v̇`, this value does not matter |
| 38 | +// for the final result. Symmetric reasoning applies for the reverse mode of |
| 39 | +// AD: observing that differentiated variables go upstream, we see that a |
| 40 | +// useless variable has a null derivative, in other words the partial |
| 41 | +// derivative of the output with respect to this variable is null. Conversely |
| 42 | +// when variable `v` is not varied, then whatever the value of `v`, this value |
| 43 | +// does not matter for the final result. |
| 44 | +// |
| 45 | +// Reference: |
| 46 | +// Laurent Hascoët. Automatic Differentiation by Program Transformation. 2007. |
| 47 | + |
| 48 | +#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_DIFFERENTIABLEACTIVITYANALYSIS_H_ |
| 49 | +#define SWIFT_SILOPTIMIZER_ANALYSIS_DIFFERENTIABLEACTIVITYANALYSIS_H_ |
| 50 | + |
| 51 | +#include "swift/AST/GenericEnvironment.h" |
| 52 | +#include "swift/AST/GenericSignatureBuilder.h" |
| 53 | +#include "swift/SIL/SILFunction.h" |
| 54 | +#include "swift/SIL/SILModule.h" |
| 55 | +#include "swift/SIL/SILValue.h" |
| 56 | +#include "swift/SILOptimizer/Analysis/Analysis.h" |
| 57 | +#include "llvm/ADT/DenseMap.h" |
| 58 | +#include "llvm/ADT/DenseSet.h" |
| 59 | + |
| 60 | +using llvm::SmallDenseMap; |
| 61 | +using llvm::SmallDenseSet; |
| 62 | + |
| 63 | +namespace swift { |
| 64 | + |
| 65 | +class DominanceAnalysis; |
| 66 | +class PostDominanceAnalysis; |
| 67 | +class DominanceInfo; |
| 68 | +class PostDominanceInfo; |
| 69 | +class SILFunciton; |
| 70 | + |
| 71 | +class DifferentiableActivityCollection; |
| 72 | +class DifferentiableActivityAnalysis |
| 73 | + : public FunctionAnalysisBase<DifferentiableActivityCollection> { |
| 74 | +private: |
| 75 | + DominanceAnalysis *dominanceAnalysis = nullptr; |
| 76 | + PostDominanceAnalysis *postDominanceAnalysis = nullptr; |
| 77 | + |
| 78 | +public: |
| 79 | + explicit DifferentiableActivityAnalysis() |
| 80 | + : FunctionAnalysisBase(SILAnalysisKind::DifferentiableActivity) {} |
| 81 | + |
| 82 | + static bool classof(const SILAnalysis *s) { |
| 83 | + return s->getKind() == SILAnalysisKind::DifferentiableActivity; |
| 84 | + } |
| 85 | + |
| 86 | + virtual bool shouldInvalidate(SILAnalysis::InvalidationKind k) override { |
| 87 | + return k & InvalidationKind::Everything; |
| 88 | + } |
| 89 | + |
| 90 | + virtual std::unique_ptr<DifferentiableActivityCollection> |
| 91 | + newFunctionAnalysis(SILFunction *f) override; |
| 92 | + |
| 93 | + virtual void initialize(SILPassManager *pm) override; |
| 94 | +}; |
| 95 | + |
| 96 | +/// Represents the differentiation activity associated with a SIL value. |
| 97 | +enum class ActivityFlags : unsigned { |
| 98 | + /// The value depends on a function parameter. |
| 99 | + Varied = 1 << 1, |
| 100 | + /// The value contributes to a result. |
| 101 | + Useful = 1 << 2, |
| 102 | + /// The value is both varied and useful. |
| 103 | + Active = Varied | Useful, |
| 104 | +}; |
| 105 | + |
| 106 | +using Activity = OptionSet<ActivityFlags>; |
| 107 | + |
| 108 | +/// Result of activity analysis on a function. Accepts queries for whether a |
| 109 | +/// value is "varied", "useful" or "active" against certain differentiation |
| 110 | +/// indices. |
| 111 | +class DifferentiableActivityInfo { |
| 112 | +private: |
| 113 | + DifferentiableActivityCollection &parent; |
| 114 | + |
| 115 | + /// The derivative generic signature. |
| 116 | + GenericSignature derivativeGenericSignature; |
| 117 | + |
| 118 | + /// Input values, i.e. parameters (both direct and indirect). |
| 119 | + SmallVector<SILValue, 4> inputValues; |
| 120 | + /// Output values, i.e. individual values (not the final tuple) being returned |
| 121 | + /// by the `return` instruction. |
| 122 | + SmallVector<SILValue, 4> outputValues; |
| 123 | + |
| 124 | + /// The set of varied variables, indexed by the corresponding independent |
| 125 | + /// value (input) index. |
| 126 | + SmallVector<SmallDenseSet<SILValue>, 4> variedValueSets; |
| 127 | + /// The set of useful variables, indexed by the corresponding dependent value |
| 128 | + /// (output) index. |
| 129 | + SmallVector<SmallDenseSet<SILValue>, 4> usefulValueSets; |
| 130 | + |
| 131 | + /// The original function. |
| 132 | + SILFunction &getFunction() const; |
| 133 | + |
| 134 | + /// Returns true if the given SILValue has a tangent space. |
| 135 | + bool hasTangentSpace(SILValue value) { |
| 136 | + auto type = value->getType().getASTType(); |
| 137 | + // Remap archetypes in the derivative generic signature, if it exists. |
| 138 | + if (derivativeGenericSignature && type->hasArchetype()) { |
| 139 | + type = derivativeGenericSignature->getCanonicalTypeInContext( |
| 140 | + type->mapTypeOutOfContext()); |
| 141 | + } |
| 142 | + // Look up conformance in the current module. |
| 143 | + auto lookupConformance = |
| 144 | + LookUpConformanceInModule(getFunction().getModule().getSwiftModule()); |
| 145 | + return type->getAutoDiffTangentSpace(lookupConformance).hasValue(); |
| 146 | + } |
| 147 | + |
| 148 | + /// Perform analysis and populate variedness and usefulness sets. |
| 149 | + void analyze(DominanceInfo *di, PostDominanceInfo *pdi); |
| 150 | + |
| 151 | + /// Marks the given value as varied and propagates variedness to users. |
| 152 | + void setVariedAndPropagateToUsers(SILValue value, |
| 153 | + unsigned independentVariableIndex); |
| 154 | + /// Propagates variedness from the given operand to its user's results. |
| 155 | + void propagateVaried(Operand *operand, unsigned independentVariableIndex); |
| 156 | + /// Marks the given value as varied and recursively propagates variedness |
| 157 | + /// inwards (to operands) through projections. Skips `@noDerivative` field |
| 158 | + /// projections. |
| 159 | + void |
| 160 | + propagateVariedInwardsThroughProjections(SILValue value, |
| 161 | + unsigned independentVariableIndex); |
| 162 | + |
| 163 | + /// Marks the given value as useful for the given dependent variable index. |
| 164 | + void setUseful(SILValue value, unsigned dependentVariableIndex); |
| 165 | + /// Marks the given value as useful and recursively propagates usefulness to: |
| 166 | + /// - Defining instruction operands, if the value has a defining instruction. |
| 167 | + /// - Incoming values, if the value is a basic block argument. |
| 168 | + void setUsefulAndPropagateToOperands(SILValue value, |
| 169 | + unsigned dependentVariableIndex); |
| 170 | + /// Propagates usefulnesss to the operands of the given instruction. |
| 171 | + void propagateUseful(SILInstruction *inst, unsigned dependentVariableIndex); |
| 172 | + /// Marks the given address or class-typed value as useful and recursively |
| 173 | + /// propagates usefulness inwards (to operands) through projections. Skips |
| 174 | + /// `@noDerivative` field projections. |
| 175 | + void propagateUsefulThroughAddress(SILValue value, |
| 176 | + unsigned dependentVariableIndex); |
| 177 | + /// If the given value is an `array.uninitialized_intrinsic` application, |
| 178 | + /// selectively propagate usefulness through its `RawPointer` result. |
| 179 | + void setUsefulThroughArrayInitialization(SILValue value, |
| 180 | + unsigned dependentVariableIndex); |
| 181 | + |
| 182 | +public: |
| 183 | + explicit DifferentiableActivityInfo( |
| 184 | + DifferentiableActivityCollection &parent, |
| 185 | + GenericSignature derivativeGenericSignature); |
| 186 | + |
| 187 | + /// Returns true if the given value is varied for the given independent |
| 188 | + /// variable index. |
| 189 | + bool isVaried(SILValue value, unsigned independentVariableIndex) const; |
| 190 | + |
| 191 | + /// Returns true if the given value is varied for any of the given parameter |
| 192 | + /// (independent variable) indices. |
| 193 | + bool isVaried(SILValue value, IndexSubset *parameterIndices) const; |
| 194 | + |
| 195 | + /// Returns true if the given value is useful for the given dependent variable |
| 196 | + /// index. |
| 197 | + bool isUseful(SILValue value, unsigned dependentVariableIndex) const; |
| 198 | + |
| 199 | + /// Returns true if the given value is active for the given |
| 200 | + /// `SILAutoDiffIndices` (parameter indices and result index). |
| 201 | + bool isActive(SILValue value, const SILAutoDiffIndices &indices) const; |
| 202 | + |
| 203 | + /// Returns the activity of the given value for the given `SILAutoDiffIndices` |
| 204 | + /// (parameter indices and result index). |
| 205 | + Activity getActivity(SILValue value, const SILAutoDiffIndices &indices) const; |
| 206 | + |
| 207 | + /// Prints activity information for the `indices` of the given `value`. |
| 208 | + void dump(SILValue value, const SILAutoDiffIndices &indices, |
| 209 | + llvm::raw_ostream &s = llvm::dbgs()) const; |
| 210 | + |
| 211 | + /// Prints activity information for the given `indices`. |
| 212 | + void dump(SILAutoDiffIndices indices, |
| 213 | + llvm::raw_ostream &s = llvm::dbgs()) const; |
| 214 | +}; |
| 215 | + |
| 216 | +class DifferentiableActivityCollection { |
| 217 | +public: |
| 218 | + SmallDenseMap<GenericSignature, DifferentiableActivityInfo> activityInfoMap; |
| 219 | + SILFunction &function; |
| 220 | + DominanceInfo *domInfo; |
| 221 | + PostDominanceInfo *postDomInfo; |
| 222 | + |
| 223 | + DifferentiableActivityInfo & |
| 224 | + getActivityInfo(GenericSignature assocGenSig, |
| 225 | + AutoDiffDerivativeFunctionKind kind) { |
| 226 | + auto activityInfoLookup = activityInfoMap.find(assocGenSig); |
| 227 | + if (activityInfoLookup != activityInfoMap.end()) |
| 228 | + return activityInfoLookup->getSecond(); |
| 229 | + auto insertion = activityInfoMap.insert( |
| 230 | + {assocGenSig, DifferentiableActivityInfo(*this, assocGenSig)}); |
| 231 | + return insertion.first->getSecond(); |
| 232 | + } |
| 233 | + |
| 234 | + explicit DifferentiableActivityCollection(SILFunction &f, DominanceInfo *di, |
| 235 | + PostDominanceInfo *pdi); |
| 236 | +}; |
| 237 | + |
| 238 | +} // end namespace swift |
| 239 | + |
| 240 | +#endif // SWIFT_SILOPTIMIZER_ANALYSIS_DIFFERENTIABLEACTIVITYANALYSIS_H_ |
0 commit comments