From 0f2e59f8bff0ca68794db1406e1264531da1d3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Alfv=C3=A9n?= Date: Fri, 21 Oct 2022 11:21:38 +0200 Subject: MLBEDSW-7028: Fix compiler assert for elementwise op - Refactored erroneously if statement that allowed illegal swapping between ifm1 and ifm2 for elementwise operators. Signed-off-by: Johan Alfven Change-Id: Iec571f710824432edac9104d960f199f33a1b241 --- ethosu/vela/cascade_builder.py | 29 +++++++++-------------------- ethosu/vela/scheduler.py | 22 ++++++++++------------ ethosu/vela/tensor.py | 11 +++++++++++ 3 files changed, 30 insertions(+), 32 deletions(-) (limited to 'ethosu/vela') diff --git a/ethosu/vela/cascade_builder.py b/ethosu/vela/cascade_builder.py index b042ba73..5573ff7b 100644 --- a/ethosu/vela/cascade_builder.py +++ b/ethosu/vela/cascade_builder.py @@ -16,7 +16,6 @@ # # Description: # Groups Operators in a schedule together to form Cascades. -from .high_level_command_to_npu_op import ifm_ifm2_correct_order from .live_range import ofm_can_reuse_ifm from .numeric_util import round_up from .operation import NpuBlockType @@ -98,7 +97,7 @@ class CascadeBuilder: and cost.stripe.height < sched_op.ofm.shape.height and sched_op.parent_op.read_offsets[0] is None and sched_op.parent_op.read_offsets[1] is None - and self.elementwise_cascading_correct_order(sched_op) + and self.elementwise_cascadable(sched_op) and not sched_op.parent_op.type.is_resize_op() and not sched_op.parent_op.type == Op.Conv2DBackpropInputSwitchedBias and sched_op.parent_op.attrs.get("padding", None) != Padding.TILE @@ -127,31 +126,21 @@ class CascadeBuilder: return ifm_size + ifm2_size + ofm_size + self.non_local_mem_usage.get(sched_op, 0) @staticmethod - def elementwise_cascading_conformity(sched_op): - """Check the inputs of the op to see if it's a candidate for cascading.""" + def elementwise_cascadable(sched_op): + """Check if the elementwise can be cascaded.""" if sched_op.parent_op.type.is_binary_elementwise_op(): - # We cannot rule out cascadability if at least one IFM is constant ifm = sched_op.parent_op.ifm ifm2 = sched_op.parent_op.ifm2 - ifm_const = ifm.ops != [] and ifm.ops[0].type == Op.Const - ifm2_const = ifm2.ops != [] and ifm2.ops[0].type == Op.Const - return ifm_const or ifm2_const - else: - # Either one IFM is not variable or it is not a binary elementwise op - we cannot rule out cascadability - return True + ofm = sched_op.parent_op.ofm - @staticmethod - def elementwise_cascading_correct_order(sched_op): - """Check the inputs of the op to see ifm and ifm2 has correct order.""" + # IFM must be non-constant/non-scalar/non-broadcast + ifm_cascadable = not (ifm.is_const or ifm.is_scalar or ifm.is_broadcast(ofm)) - if sched_op.parent_op.type.is_binary_elementwise_op(): - ifm2 = sched_op.parent_op.ifm2 - ifm2_const = ifm2.ops != [] and ifm2.ops[0].type == Op.Const + # IFM2 must be constant or scalar + ifm2_cascadable = ifm2.is_const or ifm2.is_scalar - # ifm_ifm2_correct_order needs full shape - correct_order = ifm_ifm2_correct_order(sched_op.ifm.shape, sched_op.ifm2.shape) - return ifm2_const and correct_order + return ifm_cascadable and ifm2_cascadable else: return True diff --git a/ethosu/vela/scheduler.py b/ethosu/vela/scheduler.py index ec7380a6..021bcc9e 100644 --- a/ethosu/vela/scheduler.py +++ b/ethosu/vela/scheduler.py @@ -227,19 +227,17 @@ class SchedulerOperation: # Perform an IFM swap for certain binary elementwise operators # in order to enable cascading, if the SchedOp conforms to # Elementwise cascading rules. - if self.op_type.is_binary_elementwise_op() and CascadeBuilder.elementwise_cascading_conformity(self): - ifm1 = ps.ifm_tensor - ifm2 = ps.ifm2_tensor - ofm = ps.ofm_tensor - assert ifm1.elements() > 0 - assert ifm2.elements() > 0 + # The non-constant/non-scalar/non-broadcast IFM should be the primary input + if self.op_type.is_binary_elementwise_op(): + ifm = self.parent_op.ifm + ifm2 = self.parent_op.ifm2 + ofm = self.parent_op.ofm - if ( - # The non-constant IFM should be the primary input - (ifm1.ops[0].type == Op.Const and ifm2.ops[0].type != Op.Const) - # The non-broadcasted IFM should be the primary input - or (ifm1.shape != ofm.shape and ifm2.shape == ofm.shape) - ): + ifm_can_be_primary = not (ifm.is_const or ifm.is_scalar or ifm.is_broadcast(ofm)) + ifm2_can_be_primary = not (ifm2.is_const or ifm2.is_scalar or ifm2.is_broadcast(ofm)) + + if not ifm_can_be_primary and ifm2_can_be_primary: + # IFM2 is the primary input self.reversed_operands = True self.ifm, self.ifm2 = self.ifm2, self.ifm diff --git a/ethosu/vela/tensor.py b/ethosu/vela/tensor.py index 17d41b1a..ba385886 100644 --- a/ethosu/vela/tensor.py +++ b/ethosu/vela/tensor.py @@ -436,6 +436,17 @@ class Tensor: def is_standard_fm(self) -> bool: return self.sub_purpose == TensorSubPurpose.Standard and self.purpose == TensorPurpose.FeatureMap + @property + def is_const(self) -> bool: + return self.ops != [] and self.ops[0].type == Op.Const + + @property + def is_scalar(self) -> bool: + return self.shape == [] and self.elements() == 1 + + def is_broadcast(self, ofm) -> bool: + return self.shape != ofm.shape + def element_size(self) -> int: if self.element_size_bytes == 0: return self.dtype.size_in_bits() // 8 -- cgit v1.2.1