diff --git a/ethosu/regor/compiler/compiler.cpp b/ethosu/regor/compiler/compiler.cpp index 9b972fd5a3aaba38055c6798dabddc5051f8c294..85d73b0ee5598eec3a924a393cdb57113b5c1253 100644 --- a/ethosu/regor/compiler/compiler.cpp +++ b/ethosu/regor/compiler/compiler.cpp @@ -33,9 +33,6 @@ #include "tensor_allocator.hpp" #include "tflite/custom_operator_ethosu.hpp" #include "tflite/tflite_reader.hpp" -#include "tflite/tflite_supported_operators.hpp" -#include "tflite/tflite_supported_operators_u55.hpp" -#include "tflite/tflite_supported_operators_u85.hpp" #include "tflite/tflite_writer.hpp" #include "tosa/tosa_reader.hpp" @@ -55,24 +52,6 @@ END_ENUM_TABLE() namespace regor { -namespace -{ - -std::unique_ptr InitSupportedOpsChecker(const std::string &target, IArchitectureConstraints *constraints) -{ - if ( target == REGOR_ARCH_ETHOSU85 ) - { - return std::make_unique(constraints); - } - else - { - assert(target == REGOR_ARCH_ETHOSU55 || target == REGOR_ARCH_ETHOSU65); - return std::make_unique(constraints); - } -} - -} // namespace - Compiler::Compiler(std::unique_ptr &arch) { _architecture = std::move(arch); @@ -445,18 +424,9 @@ std::unique_ptr Compiler::CompileGraph(std::unique_ptr &graph, { if ( graph->Notation() == GraphNotation::TFLite ) { - // Run TFLite supported-operator checks - std::unique_ptr supportedOps; - _architecture->Call([&](const std::string &target) - { supportedOps = InitSupportedOpsChecker(target, _architecture->Constraints()); }); - - if ( supportedOps ) - { - supportedOps->Process(graph.get()); - } // Run GraphNotation::TFLite Preprocess/optimise step std::unique_ptr optimiser = GraphOptimiser::MakeGraphOptimiser( - GraphNotation::TFLite, _architecture->Constraints(), _graphOptimiserOptions, _optDb.get()); + GraphNotation::TFLite, _architecture.get(), _graphOptimiserOptions, _optDb.get()); if ( optimiser ) { optimiser->Process(graph.get()); @@ -465,7 +435,7 @@ std::unique_ptr Compiler::CompileGraph(std::unique_ptr &graph, // Run GraphNotation::GraphAPI Preprocess/optimise step std::unique_ptr optimiser = GraphOptimiser::MakeGraphOptimiser( - GraphNotation::GraphAPI, _architecture->Constraints(), _graphOptimiserOptions, _optDb.get()); + GraphNotation::GraphAPI, _architecture.get(), _graphOptimiserOptions, _optDb.get()); if ( optimiser ) { optimiser->Process(graph.get()); diff --git a/ethosu/regor/compiler/graph_optimiser.cpp b/ethosu/regor/compiler/graph_optimiser.cpp index c7e0d6655d9ba135e0a342198ad0c84b1ec10bc9..9ae4720d121d471c171255b5fc336776c9e0c07a 100644 --- a/ethosu/regor/compiler/graph_optimiser.cpp +++ b/ethosu/regor/compiler/graph_optimiser.cpp @@ -26,6 +26,7 @@ #include "op_type.hpp" #include "operation.hpp" #include "tensor.hpp" +#include "tflite/tflite_supported_operators.hpp" #include "tflite_graph_optimiser.hpp" #include @@ -41,16 +42,22 @@ namespace regor { -std::unique_ptr GraphOptimiser::MakeGraphOptimiser(GraphNotation notation, - IArchitectureConstraints *constraints, const GraphOptimiserOptions &options, OptimiserDatabase *db) +std::unique_ptr GraphOptimiser::MakeGraphOptimiser( + GraphNotation notation, Architecture *arch, const GraphOptimiserOptions &options, OptimiserDatabase *db) { switch ( notation ) { case GraphNotation::TFLite: - return std::unique_ptr(std::make_unique(constraints, options, db)); + { + std::unique_ptr supportedOps; + arch->Call([&supportedOps, &arch](const std::string &target) + { supportedOps = MakeSupportedOpsChecker(target, arch->Constraints()); }); + return std::unique_ptr( + std::make_unique(arch->Constraints(), std::move(supportedOps), options, db)); + } case GraphNotation::GraphAPI: - return std::unique_ptr(std::make_unique(constraints, options, db)); + return std::unique_ptr(std::make_unique(arch->Constraints(), options, db)); default: LOG_ERROR("Invalid graph notation"); diff --git a/ethosu/regor/compiler/graph_optimiser.hpp b/ethosu/regor/compiler/graph_optimiser.hpp index b82383837c4671a6d08c8e76c7986b6e65b9c11d..3fbd7cec50073957dec7b842eea37271ebc5177c 100644 --- a/ethosu/regor/compiler/graph_optimiser.hpp +++ b/ethosu/regor/compiler/graph_optimiser.hpp @@ -1,5 +1,5 @@ // -// SPDX-FileCopyrightText: Copyright 2021-2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: Copyright 2021-2025 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 // @@ -76,8 +76,8 @@ public: - static std::unique_ptr MakeGraphOptimiser(GraphNotation notation, - IArchitectureConstraints *constraints, const GraphOptimiserOptions &options, OptimiserDatabase *db); + static std::unique_ptr MakeGraphOptimiser( + GraphNotation notation, Architecture *arch, const GraphOptimiserOptions &options, OptimiserDatabase *db); static void ParseGraphOptimiserOptions(GraphOptimiserOptions &opt, IniReader &reader); void Process(Graph *graph); diff --git a/ethosu/regor/compiler/tflite_graph_optimiser.cpp b/ethosu/regor/compiler/tflite_graph_optimiser.cpp index 4fcc82941be83af86a7f6f1fd735e8c6d6198ac6..e43931057ff88abd74ab5e38a242e3f4b03bb2cb 100644 --- a/ethosu/regor/compiler/tflite_graph_optimiser.cpp +++ b/ethosu/regor/compiler/tflite_graph_optimiser.cpp @@ -2713,6 +2713,49 @@ SliceConstTensor(const TensorConnection *conn, const Shape &sliceShape, const Sh return std::make_shared(Name, conn->tensor->Type(), sliceShape, std::move(newBuffer)); } +namespace +{ +void DisconnectActivation(Operation *const op) +{ + assert(TfLiteMapping::CanFuseActivationFunction(op)); + // Op originally had a fused activation + assert(op->Outputs().size() == 1); + assert(op->OFM()->Readers().size() == 1); + auto activation = op->OFM()->Readers().front(); + auto actOfm = activation->Output(TensorUsage::OFM); + assert(actOfm); + // bypass and disconnect the activation + op->CopyOutput(TensorUsage::OFM, *actOfm); + activation->SetPassthroughOp(); + activation->Disconnect(); +} +} // namespace + +Operation *TFLiteGraphOptimiser::SupportedOperatorChecks(Graph *const graph, Operation *const operation) +{ + if ( !_supportedOps->Check(operation) ) + { + if ( TfLiteMapping::CanFuseActivationFunction(operation) ) + { + // op originally had a fused activation + // disconnect it from the graph as it will be handled by CPU + DisconnectActivation(operation); + } + else if ( operation->IFM(0)->Writers().size() == 1 ) + { + auto pred = operation->IFM(0)->Writers().front(); + if ( TfLiteMapping::CanFuseActivationFunction(pred.get()) ) + { + // op is an activation function, disconnect op and set pred to passthrough + DisconnectActivation(pred.get()); + pred->SetPassthroughOp(); + } + } + operation->SetPassthroughOp(); + } + return operation; +} + Operation *TFLiteGraphOptimiser::ClampActivations(Graph *const graph, Operation *const operation) { OpType opType = operation->Type(); @@ -2854,9 +2897,11 @@ Operation *TFLiteGraphOptimiser::ConvertConvolutionGroup(Graph *const graph, Ope return concatOp.get(); } -TFLiteGraphOptimiser::TFLiteGraphOptimiser(IArchitectureConstraints *constraints, const GraphOptimiserOptions &options, OptimiserDatabase *db) : +TFLiteGraphOptimiser::TFLiteGraphOptimiser(IArchitectureConstraints *constraints, + std::unique_ptr supportedOps, const GraphOptimiserOptions &options, OptimiserDatabase *db) : GraphOptimiser(constraints, options, db) { + _supportedOps = std::move(supportedOps); _softmax = std::make_unique(db); } diff --git a/ethosu/regor/compiler/tflite_graph_optimiser.hpp b/ethosu/regor/compiler/tflite_graph_optimiser.hpp index 4ca3b92e709d37e81ea8e2a96d7097573bb3c5a5..ffa5af5360f2bc70c42b43a7e9738c331f0a7ff1 100644 --- a/ethosu/regor/compiler/tflite_graph_optimiser.hpp +++ b/ethosu/regor/compiler/tflite_graph_optimiser.hpp @@ -28,6 +28,7 @@ #include "operation.hpp" #include "softmax.hpp" #include "tensor.hpp" +#include "tflite/tflite_supported_operators.hpp" #include #include @@ -66,6 +67,7 @@ class TFLiteGraphOptimiser : public GraphOptimiser private: std::unique_ptr _softmax; + std::unique_ptr _supportedOps; // utility functions @@ -95,6 +97,7 @@ private: Operation *ConvertTanhSigmoidToLUT16(Operation *const op); // Rewrite functions + Operation *SupportedOperatorChecks(Graph *const graph, Operation *const operation); Operation *ClampActivations(Graph *const graph, Operation *const operation); Operation *ConvertConvolutionGroup(Graph *const graph, Operation *const operation); Operation *ConvertExpToLUT(Graph *const graph, Operation *const operation); @@ -194,6 +197,12 @@ public: #endif } }, + { + {}, + { + &TFLiteGraphOptimiser::SupportedOperatorChecks, + } + }, { {}, { @@ -272,7 +281,8 @@ public: }}; // clang-format on - explicit TFLiteGraphOptimiser(IArchitectureConstraints *constraints, const GraphOptimiserOptions &options, OptimiserDatabase *db); + explicit TFLiteGraphOptimiser(IArchitectureConstraints *constraints, + std::unique_ptr supportedOps, const GraphOptimiserOptions &options, OptimiserDatabase *db); const GraphOptStepArray &GraphOptimisationSteps() const { return _graphOptimisationSteps; } diff --git a/ethosu/regor/test/test_graphir_optimiser.cpp b/ethosu/regor/test/test_graphir_optimiser.cpp index 567c1d38bdd7b7e3185ffc3509402c5a301db17e..4a2289c847597681bfe7894174b60ac460217a7a 100644 --- a/ethosu/regor/test/test_graphir_optimiser.cpp +++ b/ethosu/regor/test/test_graphir_optimiser.cpp @@ -60,7 +60,7 @@ TEST_CASE("test_graphir_optimiser - constant propagation") }(); GraphOptimiserOptions options; - auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch->Constraints(), options, nullptr); + auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch.get(), options, nullptr); std::vector allOps; @@ -106,7 +106,7 @@ TEST_CASE("test_graphir_optimiser - constant propagation") }(); GraphOptimiserOptions options; - auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch->Constraints(), options, nullptr); + auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch.get(), options, nullptr); std::vector allOps; @@ -157,7 +157,7 @@ TEST_CASE("test_graphir_optimiser - ReduceSum") }(); GraphOptimiserOptions options; - auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch->Constraints(), options, nullptr); + auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch.get(), options, nullptr); REQUIRE(bool(optimiser)); optimiser->Process(graph.get()); @@ -204,7 +204,7 @@ TEST_CASE("test_graphir_optimiser - transpose removal") auto graph = CreateGraph(ops); GraphOptimiserOptions options; - auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch->Constraints(), options, nullptr); + auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch.get(), options, nullptr); optimiser->Process(graph.get()); @@ -248,7 +248,7 @@ TEST_CASE("test_graphir_optimiser - transpose merge") auto graph = CreateGraph(ops); GraphOptimiserOptions options; - auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch->Constraints(), options, nullptr); + auto optimiser = GraphOptimiser::MakeGraphOptimiser(graph->Notation(), arch.get(), options, nullptr); optimiser->Process(graph.get()); diff --git a/ethosu/regor/test/test_tflite_supported_operators.cpp b/ethosu/regor/test/test_tflite_supported_operators.cpp index 70ae089a3edc9b7e1f9c39c8e9e039346972c955..7aeff463986548427ace5f5db211e17f229fcac2 100644 --- a/ethosu/regor/test/test_tflite_supported_operators.cpp +++ b/ethosu/regor/test/test_tflite_supported_operators.cpp @@ -29,23 +29,12 @@ #include -#include "regor.h" +#include "include/regor.h" using namespace regor; namespace { -std::unique_ptr MakeSupportedOpsChecker(std::string target, std::shared_ptr &arch) -{ - if ( target == REGOR_ARCH_ETHOSU85 ) - { - return std::make_unique(arch->Constraints()); - } - else - { - return std::make_unique(arch->Constraints()); - } -} std::shared_ptr CreateOperation(OpType opType, Shape ifmShape, DataType ifmType, Shape ifm2Shape, DataType ifm2Type, Shape ofmShape, DataType ofmType) @@ -83,7 +72,7 @@ TEST_CASE("Supported operators Common") std::string err = "noerror"; arch->CheckConfiguration(err); REQUIRE(err == "noerror"); - auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU55, arch); + auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU55, arch->Constraints()); SECTION("ConstraintTensQuantized") { @@ -460,7 +449,7 @@ TEST_CASE("Supported operators EthosU55") arch->CheckConfiguration(err); REQUIRE(err == "noerror"); - auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU55, arch); + auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU55, arch->Constraints()); SECTION("Test positive") { @@ -602,7 +591,7 @@ TEST_CASE("Supported operators EthosU85") arch->CheckConfiguration(err); REQUIRE(err == "noerror"); - auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU85, arch); + auto supportedOps = MakeSupportedOpsChecker(REGOR_ARCH_ETHOSU85, arch->Constraints()); SECTION("Test positive") { diff --git a/ethosu/regor/tflite/tflite_supported_operators.cpp b/ethosu/regor/tflite/tflite_supported_operators.cpp index 5cd6f57b9bb4052a57212f444b34a48a875cfd3e..4ceb97792291c050fa0b14c9ba2c10cef12d0af2 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators.cpp @@ -22,6 +22,10 @@ #include "common/logging.hpp" #include "compiler/op_type.hpp" +#include "tflite_supported_operators_u55.hpp" +#include "tflite_supported_operators_u85.hpp" + +#include "include/regor.h" namespace regor { @@ -754,7 +758,7 @@ void TfLiteSupportedOperators::Failure(const Operation *op, const std::string &m name = ofmConn->tensor->Name().c_str(); } std::string type = OpTypeToString(op->Type()); - if ( opType != OpType::None ) + if ( opType != OpType::None && opType != OpType::Passthrough ) { auto tfLiteType = TfLiteMapping::OpTypeToBuiltinOperator(opType); type = TfLiteMapping::BuiltinOperatorToString(tfLiteType); @@ -803,59 +807,16 @@ TfLiteSupportedOperators::TfLiteSupportedOperators(IArchitectureConstraints *con }; } -namespace -{ -void DisconnectActivation(std::shared_ptr op) -{ - assert(TfLiteMapping::CanFuseActivationFunction(op.get())); - // Op originally had a fused activation - assert(op->Outputs().size() == 1); - assert(op->OFM()->Readers().size() == 1); - auto activation = op->OFM()->Readers().front(); - auto actOfm = activation->Output(TensorUsage::OFM); - assert(actOfm); - // bypass and disconnect the activation - op->CopyOutput(TensorUsage::OFM, *actOfm); - activation->SetPassthroughOp(); - activation->Disconnect(); -} -} // namespace - -void TfLiteSupportedOperators::Process(Graph *graph) +std::unique_ptr MakeSupportedOpsChecker(const std::string &target, IArchitectureConstraints *constraints) { - std::vector> operatorList; - graph->GetAllOperations(operatorList); - for ( auto &op : operatorList ) + if ( target == REGOR_ARCH_ETHOSU85 ) { - if ( op->Type() == OpType::Passthrough ) - { - // Op is already passthrough - // Only valid scenario is that op is a previously disconnected activation - assert(op->Passthrough() == nullptr && "source-operation set to passthrough before supported-ops checks"); - assert(op->CountInputs(TensorUsage::IFM) == 0); - assert(op->CountOutputs(TensorUsage::OFM) == 0); - continue; - } - if ( !Check(op.get()) ) - { - if ( TfLiteMapping::CanFuseActivationFunction(op.get()) ) - { - // op originally had a fused activation - // disconnect it from the graph as it will be handled by CPU - DisconnectActivation(op); - } - else if ( op->IFM(0)->Writers().size() == 1 ) - { - auto pred = op->IFM(0)->Writers().front(); - if ( TfLiteMapping::CanFuseActivationFunction(pred.get()) ) - { - // op is an activation function, disconnect op and set pred to passthrough - DisconnectActivation(pred); - pred->SetPassthroughOp(); - } - } - op->SetPassthroughOp(); - } + return std::make_unique(constraints); + } + else + { + assert(target == REGOR_ARCH_ETHOSU55 || target == REGOR_ARCH_ETHOSU65); + return std::make_unique(constraints); } } diff --git a/ethosu/regor/tflite/tflite_supported_operators.hpp b/ethosu/regor/tflite/tflite_supported_operators.hpp index b2e3ebbacf6fc5cfdc3490a4c0ec73f49ef33fa7..66584a194bf65d3e6ac39aaf2ac6b0bfd16979d9 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.hpp +++ b/ethosu/regor/tflite/tflite_supported_operators.hpp @@ -18,6 +18,7 @@ #pragma once +#include "architecture/architecture.hpp" #include "architecture/architecture_constraints.hpp" #include "compiler/graph.hpp" #include "compiler/operation.hpp" @@ -25,6 +26,7 @@ namespace regor { + class TfLiteSupportedOperators { using OperatorCheck = bool (TfLiteSupportedOperators::*)(const Operation *); @@ -41,8 +43,6 @@ protected: public: TfLiteSupportedOperators(IArchitectureConstraints *constraints); virtual ~TfLiteSupportedOperators() = default; - // process graph and set passthrough for unsupported operators - void Process(Graph *graph); virtual bool Check(const Operation *) = 0; protected: @@ -73,4 +73,8 @@ private: bool ConstraintSoftmax(const Operation *op); bool ConstraintPad(const Operation *op); }; + +// Factory for supported-ops checkers +std::unique_ptr MakeSupportedOpsChecker(const std::string &target, IArchitectureConstraints *constraints); + } // namespace regor