From dac14fa6e7f71cf4626cf892e6c2eed33d8464bf Mon Sep 17 00:00:00 2001 From: Johan Gunnarsson Date: Fri, 27 Jun 2025 14:37:54 +0200 Subject: [PATCH] MLBEDSW-10786: Handle multiple constant 1-element IFMs Only one IFM can be used as scalar. If an op has multiple IFMs that could be used as scalars, pick IFM0 and keep the other IFMs as tensors. This decision whether an IFM should be scalar or not happens in RCS generator. To be able to fall back to using read only 1-element IFMs as tensors we need to put all tensors in read only memory, even if they could potentially be ignored. This will waste small amounts of read only memory. Signed-off-by: Johan Gunnarsson Change-Id: I9adf7bce615bf705eaf2686fd9640b27130fab17 --- .../ethos_u85_register_cs_generator.cpp | 2 +- ethosu/regor/compiler/scheduler.cpp | 49 +------------------ 2 files changed, 2 insertions(+), 49 deletions(-) diff --git a/ethosu/regor/architecture/ethosu85/ethos_u85_register_cs_generator.cpp b/ethosu/regor/architecture/ethosu85/ethos_u85_register_cs_generator.cpp index e0ae155e..a30bc247 100644 --- a/ethosu/regor/architecture/ethosu85/ethos_u85_register_cs_generator.cpp +++ b/ethosu/regor/architecture/ethosu85/ethos_u85_register_cs_generator.cpp @@ -1885,7 +1885,7 @@ void EthosU85RCSGenerator::GenerateElementwiseOp(HLCStripe *stripe, MemoryAccess GenerateScalingForElementwise(op); GenerateCommon(stripe, useGlobalScale, memoryAccesses); bool ifmIsScalar = IsScalar(op->ifm[0], scalarValue); - bool ifm2IsScalar = IsScalar(op->ifm[1], scalarValue); + bool ifm2IsScalar = !ifmIsScalar && IsScalar(op->ifm[1], scalarValue); GenerateIFM2Precision(op->ifm[1], ifm2Chained, ifm2IsScalar); GenerateIFM2(opType, op->ifm[1], stripe->ifmAreas[1], ifm2IsScalar, scalarValue, ifm2Cb); if ( !ifm2IsScalar && !ifm2Chained ) diff --git a/ethosu/regor/compiler/scheduler.cpp b/ethosu/regor/compiler/scheduler.cpp index 77eda599..42fa4aa6 100644 --- a/ethosu/regor/compiler/scheduler.cpp +++ b/ethosu/regor/compiler/scheduler.cpp @@ -859,56 +859,9 @@ bool Scheduler::AllocateAddresses(Schedule *schedule) } -/// @brief Specialised LiveRangeGraph for read only (flash) memory which ignores scalars if possible -class ReadOnlyLiveRangeGraph : public LiveRangeGraph -{ -private: - Architecture *_arch; - -public: - ReadOnlyLiveRangeGraph(Architecture *arch) : _arch(arch) {} - bool ShouldBeIgnored(SchedulerTensor *tens, const MemArea &targetMemory) override - { - // First do the regular check for matching memory type - if ( LiveRangeGraph::ShouldBeIgnored(tens, targetMemory) ) - { - return true; - } - // Memory type correct, check if this tensor is a scalar that can be encoded - // in the command stream for this architecture - auto srcTens = tens->srcTensor; - if ( srcTens && srcTens->StorageShape().Elements() == 1 && srcTens->IsConstant() ) - { - // All consumers must accept this scalar if we are to ignore it - for ( auto op : tens->consumers ) - { - // Find usage of the tensor for this consumer op - TensorUsage usage(TensorUsage::None); - for ( auto input : op->inputs.pairs() ) - { - if ( input.second.tensor.get() == tens ) - { - usage = input.first; - } - } - if ( !_arch->SupportsScalar(op->Type(), tens->dataType, usage) ) - { // This scalar cannot be ignored and must be handled - return false; - } - } - // At this point we have determined that the tensor can be encoded - // as a scalar in the command stream and can safely be ignored - return true; - } - // Not a scalar - cannot be ignored - return false; - } -}; - - void Scheduler::AllocateReadOnlyAddresses(Schedule *schedule, IncrementalLinearAllocator &readOnlyAllocator) { - auto lrGraph = ReadOnlyLiveRangeGraph(_arch); + LiveRangeGraph lrGraph; lrGraph.ExtractLiveRangesFromCascades(_ops, schedule, _arch->ReadonlyMemory(), false); auto totalSize = readOnlyAllocator.Allocate(&lrGraph, NPUTensorAlignment, _options.verboseAllocation); schedule->memoryUsage[_arch->ReadonlyMemory()] = int(totalSize); -- GitLab