Skip to content

Commit af228e6

Browse files
committed
Arm backend: add preserve_io_quantization compile spec
Add an option to preserve the quantization on IO. Useful for keeping input and output tensors quantized when backend supports both +INT and +FP. Change-Id: Ibf6177e70c2abd9f64151553cb94698591a77acc Signed-off-by: Per Åstrand <per.astrand@arm.com>
1 parent 783cc26 commit af228e6

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

backends/arm/common/arm_compile_spec.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class DebugMode(Enum):
3636
compiler_flags: list[str] = field(default_factory=list)
3737
path_for_intermediates: str | None = None
3838
tosa_debug_mode: DebugMode | None = None
39+
preserve_io_quantization: bool = False
3940

4041
_TOSA_SPEC_KEY = "tosa_spec"
4142
_COMPILE_FLAGS_KEY = "compile_flags"
@@ -44,6 +45,7 @@ class DebugMode(Enum):
4445
_DEBUG_MODE_KEY = "dump_debug_info"
4546
_OUTPUT_REORDER_KEY = "ouput_reorder_workaround"
4647
_TRANSFORM_PIPELINE_CONFIG_KEY = "transform_pipeline_config"
48+
_PRESERVE_IO_QUANT_KEY = "preserve_io_quantization"
4749

4850
def _set_compile_specs(
4951
self,
@@ -53,6 +55,7 @@ def _set_compile_specs(
5355
tosa_debug_mode: DebugMode | None = None,
5456
output_order_workaround: bool = False,
5557
pipeline_config: ArmPassPipelineConfig | None = None,
58+
preserve_io_quantization: bool = False,
5659
):
5760
"""Set all values of dataclass directly."""
5861
self.tosa_spec = tosa_spec
@@ -61,6 +64,8 @@ def _set_compile_specs(
6164
self.tosa_debug_mode = tosa_debug_mode
6265
self._pipeline_config = pipeline_config
6366
self.output_order_workaround = output_order_workaround
67+
self.preserve_io_quantization = preserve_io_quantization
68+
self._warn_if_redundant_preserve_io_quantization()
6469
if output_order_workaround:
6570
warnings.warn(
6671
"ArmCompileSpec(output_order_workaround=True) is deprecated and will be "
@@ -78,6 +83,7 @@ def _from_list(cls, compile_specs: list[CompileSpec]): # noqa: C901
7883
tosa_debug_mode: ArmCompileSpec.DebugMode | None = None
7984
output_order_workaround: bool = False
8085
pipeline_config: ArmPassPipelineConfig | None = None
86+
preserve_io_quantization: bool = False
8187
unknown_specs: dict[str, str] = {}
8288
for spec in compile_specs:
8389
key = spec.key
@@ -128,6 +134,8 @@ def _from_list(cls, compile_specs: list[CompileSpec]): # noqa: C901
128134
"More than one transform pipeline entry in compile spec."
129135
)
130136
pipeline_config = ArmPassPipelineConfig.from_dict(json.loads(val))
137+
elif key == ArmCompileSpec._PRESERVE_IO_QUANT_KEY:
138+
preserve_io_quantization = str(val).lower() in ("1", "true", "yes")
131139
else:
132140
unknown_specs[key] = val
133141

@@ -151,6 +159,7 @@ def _from_list(cls, compile_specs: list[CompileSpec]): # noqa: C901
151159
tosa_debug_mode=tosa_debug_mode,
152160
output_order_workaround=output_order_workaround,
153161
pipeline_config=pipeline_config,
162+
preserve_io_quantization=preserve_io_quantization,
154163
)
155164
cls._from_list_hook(compile_spec, unknown_specs)
156165
compile_spec._validate()
@@ -227,8 +236,35 @@ def _to_list(self):
227236
self._pipeline_config.serialize(),
228237
)
229238
)
239+
compile_spec.append(
240+
CompileSpec(
241+
ArmCompileSpec._PRESERVE_IO_QUANT_KEY,
242+
str(bool(self.preserve_io_quantization)).encode(),
243+
)
244+
)
230245
return compile_spec
231246

247+
def _set_preserve_io_quantization(self, enabled: bool) -> "ArmCompileSpec":
248+
"""Preserve Q/DQ nodes at IO boundaries when lowering."""
249+
self.preserve_io_quantization = enabled
250+
self._warn_if_redundant_preserve_io_quantization()
251+
return self
252+
253+
def _warn_if_redundant_preserve_io_quantization(self) -> None:
254+
"""Warn when preserve_io_quantization has no effect for INT-only
255+
specs.
256+
"""
257+
if (
258+
self.preserve_io_quantization
259+
and self.tosa_spec.support_integer()
260+
and not self.tosa_spec.support_float()
261+
):
262+
warnings.warn(
263+
"preserve_io_quantization=True is redundant for INT-only TOSA "
264+
"specifications because boundary Q/DQ are already de-tagged.",
265+
stacklevel=3,
266+
)
267+
232268
def _get_pass_pipeline_config(self) -> ArmPassPipelineConfig:
233269
"""Returns configuration that controls how the Arm pass pipeline should
234270
behave.

backends/arm/test/misc/test_compile_spec.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
import warnings
7+
68
from executorch.backends.arm.common.pipeline_config import SoftmaxDecompositionConfig
79
from executorch.backends.arm.ethosu import EthosUCompileSpec
810
from executorch.backends.arm.tosa.compile_spec import TosaCompileSpec
911
from executorch.backends.arm.vgf import VgfCompileSpec
10-
from pytest import raises
12+
from pytest import raises, warns
1113

1214

1315
def test_compile_spec_u55_INT():
@@ -68,3 +70,24 @@ def test_compile_spec_tosa_INT():
6870
assert TosaCompileSpec._from_list(spec_list) == compile_spec
6971
with raises(ValueError, match="Incorrect output format"):
7072
VgfCompileSpec._from_list(spec_list)
73+
74+
75+
def test_preserve_io_quantization_roundtrip_vgf_FP_INT():
76+
compile_spec = VgfCompileSpec()._set_preserve_io_quantization(True)
77+
roundtripped = VgfCompileSpec._from_list(compile_spec._to_list())
78+
assert roundtripped.preserve_io_quantization is True
79+
80+
81+
def test_preserve_io_quantization_warns_for_u55_INT():
82+
with warns(
83+
UserWarning,
84+
match="preserve_io_quantization=True is redundant for INT-only TOSA",
85+
):
86+
EthosUCompileSpec("ethos-u55-128")._set_preserve_io_quantization(True)
87+
88+
89+
def test_preserve_io_quantization_no_warn_for_vgf_FP_INT():
90+
with warnings.catch_warnings(record=True) as recorded_warnings:
91+
warnings.simplefilter("always")
92+
VgfCompileSpec()._set_preserve_io_quantization(True)
93+
assert len(recorded_warnings) == 0

0 commit comments

Comments
 (0)