From a235871ece8efa1bd4a01c8ef7ef773d54e431f5 Mon Sep 17 00:00:00 2001 From: Philip Hall Date: Wed, 11 Dec 2024 16:02:03 +0000 Subject: [PATCH] MLBEDSW-10159: Allow multiple attributes per operation - Some optimisations require additional attribute information on the replacement operation. The current attribute implementation allows only for one attribute at a time. This commit allows multiple attributes to be applied to any graph or scheduler operation. Signed-off-by: Philip Hall Change-Id: If7609a95405f3f83b27fe014daa72c8de0e48521 --- ethosu/regor/common/dynamic_typing.hpp | 14 ++-- ethosu/regor/compiler/attributes.cpp | 3 +- ethosu/regor/compiler/attributes.hpp | 72 ++++++++++++++++++- ethosu/regor/compiler/operation.hpp | 53 ++------------ ethosu/regor/compiler/scheduler_decompose.cpp | 4 +- ethosu/regor/compiler/scheduler_operation.hpp | 41 ++--------- ethosu/regor/compiler/scheduler_packing.cpp | 4 +- 7 files changed, 98 insertions(+), 93 deletions(-) diff --git a/ethosu/regor/common/dynamic_typing.hpp b/ethosu/regor/common/dynamic_typing.hpp index fe8c8cb8..ca389942 100644 --- a/ethosu/regor/common/dynamic_typing.hpp +++ b/ethosu/regor/common/dynamic_typing.hpp @@ -1,5 +1,5 @@ // -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 // @@ -216,8 +216,9 @@ public: DynamicRef &operator=(const DynamicRef &other) { - if ( &other != this && other._instance ) + if ( &other != this ) { + auto tmp = other._info ? other._info->AddRef(other._instance) : nullptr; if ( _instance ) { assert(_info); @@ -225,8 +226,7 @@ public: _instance = nullptr; } _info = other._info; - _instance = _info->AddRef(other._instance); - assert(_instance); + _instance = tmp; } return *this; } @@ -235,6 +235,12 @@ public: { if ( &other != this ) { + if ( _instance ) + { + assert(_info); + _info->Delete(_instance); + _instance = nullptr; + } _info = other._info; _instance = other._instance; other._instance = nullptr; diff --git a/ethosu/regor/compiler/attributes.cpp b/ethosu/regor/compiler/attributes.cpp index 8495aa79..ad7be889 100644 --- a/ethosu/regor/compiler/attributes.cpp +++ b/ethosu/regor/compiler/attributes.cpp @@ -1,5 +1,5 @@ // -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 // @@ -23,7 +23,6 @@ namespace regor { -#define REDUCED_HASH(hash) (hash & 0x000FFFFF) #define CASE_MAKE_ATTR_INSTANCE(TYPE_) \ case REDUCED_HASH(TypeHash::HASH): \ return DynamicRef(TypeInfoOf::Get(true), TypeInfoOf::SharedNew()); diff --git a/ethosu/regor/compiler/attributes.hpp b/ethosu/regor/compiler/attributes.hpp index d91a1880..badb7961 100644 --- a/ethosu/regor/compiler/attributes.hpp +++ b/ethosu/regor/compiler/attributes.hpp @@ -1,5 +1,5 @@ // -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 // @@ -26,6 +26,7 @@ #include "common/shape.hpp" #include "include/graphapi.hpp" +#include #include #include @@ -260,6 +261,73 @@ struct pad_attr_t END_FIELD_TABLE() }; -DynamicRef CreateAttribute(uint32_t reducedhash); +#define REDUCED_HASH(hash) (hash & 0x000FFFFF) + +DynamicRef CreateAttribute(uint32_t hash); + +// Attribute container - maintains chains of +// dynamically allocated attribute objects. +struct Attributes +{ + mutable std::forward_list _chain; + + DynamicRef *Get(bool create, uint32_t hash) const + { + for ( auto pos = _chain.begin(); pos != _chain.end(); pos++ ) + { + if ( *pos && (pos->Info()->Hash() == hash || REDUCED_HASH(pos->Info()->Hash()) == hash) ) return &(*pos); + } + if ( !create ) return nullptr; + return &_chain.emplace_front(CreateAttribute(hash)); + } + + DynamicRef *Require(uint32_t hash) const + { + DynamicRef *ref = Get(false, hash); + if ( !ref ) throw std::runtime_error("requested attribute must be already assigned"); + return ref; + } + + Attributes &operator=(const Attributes &attr) + { + if ( &attr != this ) + { + _chain = attr._chain; + } + return *this; + } +}; + +// Mixin to make objects attributable +class Attributable +{ +protected: + Attributes _attr; + +public: + template + TYPE *Attribute() + { + return static_cast(_attr.Get(true, TypeHash::HASH)->Instance()); + } + + template + const TYPE *Attribute() const + { + return static_cast(_attr.Require(TypeHash::HASH)->Instance()); + } + + template + bool HasAttribute() const + { + return _attr.Get(false, TypeHash::HASH) != nullptr; + } + + DynamicRef *AttributeByKey(uint32_t hash) { return _attr.Get(true, hash); } + + const Attributes &AttributeRef() const { return _attr; } +}; + + } // namespace regor diff --git a/ethosu/regor/compiler/operation.hpp b/ethosu/regor/compiler/operation.hpp index 24ce3f6e..200a8216 100644 --- a/ethosu/regor/compiler/operation.hpp +++ b/ethosu/regor/compiler/operation.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 // @@ -108,21 +108,24 @@ struct TensorConnection /// /// Graph Operation representation /// -class Operation : public std::enable_shared_from_this, public GraphApi::GraphOperation +class Operation : public std::enable_shared_from_this, public GraphApi::GraphOperation, public Attributable { private: ordered_map _inputs; ordered_map _outputs; OpType _type; std::unique_ptr _kernel; - DynamicRef _attr; const void *_passthrough = nullptr; // Original flatbuffer description of this op (if it was loaded from one) public: Operation(OpType opType); Operation(const Operation &op); - OpType Type() const { return _type; } + Operation &operator=(const Operation &) = delete; + Operation &operator=(Operation &&) = delete; + +public: + OpType Type() const { return _type; } const ordered_map &Outputs() const { return _outputs; } const ordered_map &Inputs() const { return _inputs; } @@ -156,48 +159,6 @@ public: bool IsDisconnected() const; bool HasScaling() const; - template - TYPE *Attribute() - { - if ( !_attr ) - { - _attr = CreateAttribute(TypeHash::HASH); - } - else if ( _attr.Info()->Hash() != TypeHash::HASH ) - { - throw std::runtime_error("attribute already assigned for this operator"); - } - - return static_cast(_attr.Instance()); - } - - template - const TYPE *Attribute() const - { - if ( !_attr || (_attr.Info()->Hash() != TypeHash::HASH) ) - { - throw std::runtime_error("requested attribute must be already assigned"); - } - return static_cast(_attr.Instance()); - } - - DynamicRef *AttributeByKey(uint32_t hash) - { - if ( !_attr ) - { - _attr = CreateAttribute(hash); - } - return &_attr; - } - - const DynamicRef &AttributeRef() const { return _attr; } - - template - bool HasAttribute() const - { - return _attr && (_attr.Info()->Hash() == TypeHash::HASH); - } - private: int CountUsage(const ordered_map &list, TensorUsage usage) const { diff --git a/ethosu/regor/compiler/scheduler_decompose.cpp b/ethosu/regor/compiler/scheduler_decompose.cpp index cd3adf05..50cc6714 100644 --- a/ethosu/regor/compiler/scheduler_decompose.cpp +++ b/ethosu/regor/compiler/scheduler_decompose.cpp @@ -1,5 +1,5 @@ // -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its affiliates // // SPDX-License-Identifier: Apache-2.0 // @@ -128,7 +128,7 @@ static std::unique_ptr MakeSubOperation(const SchedulerOpera subOp->SetHasScaling(schedOp->HasScaling()); subOp->_srcKey = schedOp->_srcKey; subOp->SetPrimaryIfmIndex(schedOp->PrimaryIfmIndex()); - subOp->SetAttributeRef(schedOp->_attr); + subOp->SetAttributes(schedOp->AttributeRef()); subOp->SetAccumulatorMode(schedOp->AccumulatorMode()); for ( const auto *list : {&schedOp->inputs, &schedOp->outputs} ) { diff --git a/ethosu/regor/compiler/scheduler_operation.hpp b/ethosu/regor/compiler/scheduler_operation.hpp index 1db3d2d7..ea463633 100644 --- a/ethosu/regor/compiler/scheduler_operation.hpp +++ b/ethosu/regor/compiler/scheduler_operation.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 // @@ -134,7 +134,7 @@ struct AccumulatorControl /// /// Scheduler's representation of executable operations /// -class SchedulerOperation +class SchedulerOperation : public Attributable { friend class SchedulerPacking; friend class Scheduler; @@ -148,7 +148,6 @@ public: void *_srcKey = nullptr; int _primaryIfmIndex = 0; AccumulatorControl _accumulatorControl; - DynamicRef _attr; const class SchedulerOperation *_parent = nullptr; std::vector> _subOps; // activations or Ethos-U85 chained ops ordered_map inputs; @@ -163,6 +162,9 @@ public: SchedulerOperation(OpType opType) : _type(opType) { _uid = GenerateUniqueId(); } ~SchedulerOperation() { Disconnect(); } + SchedulerOperation &operator=(const SchedulerOperation &) = delete; + SchedulerOperation &operator=(SchedulerOperation &&) = delete; + public: OpType Type() const { return _type; } int Index() const { return _index; } @@ -188,38 +190,7 @@ public: int PrimaryIfmIndex() const { return _primaryIfmIndex; } void SetPrimaryIfmIndex(int index) { _primaryIfmIndex = index; } - void SetAttributeRef(DynamicRef attr) { _attr = attr; } - - template - TYPE *Attribute() - { - if ( !_attr ) - { - _attr = CreateAttribute(TypeHash::HASH); - } - else if ( _attr.Info()->Hash() != TypeHash::HASH ) - { - throw std::runtime_error("attribute already assigned for this operator"); - } - - return static_cast(_attr.Instance()); - } - - template - const TYPE *Attribute() const - { - if ( !_attr || (_attr.Info()->Hash() != TypeHash::HASH) ) - { - throw std::runtime_error("requested attribute must be already assigned"); - } - return static_cast(_attr.Instance()); - } - - template - bool HasAttribute() const - { - return _attr && (_attr.Info()->Hash() == TypeHash::HASH); - } + void SetAttributes(const Attributes &attr) { _attr = attr; } // Input connections SchedulerConnection *AddInput(TensorUsage usage) { return &inputs[usage]; } diff --git a/ethosu/regor/compiler/scheduler_packing.cpp b/ethosu/regor/compiler/scheduler_packing.cpp index eb061cb8..4a9e6cc1 100644 --- a/ethosu/regor/compiler/scheduler_packing.cpp +++ b/ethosu/regor/compiler/scheduler_packing.cpp @@ -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 // @@ -445,7 +445,7 @@ std::unique_ptr SchedulerPacking::MakeSchedulerOperation(Ope schedOp->SetKernel(op->Kernel()); schedOp->SetHasScaling(op->HasScaling()); - schedOp->SetAttributeRef(op->AttributeRef()); + schedOp->SetAttributes(op->AttributeRef()); schedOp->_srcKey = op; // Get the inputs from the source op and connect with scheduler specific tensor -- GitLab