ExecutionPlan.h revision b26049114bc4c64e6bea3a5d5d129fcaec8e69b6
191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet/*
291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Copyright (C) 2017 The Android Open Source Project
391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet *
491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Licensed under the Apache License, Version 2.0 (the "License");
591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * you may not use this file except in compliance with the License.
691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * You may obtain a copy of the License at
791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet *
891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet *      http://www.apache.org/licenses/LICENSE-2.0
991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet *
1091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Unless required by applicable law or agreed to in writing, software
1191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * distributed under the License is distributed on an "AS IS" BASIS,
1291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * See the License for the specific language governing permissions and
1491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * limitations under the License.
1591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet */
1691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
1791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet// Classes used to plan how to execute a model across multiple devices.
1891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
1991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#ifndef ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H
2091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#define ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H
2191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
2291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "HalInterfaces.h"
2391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "Memory.h"
2491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "NeuralNetworks.h"
2591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "Utils.h"
2691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
278913ae3283de7752aed108c1b26aef1adacb049fDavid Gross#include <set>
288913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
2991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletnamespace android {
3091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletnamespace nn {
3191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
3291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass CompilationBuilder;
3391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass Device;
34b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Grossclass ExecutionBuilder;
358913ae3283de7752aed108c1b26aef1adacb049fDavid Grossclass ExecutionPlan;
3691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass Memory;
3791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ModelBuilder;
38b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Grossclass StepExecutor;
3991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
4091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ExecutionStep {
418913ae3283de7752aed108c1b26aef1adacb049fDavid Grossprivate:
428913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    typedef std::vector<std::pair<uint32_t, uint32_t>> RemapVectorType;
43b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    typedef std::set<std::pair<uint32_t, uint32_t>> SubModelOutputSetType;
448913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
4591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletpublic:
46f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    enum OperandKind { INPUT, OUTPUT };
47f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross
488913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    ExecutionStep(ExecutionPlan* plan,
498913ae3283de7752aed108c1b26aef1adacb049fDavid Gross                  uint32_t stepIndex,
508913ae3283de7752aed108c1b26aef1adacb049fDavid Gross                  std::shared_ptr<ModelBuilder> model,
518913ae3283de7752aed108c1b26aef1adacb049fDavid Gross                  std::shared_ptr<Device> device);
5291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    int addOperation(int operationIndex, const ModelBuilder& fromModel);
5391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    int addOperand(uint32_t fromOperandIndex, uint32_t* toOperandIndex,
54f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross                   const ModelBuilder& fromModel, OperandKind kind);
5591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
568913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    // Each vector entry is of the form (fromModel index, subModel index)
578913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    const RemapVectorType& getSubModelInputs() const {
588913ae3283de7752aed108c1b26aef1adacb049fDavid Gross        return mSubModelInputs;
598913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    }
608913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
61b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    size_t countSubModelOutputs() const;
62b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
638913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    void recordSubModelOutput(uint32_t fromModelIndex) {
648913ae3283de7752aed108c1b26aef1adacb049fDavid Gross        const auto it = mOperandMap.find(fromModelIndex);
658913ae3283de7752aed108c1b26aef1adacb049fDavid Gross        nnAssert(it != mOperandMap.end());
668913ae3283de7752aed108c1b26aef1adacb049fDavid Gross        mSubModelOutputs.insert(std::make_pair(fromModelIndex, it->second));
678913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    }
688913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
69b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // If this step has a submodel output of unknown size, sets
70b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // *hasOutputOfUnknownSize to true; otherwise, leaves it
71b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // unchanged.
72b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    int finishSubModel(bool* hasOutputOfUnknownSize);
738913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
748913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    void dump() const;
7591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletprivate:
76f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // TODO: Some of the data is working state information that
77f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // shouldn't be needed after we've constructed but not executed
78f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // the step.
79f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross
808913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    ExecutionPlan* mPlan;
818913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    uint32_t mIndex;  // index of step within plan
8291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    std::shared_ptr<ModelBuilder> mSubModel;
838913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    std::shared_ptr<Device> mDevice;  // nullptr signifies CPU
841f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    sp<IPreparedModel> mPreparedSubModel;  // not used for CPU
85f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross
86f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // Inputs of original model that are also inputs of this submodel:
87f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    //     (fromModel index, subModel index)
888913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    RemapVectorType mModelInputs;
89f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // Outputs of original model that are also outputs of this submodel:
90f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    //     (fromModel index, subModel index)
918913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    RemapVectorType mModelOutputs;
92f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    // Temporaries of original model that are inputs of this submodel:
93f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross    //     (fromModel index, subModel index)
948913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    RemapVectorType mSubModelInputs;
958913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    // Temporaries of original model that are outputs of this submodel:
968913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    //     (fromModel index, subModel index)
97b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    SubModelOutputSetType mSubModelOutputs;
9891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    // Converts operand indexes from the main model to the submodel.
9991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    std::unordered_map<uint32_t, uint32_t> mOperandMap;
10091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet};
10191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
10291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ExecutionPlan {
1038913ae3283de7752aed108c1b26aef1adacb049fDavid Grosspublic:
1041f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    ExecutionPlan(const ExecutionPlan&) = delete;
1051f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    ExecutionPlan& operator=(const ExecutionPlan&) = delete;
1068913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1071f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    ExecutionPlan() { }
1081f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    ~ExecutionPlan() { delete mBody; }
1091f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
110b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // Controller is part of the interface to a mechanism for
111b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // performing an execution in N steps.
112b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    //
113b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // Usage pattern:
114b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // - Instantiate Controller with ExecutionPlan::makeController().
115b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // - Call ExecutionPlan::next() on Controller N+1 times.  The first N times,
116b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    //   *executor is set to point to a new StepExecutor corresponding
117b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    //   to that step.  The N+1st time, *executor is set to nullptr,
118b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    //   signifying there are no more steps.
119b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // - If ExecutionPlan::next() returns anything other than ANEURALNETWORKS_NO_ERROR,
120b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    //   a problem has occurred.
121b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    class Controller {
122b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        friend class ExecutionPlan;
123b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    private:
124b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        static const size_t kBadStepIndex = ~size_t(0);
125b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
126b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        Controller(const ExecutionPlan* plan, const ExecutionBuilder* executionBuilder) :
127b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross                mPlan(plan), mExecutionBuilder(executionBuilder), mNextStepIndex(0) {}
128b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        Controller() {}  // used for error state
129b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
130b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        const ExecutionPlan* mPlan = nullptr;
131b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        const ExecutionBuilder* mExecutionBuilder = nullptr;
132b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        size_t mNextStepIndex = kBadStepIndex;
133b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    };
134b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
135b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    Controller makeController(const ExecutionBuilder* executionBuilder) const;
136b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
137b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    int next(Controller* controller, std::shared_ptr<StepExecutor>* executor) const;
138b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
1391f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    std::shared_ptr<ExecutionStep> createNewStep(const std::shared_ptr<Device> device);
1401f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1411f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    void becomeSingleStep(const std::shared_ptr<Device> device,
1421f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross                          const ModelBuilder* model);
1431f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1441f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    int finish();
1458913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1468913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    void recordTemporaryDef(uint32_t fromModelIndex, uint32_t stepIndex) {
1471f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        auto& temporaryToDefiningStep = compound()->mTemporaryToDefiningStep;
1481f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        nnAssert(temporaryToDefiningStep.count(fromModelIndex) == 0);
1491f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        temporaryToDefiningStep.insert(std::make_pair(fromModelIndex, stepIndex));
15091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet    }
1518913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1528913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    void dump() const;
1538913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1541f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    // TODO: This member function is only temporary, until we finish
1551f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    // fully integrating ExecutionPlan with the compilation and
156b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // execution phases of the NN API.
1571f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    //
158b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // Returns true if the plan is "in scope for execution" -- i.e.,
159b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // the structure of the plan is such that the
160b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // currently-implemented execution system ought to be able to
161b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // handle it.  May return true even if something went wrong with
162b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // the partitioning and compilation process.
1631f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    //
164b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // true - single partition (even if compilation failed)
165b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    // false - multiple partitions
166b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross    bool shouldBeExecutable() const;
1671f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1688913ae3283de7752aed108c1b26aef1adacb049fDavid Grossprivate:
1698913ae3283de7752aed108c1b26aef1adacb049fDavid Gross    void findSubModelOutputs();
1708913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1711f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    struct Body {
1721f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        virtual ~Body() {}
1731f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        virtual void dump() const = 0;
1741f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        virtual int finish() = 0;
1751f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    };
1761f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1771f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    struct SimpleBody : Body {
1781f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        SimpleBody(std::shared_ptr<Device> device, const ModelBuilder* model) :
1791f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross                mDevice(device), mModel(model) {}
1808913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1811f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        void dump() const override;
1821f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        int finish() override;
1838913ae3283de7752aed108c1b26aef1adacb049fDavid Gross
1841f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        std::shared_ptr<Device> mDevice;  // nullptr signifies CPU
1851f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        const ModelBuilder* mModel;
1861f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        sp<IPreparedModel> mPreparedModel;  // not used for CPU
1871f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        bool mSuccessfulFinish = false;
1881f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    };
1891f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1901f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    struct CompoundBody : Body {
1911f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        void dump() const override;
1921f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        int finish() override;
1931f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1941f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        // TODO: Some of the data is working state information that
1951f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        // shouldn't be needed after we've constructed but not
1961f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        // executed the plan.
1971f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
1981f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        std::vector<std::shared_ptr<ExecutionStep>> mSteps;
1991f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
2001f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        // Map from original operand index to defining step index.
2011f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        // Used for all (and only) TEMPORARY_VARIABLEs.
2021f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        std::unordered_map<uint32_t, uint32_t> mTemporaryToDefiningStep;
2031f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
204b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        // Total number of submodel outputs across all steps.
205b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        size_t mSubModelOutputCount = 0;
206b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross
207b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross        bool mHasSubModelOutputOfUnknownSize = true;
2081f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    private:
2091f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        void findSubModelOutputs();
2101f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    };
2111f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross
2121f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    enum { EMPTY, SIMPLE, COMPOUND } mState = EMPTY;
2131f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    Body* mBody = nullptr;
2141f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    CompoundBody* compound() {
2151f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        nnAssert(mState == COMPOUND);
2161f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross        return static_cast<CompoundBody*>(mBody);
2171f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross    }
21891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet};
21991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
22091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet}  // namespace nn
22191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet}  // namespace android
22291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet
22391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#endif  // ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H
224