Skip to content

Commit fb9ea02

Browse files
committed
Update
[ghstack-poisoned]
2 parents 69c8455 + dbc5665 commit fb9ea02

18 files changed

Lines changed: 547 additions & 19 deletions

File tree

backends/arm/quantizer/quantization_annotator.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,6 @@ def _match_pattern(
547547
torch.ops.aten.split.Tensor,
548548
torch.ops.aten.split_with_sizes.default,
549549
torch.ops.aten.split_copy.Tensor,
550-
torch.ops.aten.transpose.Dimname,
551550
torch.ops.aten.transpose.int,
552551
torch.ops.aten.transpose_copy.int,
553552
torch.ops.aten.t_copy.default,
@@ -575,6 +574,15 @@ def _match_pattern(
575574
torch.ops.aten.detach_copy.default,
576575
}
577576

577+
# Dimname has been removed from upstream PyTorch, but there may be a window
578+
# where developers in this backend are using a mainline build of this backend
579+
# with an older version of PyTorch.
580+
# TODO: remove this once the build has time to be propagated and majority of
581+
# dev expected to be unimpacted
582+
_transpose_dimname = getattr(torch.ops.aten.transpose, "Dimname", None)
583+
if _transpose_dimname is not None:
584+
_one_to_one_shared_input_qspec.add(_transpose_dimname)
585+
578586
_one_to_one_shared_input_or_input_act_qspec: set[OpOverload] = {
579587
torch.ops.aten.alias.default,
580588
torch.ops.aten.clone.default,

backends/arm/test/runner_utils.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ def _elf_search_roots() -> list[Path]:
892892

893893

894894
def _elf_path_candidates(
895-
target_board: str, use_portable_ops: bool = False
895+
target_board: str, use_portable_ops: bool = False, build_dir_suffix: str = ""
896896
) -> list[Path]:
897897
if target_board not in VALID_TARGET:
898898
raise ValueError(f"Unsupported target: {target_board}")
@@ -901,11 +901,14 @@ def _elf_path_candidates(
901901
if target_board in ("corstone-300", "corstone-320"):
902902
build_dir = Path(
903903
"arm_test",
904-
f"arm_semihosting_executor_runner_{portable_ops_str}{target_board}",
904+
f"arm_semihosting_executor_runner_"
905+
f"{portable_ops_str}{target_board}{build_dir_suffix}",
905906
)
906907
binary_name = "arm_executor_runner"
907908
else:
908-
build_dir = Path("arm_test", f"arm_executor_runner_{portable_ops_str}vkml")
909+
build_dir = Path(
910+
"arm_test", f"arm_executor_runner_{portable_ops_str}vkml{build_dir_suffix}"
911+
)
909912
binary_name = "executor_runner"
910913

911914
candidates: list[Path] = []
@@ -950,9 +953,15 @@ def _resolve_existing_elf_path(elf_candidates: Iterable[Path]) -> Path:
950953
)
951954

952955

953-
def get_elf_path(target_board: str, use_portable_ops: bool = False) -> str:
956+
def get_elf_path(
957+
target_board: str, use_portable_ops: bool = False, build_dir_suffix: str = ""
958+
) -> str:
954959
elf_path = _resolve_existing_elf_path(
955-
_elf_path_candidates(target_board, use_portable_ops=use_portable_ops)
960+
_elf_path_candidates(
961+
target_board,
962+
use_portable_ops=use_portable_ops,
963+
build_dir_suffix=build_dir_suffix,
964+
)
956965
)
957966
return str(elf_path)
958967

backends/arm/test/tester/serialize.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,25 @@ def __init__(
3434
module: Optional[torch.nn.Module],
3535
use_portable_ops: bool = False,
3636
timeout: int = 120,
37+
build_dir_suffix: str = "",
3738
):
3839
"""
3940
Args:
4041
compile_spec: CompileSpecs to be used for serialization.
4142
module: Original Module to be used for serialization. Optional - can be used for reference output generation.
4243
portable_ops: If True tests with compiled in portable ops, default is to test without this to get error if not fully delegated
4344
timeout: Timeout for fvp. Default is 120 seconds.
45+
build_dir_suffix: Suffix appended to the executor-runner build dir
46+
name when resolving the ELF, letting callers select a runner
47+
built for a specific target (e.g. a Cortex-M variant).
4448
"""
4549
super().__init__()
4650
self.module = module
4751
self.timeout = timeout
4852
self.executorch_program_manager: ExecutorchProgramManager | None
4953
self.compile_spec = compile_spec
5054
self.use_portable_ops = use_portable_ops
55+
self.build_dir_suffix = build_dir_suffix
5156

5257
def run(self, artifact: ExecutorchProgramManager, inputs=None) -> None:
5358
super().run(artifact, inputs)
@@ -62,7 +67,9 @@ def run_artifact(self, inputs):
6267
inputs_flattened, _ = tree_flatten(inputs)
6368
intermediate_path = self.compile_spec._get_intermediate_path()
6469
target_board = get_target_board(self.compile_spec)
65-
elf_path = get_elf_path(target_board, self.use_portable_ops)
70+
elf_path = get_elf_path(
71+
target_board, self.use_portable_ops, build_dir_suffix=self.build_dir_suffix
72+
)
6673

6774
if not os.path.exists(elf_path):
6875
raise FileNotFoundError(

backends/cadence/aot/replace_ops.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ def maybe_remove_or_replace(self, node: torch.fx.Node) -> bool:
632632
value = 0 if len(node.args) == 2 else node.args[2]
633633

634634
arg_shape = input_node.meta["val"].shape
635+
dtype = input_node.meta["val"].dtype
635636

636637
# Convert orig_padding to a list for manipulation
637638
# pyre-ignore[6]: Argument type
@@ -663,7 +664,7 @@ def maybe_remove_or_replace(self, node: torch.fx.Node) -> bool:
663664
left_padding_shape,
664665
value,
665666
),
666-
kwargs={"dtype": torch.float32},
667+
kwargs={"dtype": dtype},
667668
)
668669
left_padding_node.meta = node.meta
669670
cat_tensors.append(left_padding_node)
@@ -683,7 +684,7 @@ def maybe_remove_or_replace(self, node: torch.fx.Node) -> bool:
683684
right_padding_shape,
684685
value,
685686
),
686-
kwargs={"dtype": torch.float32},
687+
kwargs={"dtype": dtype},
687688
)
688689
right_padding_node.meta = node.meta
689690
cat_tensors.append(right_padding_node)

