/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Classes used to plan how to execute a model across multiple devices. #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H #include #include #include #include #include #include #include #include #include #include #include #include #include "HalInterfaces.h" #include "Memory.h" #include "ModelArgumentInfo.h" #include "ModelBuilder.h" #include "NeuralNetworks.h" #include "TokenHasher.h" #include "Utils.h" namespace android { namespace nn { class BurstBuilder; class CompilationBuilder; class Device; class ExecutionBuilder; class ExecutionBurstController; class ExecutionPlan; class Memory; class PreparedModel; class StepExecutor; struct ConstantReferenceLocation; // NNAPI Control Flow allows referring to an NNAPI model inside another NNAPI // model using OperandType::SUBGRAPH. For example, an IF operation within a // model mey refer to two other models corresponding to then and else branches. // // The partitioning process transforms this nested representation into a list // of LogicalSteps. // // The following terms are used: // - The main model is the top-level model being compiled (not referenced by any // OperandType::SUBGRAPH operand within the compilation). // - A referenced model is a non-top-level model being compiled (referenced by // at least one OperandType::SUBGRAPH operand within the set of models being // compiled). // - A source model is either the main model or a referenced model. // - A step model is a model excerpted from a source model during the // partitioning process. // - A partition is a LogicalStep representing at least one operation of a // source model. In particular, ExecutionStep represents a step model, IfStep // represents an IF operation, WhileStep represents a WHILE operation. // A GotoStep is not a partition. // - A partition boundary operand is a source model operand that is an input or // output of a partition. For ExecutionStep, the inputs and outputs of the // step model are boundary operands; for IfStep and WhileStep, the inputs and // outputs of the corresponding operation are boundary operands. // // Referenced models can be sources of parition boundary operands. For example, // this happens when a referenced model is paritioned into one or more // LogicalSteps. // // (model index, operand index within model) typedef std::pair SourceOperandIndex; // A collection of source models. class SourceModels { public: uint32_t addModel(const ModelBuilder* model) { uint32_t modelIndex = mModels.size(); mModels.push_back(model); return modelIndex; } const ModelBuilder* getModel(uint32_t index) const { return mModels[index]; } uint32_t size() const { return mModels.size(); } private: std::vector mModels; }; // An excerpt of a source model to be run by a specific device. class ExecutionStep { public: typedef std::vector> RemapVectorType; typedef std::set> StepModelOutputSetType; enum OperandKind { INPUT, OUTPUT }; ExecutionStep(ExecutionPlan* plan, uint32_t stepIndex, uint32_t sourceModelIndex, std::shared_ptr device); int addOperation(int operationIndex); int addOperand(uint32_t sourceOperandIndex, uint32_t* stepOperandIndex, OperandKind kind); // Each container entry is of the form (source model operand index, step model operand index) const RemapVectorType& getModelInputs() const { return mModelInputs; } const RemapVectorType& getModelOutputs() const { return mModelOutputs; } const RemapVectorType& getTempsAsStepModelInputs() const { return mTempsAsStepModelInputs; } const StepModelOutputSetType& getTempsAsStepModelOutputs() const { return mTempsAsStepModelOutputs; } const RemapVectorType& getOutputsAsStepModelInputs() const { return mOutputsAsStepModelInputs; } const std::vector& getInputIndexStepModelToMainModel() const { return mInputIndexStepModelToMainModel; } const std::vector& getOutputIndexStepModelToMainModel() const { return mOutputIndexStepModelToMainModel; } const std::vector& getOutputsAsStepModelInputsIndexToMainModel() const { return mOutputsAsStepModelInputsIndexToMainModel; } uint32_t getSourceModelIndex() const { return mSourceModelIndex; } void recordTempAsStepModelOutput(uint32_t stepOperandIndex); // If this step has a step model output of unknown size, sets // *hasOutputOfUnknownSize to true; otherwise, leaves it // unchanged. int finishStepModel(const ModelBuilder* mainModel, bool* hasOutputOfUnknownSize, int32_t executionPreference, int32_t priority); const ModelBuilder* getStepModel() const { return &mStepModel; } std::shared_ptr getDevice() const { return mDevice; } // only available after calling finishStepModel() std::shared_ptr getPreparedStepModel() const { return mPreparedStepModel; } // Map inputs and outputs from ExecutionBuilder to StepExecutor. // // This method only reads map entries for which the first element of // SourceOperandIndex is mSourceModelIndex. void mapInputsAndOutputs( std::shared_ptr stepExecutor, const Memory* temporaryMemory, const std::map& sourceOperandToOffsetOfTemporary, const std::map& sourceOperandToInputIndex, const std::map& sourceOperandToOutputIndex, const std::map& sourceOperandToConstantReference) const; void dump() const; // For test only, get the transformed cache token. const uint8_t* forTest_getCacheToken() const { return mToken.getCacheToken(); } private: void logStepModel() const; const ModelBuilder* getSourceModel() const; // TODO: Some of the data is working state information that // shouldn't be needed after we've constructed but not executed // the step. ExecutionPlan* mPlan; uint32_t mIndex; // index of step within plan uint32_t mSourceModelIndex; ModelBuilder mStepModel; // An excerpt of a source model to be run by one device. std::shared_ptr mDevice; std::shared_ptr mPreparedStepModel; // All inputs of this step model: // (source model operand index, step model operand index) // // Depending on whether the source operand is an input or output of the main // model, the memory should be mapped using // ExecutionPlan::CompoundBody::mSourceOperandToInputIndex, // ExecutionPlan::Controller::mSourceOperandToOffsetOfTemporary, or // ExecutionPlan::CompoundBody::mSourceOperandToOutputIndex. RemapVectorType mStepModelInputs; // All outputs of this step model: // (source model operand index, step model operand index) // // Depending on whether the source operand is an output of the main model, // the memory should be mapped using // ExecutionPlan::CompoundBody::mSourceOperandToOutputIndex or // ExecutionPlan::Controller::mSourceOperandToOffsetOfTemporary. // // mOutputIndexStepModelToMainModel relies on mModelOutputs being a prefix of // mStepModelOutputs. RemapVectorType mStepModelOutputs; // Inputs of main model that are also inputs of this step model: // (main model operand index, step model operand index) RemapVectorType mModelInputs; // Outputs of main model that are also outputs of this step model: // (main model operand index, step model operand index) RemapVectorType mModelOutputs; // Temporaries of source model that are inputs of this step model: // (source model operand index, step model operand index) RemapVectorType mTempsAsStepModelInputs; // Temporaries of source model that are outputs of this step model: // (source model operand index, step model operand index) StepModelOutputSetType mTempsAsStepModelOutputs; // Outputs of main model that are inputs of this step model: // (main model operand index, step model operand index) RemapVectorType mOutputsAsStepModelInputs; // Converts operand indexes from the source model to the step model. std::unordered_map mOperandMap; // Converts input indexes from the step model to the main model // (these are input indexes, not operand indexes). This vector // only describes inputs of the step model that are also inputs of // the main model -- that is, mModelInputs but not mTempsAsStepModelInputs. std::vector mInputIndexStepModelToMainModel; // Converts output indexes from the step model to the main model // (these are output indexes, not operand indexes). This vector // only describes outputs of the step model that are also outputs of // the main model -- that is, mModelOutputs but not // mTempsAsStepModelOutputs. std::vector mOutputIndexStepModelToMainModel; // Converts indexes into mOutputsAsStepModelInputs to indexes into // main model outputs (these are input and output indexes, not // operand indexes). To be specific, if the main model outputs // are mainModelOutputs, // // mOutputsAsStepModelInputsIndexToMainModel.size() == // mOutputsAsStepModelInputs.size() // // and when (0 <= i < mOutputsAsStepModelInputs.size()), // // mainModelOutputs[mOutputsAsStepModelInputsIndexToMainModel[i]] == // mOutputsAsStepModelInputs[i].first std::vector mOutputsAsStepModelInputsIndexToMainModel; // The compilation caching token. TokenHasher mToken; }; // An IF operation to be run on the ExecutionPlan::next() interpreter. The // branch models might run on devices. See LogicalStep. // // Execution plan structure: // Index Step // i if then=(i + 1) else=(j + 1) // ... (then model steps) // j goto k // ... (else model steps) // k (steps after the IF) struct IfStep { // The index of this step. size_t index = ~size_t(0); // The index of the first step of the "then" branch. size_t thenStepIndex = ~size_t(0); // The index of the first step of the "else" branch. size_t elseStepIndex = ~size_t(0); // The boolean condition input of the IF operation. The value of this // operand determines the branch of the IF operation to be executed. SourceOperandIndex conditionOperandIndex = {~uint32_t(0), ~uint32_t(0)}; // Input operands of the IF operation to be passed to a branch model. std::vector outerInputOperands; // Output operands of the IF operation. std::vector outerOutputOperands; // Input operands of the "then" branch model. std::vector thenBranchInputOperands; // Output operands of the "then" branch model. std::vector thenBranchOutputOperands; // Input operands of the "else" branch model. std::vector elseBranchInputOperands; // Output operands of the "else" branch model. std::vector elseBranchOutputOperands; }; // A WHILE operation to be run on the ExecutionPlan::next() interpreter. The // condition and body models might run other devices. See LogicalStep. // // Execution plan structure: // Index Step // i while cond=(i + 1) body=(j + 1) exit=(k + 1) // ... (cond model steps) // j goto i // ... (body model steps) // k goto i // ... (steps after the WHILE) // // Note that WhileStep has WhileState associated with it. struct WhileStep { // The index of this step. size_t index = ~size_t(0); // The index of the first step of the condition model. size_t condStepIndex = ~size_t(0); // The index of the first step of the body model. size_t bodyStepIndex = ~size_t(0); // The index of the first step after the loop. size_t exitStepIndex = ~size_t(0); // Input operands of the WHILE operation to be passed to the condition and // body models. std::vector outerInputOperands; // Output operands of the WHILE operation. std::vector outerOutputOperands; // Input operands of the condition model. std::vector condInputOperands; // Output operand of the condition model. The value of this operand // determines whether to continue execution or exit the loop. SourceOperandIndex condOutputOperand = {~uint32_t(0), ~uint32_t(0)}; // Input operands of the body model. std::vector bodyInputOperands; // Output operands of the body model. std::vector bodyOutputOperands; }; // A helper step. See LogicalStep. struct GotoStep { // The index of this step. size_t index = ~size_t(0); // The index of the step to go to. size_t gotoStepIndex = ~size_t(0); }; // One of ExecutionStep, IfStep, WhileStep, or GotoStep. // // When ExecutionPlan::next() is called, it interprets logical steps until it // encounters an ExecutionStep ("interpreted execution"). // - For an IfStep, it decides which branch to take and proceeds to the // corresponding step. // - For a WhileStep, it decides whether to execute the condition or body (based // on WhileState), or exit the loop (based on the condition model output), and // proceeds to the corresponding step. // - For a GotoStep, it proceeds to the indicated step unconditionally. class LogicalStep { public: template explicit LogicalStep(Args&&... args) : mStep(std::forward(args)...) {} bool isExecution() const { return std::holds_alternative(mStep); } bool isIf() const { return std::holds_alternative(mStep); } bool isWhile() const { return std::holds_alternative(mStep); } bool isGoto() const { return std::holds_alternative(mStep); } // Returns a non-null pointer or crashes. ExecutionStep* executionStep() { return &std::get(mStep); } IfStep* ifStep() { return &std::get(mStep); } WhileStep* whileStep() { return &std::get(mStep); } GotoStep* gotoStep() { return &std::get(mStep); } // Returns a non-null pointer or crashes. const ExecutionStep* executionStep() const { return &std::get(mStep); } const IfStep* ifStep() const { return &std::get(mStep); } const WhileStep* whileStep() const { return &std::get(mStep); } const GotoStep* gotoStep() const { return &std::get(mStep); } // May return nullptr. ExecutionStep* tryExecutionStep() { return std::get_if(&mStep); } IfStep* tryIfStep() { return std::get_if(&mStep); } WhileStep* tryWhileStep() { return std::get_if(&mStep); } GotoStep* tryGotoStep() { return std::get_if(&mStep); } // May return nullptr. const ExecutionStep* tryExecutionStep() const { return std::get_if(&mStep); } const IfStep* tryIfStep() const { return std::get_if(&mStep); } const WhileStep* tryWhileStep() const { return std::get_if(&mStep); } const GotoStep* tryGotoStep() const { return std::get_if(&mStep); } void dump() const; private: std::variant mStep; }; std::string toString(const IfStep& step); std::string toString(const WhileStep& step); std::string toString(const GotoStep& step); // Describes the state of WhileStep. struct WhileState { // A pseudo iteration number indicating the loop is not being executed. static constexpr uint64_t kOutsideLoop = ~uint64_t(0); // Whether we need to evaluate the condition or body next. enum Stage { EVALUATE_CONDITION, EVALUATE_BODY } stage = EVALUATE_CONDITION; // Current iteration number. Must be set to kOutsideLoop when exiting the // loop. uint64_t iteration = kOutsideLoop; // Time point when the loop started executing. std::chrono::time_point startTime; }; struct ConstantCopyLocation { const uint8_t* buffer; uint32_t length; }; struct ConstantReferenceLocation { const Memory* memory; uint32_t offset; uint32_t length; }; class ExecutionPlan { public: ExecutionPlan(const ExecutionPlan&) = delete; ExecutionPlan& operator=(const ExecutionPlan&) = delete; ExecutionPlan() {} ~ExecutionPlan() { delete mBody; } // Controller is part of the interface to a mechanism for performing an // execution in N steps. // // The value of N may not be known beforehand if the model contains WHILE // loops. See LogicalStep. // // Usage pattern: // - Instantiate Controller with ExecutionPlan::makeController(). // - Call ExecutionPlan::next() on Controller N+1 times. The first N times, // *executor is set to point to a new StepExecutor corresponding // to that step. The N+1st time, *executor is set to nullptr, // signifying there are no more steps. // - If ExecutionPlan::next() returns anything other than ANEURALNETWORKS_NO_ERROR, // a problem has occurred. class Controller { friend class ExecutionPlan; private: Controller(const Controller&) = delete; Controller& operator=(const Controller&) = delete; static const size_t kBadStepIndex = ~size_t(0); // A constructor for mState == SIMPLE. Controller(const ExecutionPlan* plan, ExecutionBuilder* executionBuilder, const BurstBuilder* burstBuilder); // A constructor for mState == COMPOUND. Controller(const ExecutionPlan* plan, ExecutionBuilder* executionBuilder, const BurstBuilder* burstBuilder, uint32_t totalSizeOfTemporaries, std::map sourceOperandToOffsetOfTemporary, std::map sourceOperandToOffsetOfTemporary2, std::map sourceOperandToInputIndex, std::map sourceOperandToOutputIndex, const std::map& sourceOperandToConstantCopy, std::map sourceOperandToConstantReference); // Sets the location of innerOperand to be the same as the location of outerOperand. void setInput(const SourceOperandIndex& outerOperand, const SourceOperandIndex& innerOperand); void setOutput(const SourceOperandIndex& outerOperand, const SourceOperandIndex& innerOperand); // Wait for mLastStepSyncFd to signal. // No-op if mLastStepSyncFd is -1 which the mLastStepSyncFd is initialized to. // mLastStepSyncFd will also be set to -1 when the most recently processed step // does not generate a sync fence. int waitForLastStepSyncFence() const; const ExecutionPlan* mPlan; ExecutionBuilder* mExecutionBuilder; const BurstBuilder* mBurstBuilder; // Map from source operand index to an offset into mTemporaries used // to represent that operand as an inter-partition input or output. // // The four maps // - mSourceOperandToOffsetOfTemporary // - mSourceOperandToInputIndex // - mSourceOperandToOutputIndex // - mSourceOperandToConstantReference // are initialized from similarly named fields of ExecutionPlan::CompoundBody. // // A particular key appears in at most one map at any given time. This // restriction does not apply to mSourceOperandToOffsetOfTemporary2. // // The maps are modified during the execution of IfStep and WhileStep. // See ExecutionPlan::nextCompound(). std::map mSourceOperandToOffsetOfTemporary; // Map from source operand index to an additional offset into // mTemporaries used for double buffering of WHILE loop output operands. std::map mSourceOperandToOffsetOfTemporary2; // Map from source operand index to an input index of the main model. std::map mSourceOperandToInputIndex; // Map from source operand index to an output index of the main model. std::map mSourceOperandToOutputIndex; // Map from source operand index to a constant reference location. // Used for WHILE loop operand initializers that are constant references. std::map mSourceOperandToConstantReference; std::unique_ptr mTemporaries; // Index of the next step to be processed by ExecutionPlan::next(). size_t mNextStepIndex; // The value to reset mNextStepIndex to for partial CPU fallback. size_t mFallbackNextStepIndex; // Map from WhileStep index to the associated WhileState. std::unordered_map mWhileState; // The sync fence fd of the last step. int mLastStepSyncFd; }; std::vector> makeBursts(int preference) const; std::shared_ptr makeController(ExecutionBuilder* executionBuilder, const BurstBuilder* burstBuilder) const; // Sets up a new StepExecutor and burstController (if applicable) if there // is a step to execute. See ExecutionPlan::Controller. // Handles control flow. See LogicalStep. // syncFdOfLastStep is the sync fence fd generated by the most recently processed step. int next(std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController = nullptr, int syncFdOfLastStep = -1) const; // Create the same executor as the last one created by next(). int fallback(std::shared_ptr controller, std::shared_ptr* executor) const; ExecutionStep* createNewExecutionStep(uint32_t sourceModelIndex, const std::shared_ptr device); IfStep* createNewIfStep(); WhileStep* createNewWhileStep(); GotoStep* createNewGotoStep(); // Only legal to call when mState == COMPOUND. size_t getNextStepIndex() const { return compound()->mSteps.size(); } void becomeSingleStep(const std::shared_ptr device, const ModelBuilder* model); int finish(int32_t executionPreference, int32_t priority, const std::optional& deadline); void recordTemporaryDef(SourceOperandIndex sourceOperandIndex, uint32_t stepIndex); void dump() const; void reset(); bool isValid() const { return mState != EMPTY && mBody != nullptr && mBody->mSuccessfulFinish; } bool isSimple() const { return mState == SIMPLE; } bool isSimpleCpu() const; void setCaching(const std::string* cacheDir, const uint8_t* token) { mCacheDir = cacheDir; mToken = token; } const std::string* getCacheDir() const { return mCacheDir; } const uint8_t* getCacheToken() const { return mToken; } // The caller is responsible for making sure the index is not out of range. void forEachStepRoleOfInput(uint32_t index, const StepRoleCallback& callback) const { CHECK(mBody != nullptr); mBody->forEachStepRoleOfInput(index, callback); } void forEachStepRoleOfOutput(uint32_t index, const StepRoleCallback& callback) const { CHECK(mBody != nullptr); mBody->forEachStepRoleOfOutput(index, callback); } SourceModels& getSourceModels() { return mSourceModels; } const SourceModels& getSourceModels() const { return mSourceModels; } // These functions are solely intended for use by unit tests of // the partitioning algorithm. enum class Kind { ERROR, EMPTY, SIMPLE, COMPOUND }; // See operator<< defined outside this class Kind forTest_getKind() const; std::shared_ptr forTest_simpleGetDevice() const; const std::vector>& forTest_compoundGetSteps() const; bool forTest_hasStepModelOutputsOfUnknownSize() const; const uint8_t* forTest_simpleGetCacheToken() const; private: // Becomes a new COMPOUND step if mState == EMPTY, otherwise does nothing. // Illegal to call for when mState == SIMPLE. void becomeCompoundIfEmpty(); void findTempsAsStepModelOutputs(); class Buffer { public: Buffer(void* pointer, uint32_t size); Buffer(RunTimePoolInfo info, uint32_t offset); void* getPointer() const; uint32_t getSize() const; void flush() const; private: RunTimePoolInfo mInfo; uint32_t mOffset; }; // Returns the buffer associated with a partition boundary operand. std::optional getBuffer(std::shared_ptr controller, SourceOperandIndex operandIndex) const; std::optional getBufferFromModelArgumentInfo( const ModelArgumentInfo& info, const ExecutionBuilder* executionBuilder) const; // Reads the value of a partition boundary boolean condition operand. int readConditionValue(std::shared_ptr controller, SourceOperandIndex operandIndex, bool* value) const; // Handles control flow. See LogicalStep. int nextCompound(std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController) const; int nextCompound(const ExecutionStep* step, std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController) const; int nextCompound(const IfStep* step, std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController) const; int nextCompound(const WhileStep* step, std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController) const; int nextCompound(const GotoStep* step, std::shared_ptr controller, std::shared_ptr* executor, std::shared_ptr* burstController) const; struct Body { virtual ~Body() {} virtual void dump() const = 0; virtual int finish(const SourceModels* sourceModels, int32_t executionPreference, int32_t priority, const std::optional& deadline) = 0; virtual bool hasStepModelOutputsOfUnknownSize() const = 0; virtual void forEachStepRoleOfInput(uint32_t index, const StepRoleCallback& callback) const = 0; virtual void forEachStepRoleOfOutput(uint32_t index, const StepRoleCallback& callback) const = 0; bool mSuccessfulFinish = false; }; struct SimpleBody : Body { SimpleBody(std::shared_ptr device, const ModelBuilder* model, const std::string* cacheDir, const uint8_t* token) : mDevice(device), mModel(model), mCacheDir(cacheDir), mToken(token) {} void dump() const override; int finish(const SourceModels* sourceModels, int32_t executionPreference, int32_t priority, const std::optional& deadline) override; bool hasStepModelOutputsOfUnknownSize() const override { return false; } void forEachStepRoleOfInput(uint32_t index, const StepRoleCallback& callback) const override; void forEachStepRoleOfOutput(uint32_t index, const StepRoleCallback& callback) const override; std::shared_ptr mDevice; const ModelBuilder* mModel; std::shared_ptr mPreparedModel; const std::string* mCacheDir; TokenHasher mToken; }; struct CompoundBody : Body { void dump() const override; int finish(const SourceModels* sourceModels, int32_t executionPreference, int32_t priority, const std::optional& deadline) override; bool hasStepModelOutputsOfUnknownSize() const override { return mHasStepModelOutputOfUnknownSize; } void forEachStepRoleOfInput(uint32_t index, const StepRoleCallback& callback) const override; void forEachStepRoleOfOutput(uint32_t index, const StepRoleCallback& callback) const override; // TODO: Some of the data is working state information that // shouldn't be needed after we've constructed but not // executed the plan. std::vector> mSteps; // Map from source operand index to defining ExecutionStep index. // Used for all (and only) TEMPORARY_VARIABLEs that are defined by // ExecutionSteps. Those defined by IfSteps and WhileSteps are not in // the map. std::map mTemporaryToDefiningExecutionStep; // Map from source operand index to input index of the main model. // This map only contains SUBGRAPH_INPUTs of the main model and is used // to initialize ExecutionPlan::Controller::mSourceOperandToInputIndex; std::map mSourceOperandToInputIndex; // Map from source operand index to output index of the main model. // This map only contains SUBGRAPH_OUTPUTs of the main model and is used // to initialize ExecutionPlan::Controller::mSourceOperandToOutputIndex; std::map mSourceOperandToOutputIndex; // Map from source operand index to location of a CONSTANT_COPY operand. // This map only contains constant partition boundary IF and WHILE // operands and is used to create a ExecutionPlan::Controller. std::map mSourceOperandToBoundaryConstantCopy; // Map from source operand index to location of a CONSTANT_REFERENCE // operand. This map only contains constant partition boundary IF and // WHILE operands and is used to initialize // ExecutionPlan::Controller::mSourceOperandToConstantReference. std::map mSourceOperandToBoundaryConstantReference; bool mHasStepModelOutputOfUnknownSize = false; private: void findTempsAsStepModelOutputs(); // Constant values that are inputs to IF and WHILE operations and lie on // a partition boundary ("control flow boundary constants") require // special treatment. We need to be able to dynamically associate those // values with the corresponding SUBGRAPH_INPUT operands in a referenced // model. // // For CONSTANT_COPY boundary operands, we copy those to temporary // memory and treat them similarly to TEMPORARY_VARIABLE operands in // Controller. // // For CONSTANT_REFERENCE boundary operands, we keep track of them in // ExecutionPlan::Controller::mSourceOperandToConstantReference. // // Note that for IF inputs and input-only WHILE inputs that are boundary // constants, we could embed those inside the referenced model, but we // currently don't do so. See b/148216514. void findControlFlowBoundaryConstants(const SourceModels* sourceModels); }; enum { EMPTY, SIMPLE, COMPOUND } mState = EMPTY; Body* mBody = nullptr; SimpleBody* simple() { CHECK(mState == SIMPLE); CHECK(mBody != nullptr); return static_cast(mBody); } const SimpleBody* simple() const { CHECK(mState == SIMPLE); CHECK(mBody != nullptr); return static_cast(mBody); } CompoundBody* compound() { CHECK(mState == COMPOUND); CHECK(mBody != nullptr); return static_cast(mBody); } const CompoundBody* compound() const { CHECK(mState == COMPOUND); CHECK(mBody != nullptr); return static_cast(mBody); } // Pointers to compilation caching information in CompilationBuilder. const std::string* mCacheDir = nullptr; const uint8_t* mToken = nullptr; SourceModels mSourceModels; }; inline std::ostream& operator<<(std::ostream& out, ExecutionPlan::Kind kind) { const int intKind = static_cast(kind); if (kind < ExecutionPlan::Kind::ERROR || kind > ExecutionPlan::Kind::COMPOUND) { return out << ""; } static const char* name[] = {"ERROR", "EMPTY", "SIMPLE", "COMPOUND"}; return out << name[intKind]; } } // namespace nn } // namespace android #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H