backends/cadence/aot/tests/test_replace_ops_passes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,30 @@ def test_replace_pad_with_cat(self, shape: Tuple[int], padding: Tuple[int]) -> N
839839
0,
840840
)
841841

842+
@torch.no_grad()
843+
def test_replace_pad_with_cat_preserves_dtype(self) -> None:
844+
# The padding constant tensors must match the input dtype, otherwise the
845+
# resulting cat mixes dtypes and fails edge dialect dtype verification
846+
# (e.g. for quantized int8 graphs).
847+
x = torch.randint(-128, 127, (1, 2, 3), dtype=torch.int8)
848+
original_gm = single_op_builder(
849+
placeholders=(x,),
850+
op=exir_ops.edge.aten.constant_pad_nd.default,
851+
args=(x, [1, 1]),
852+
)
853+
854+
p = ReplacePadWithCatPass()
855+
result = cast(PassResult, p(original_gm))
856+
self.assertTrue(result.modified)
857+
graph_after_passes = result.graph_module
858+
859+
full_nodes = graph_after_passes.graph.find_nodes(
860+
op="call_function", target=exir_ops.edge.aten.full.default
861+
)
862+
self.assertEqual(len(full_nodes), 2)
863+
for full_node in full_nodes:
864+
self.assertEqual(full_node.kwargs["dtype"], torch.int8)
865+
842866
@torch.no_grad()
843867
def test_replace_repeat_with_cat(self) -> None:
844868
x = torch.randn([3, 5])

backends/cortex_m/target_config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ def __post_init__(self) -> None:
7878
f"{self.cpu.name}; supported: {allowed}"
7979
)
8080

81+
@property
82+
def target_string(self) -> str:
83+
"""Canonical ``cortex-m<variant>`` string; inverse of
84+
``from_target_string``."""
85+
return "cortex-m" + self.cpu.name[1:].lower()
86+
8187
@property
8288
def backend(self) -> cmsis_nn.Backend:
8389
if self.isa is not None:
@@ -105,6 +111,6 @@ def from_target_string(cls, target: str) -> CortexMTargetConfig:
105111
except KeyError as e:
106112
raise ValueError(
107113
f"Unsupported Cortex-M target string: {target!r}. "
108-
f"Supported: {sorted('cortex-m' + m.name[1:].lower() for m in CortexM)}"
114+
f"Supported: {sorted(cls(cpu=m).target_string for m in CortexM)}"
109115
) from e
110116
return cls(cpu=cpu)

backends/cortex_m/test/build_test_runner.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ${build_executorch} --devtools --target_cpu="${target_cpu}" --cmake-args="-DCORT
3333
# Build executor runner with selected aten ops and semi hosting
3434
build_dir="${et_root_dir}/arm_test"
3535
build_executor_runner="${et_root_dir}/backends/arm/scripts/build_executor_runner.sh"
36-
build_root_test_dir="${et_root_dir}/arm_test/arm_semihosting_executor_runner_corstone-300"
36+
build_root_test_dir="${et_root_dir}/arm_test/arm_semihosting_executor_runner_corstone-300_${target}"
3737

3838
select_ops_list="\
3939
aten::add.out,\

backends/cortex_m/test/tester.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,22 @@ def __init__(self, target_config: Optional[CortexMTargetConfig] = None):
6969

7070

7171
class CortexMSerialize(Serialize):
72-
def __init__(self):
72+
def __init__(self, target_config: Optional[CortexMTargetConfig] = None):
73+
target_config = target_config or CortexMTargetConfig(cpu=CortexM.M55)
7374
compile_spec = get_u55_compile_spec()
74-
super().__init__(compile_spec, 1024)
75+
# Select the runner built for this target (build_test_runner.sh writes
76+
# one runner per target into a target-suffixed directory).
77+
super().__init__(
78+
compile_spec,
79+
None,
80+
build_dir_suffix=f"_{target_config.target_string}",
81+
)
7582

7683

7784
cortex_m_stage_classes = {
7885
StageType.EXPORT: Export,
7986
StageType.QUANTIZE: CortexMQuantize,
8087
StageType.RUN_PASSES: CortexMRunPasses,
81-
StageType.SERIALIZE: Serialize,
8288
StageType.TO_EDGE: CortexMToEdge,
8389
StageType.TO_EXECUTORCH: ToExecutorch,
8490
StageType.SERIALIZE: CortexMSerialize,
@@ -103,6 +109,9 @@ def __init__(
103109
stage_classes[StageType.RUN_PASSES] = lambda: CortexMRunPasses(
104110
target_config=target_config
105111
)
112+
stage_classes[StageType.SERIALIZE] = lambda: CortexMSerialize(
113+
target_config=target_config
114+
)
106115
super().__init__(module, resolved_example_inputs, stage_classes)
107116

108117
def test_dialect(

backends/mlx/ops.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
BitwiseAndNode,
5454
BitwiseInvertNode,
5555
BitwiseOrNode,
56+
BitwiseXorNode,
5657
BroadcastToNode,
5758
CeilNode,
5859
ClipNode,
@@ -497,6 +498,12 @@ def _isnan_handler(P: MLXProgramBuilder, n: Node) -> Slot:
497498
"aten.bitwise_or",
498499
True,
499500
),
501+
(
502+
[torch.ops.aten.bitwise_xor.Tensor, torch.ops.aten.bitwise_xor.Scalar],
503+
BitwiseXorNode,
504+
"aten.bitwise_xor",
505+
True,
506+
),
500507
(
501508
[torch.ops.aten.lt.Tensor, torch.ops.aten.lt.Scalar],
502509
LessNode,

backends/mlx/runtime/MLXInterpreter.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,15 @@ exec_bitwise_or(const BitwiseOrNode& n, ExecutionState& st, StreamOrDevice s) {
14221422
n.out, bitwise_or(st.const_tensor_ref(n.a), st.const_tensor_ref(n.b), s));
14231423
}
14241424

1425+
inline void exec_bitwise_xor(
1426+
const BitwiseXorNode& n,
1427+
ExecutionState& st,
1428+
StreamOrDevice s) {
1429+
st.set_tensor(
1430+
n.out,
1431+
bitwise_xor(st.const_tensor_ref(n.a), st.const_tensor_ref(n.b), s));
1432+
}
1433+
14251434
inline void exec_tri(const TriNode& n, ExecutionState& st, StreamOrDevice s) {
14261435
int rows = resolve_int(n.n, st);
14271436
int cols = resolve_int(n.m, st);
@@ -2078,6 +2087,9 @@ class Interpreter {
20782087
case OpCode::BITWISE_OR:
20792088
ops::exec_bitwise_or(std::get<BitwiseOrNode>(instr.node), st, s);
20802089
break;
2090+
case OpCode::BITWISE_XOR:
2091+
ops::exec_bitwise_xor(std::get<BitwiseXorNode>(instr.node), st, s);
2092+
break;
20812093
case OpCode::TRI:
20822094
ops::exec_tri(std::get<TriNode>(instr.node), st, s);
20832095
break;

0 commit comments

Comments
 (0)