Skip to content

Commit b5aaeee

Browse files
authored
[CoreAI] Reject mutated user inputs at lowering (pytorch#23317)
ExecuTorch owns user inputs, so a Core AI delegate can never take over their in-place mutation. `coreai-torch` lowers such a mutation to Core AI state, which leaves the `.aimodel` boundary unable to match what ExecuTorch passes in. * `CoreAIBackend.preprocess` now raises before conversion when the program mutates a user input: `Core AI delegates cannot mutate ExecuTorch-owned user inputs: [...]`. * Mutated buffers that the delegate owns are unaffected. * Tests (`test_io_compat.py`): * `test_mutated_user_input_is_rejected` lowers a model that mutates its input and expects the error. * The `MutableBufferBoundaryTest` docstring now describes the current behavior. Test plan: `python -m pytest backends/apple/coreai` gives 126 passed.
1 parent 4c3d3bd commit b5aaeee

2 files changed

Lines changed: 23 additions & 6 deletions

File tree

‎backends/apple/coreai/compiler/preprocess.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ def preprocess(
603603
"build-time output directory (set it via coreai_sidecar_dir)"
604604
)
605605

606+
mutated_inputs = edge_program.graph_signature.user_inputs_to_mutate
607+
if mutated_inputs:
608+
raise ValueError(
609+
"Core AI delegates cannot mutate ExecuTorch-owned user inputs: "
610+
f"{sorted(mutated_inputs.values())}"
611+
)
606612
edge_inputs, edge_outputs = _edge_io(edge_program)
607613
input_names = [f"input_{i}" for i in range(len(edge_inputs))]
608614
output_names = [f"output_{i}" for i in range(len(edge_outputs))]

‎backends/apple/coreai/test/test_io_compat.py‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
assert_io_compatible,
2727
io_mismatches,
2828
)
29-
from executorch.backends.apple.coreai.compiler.preprocess import _convert_to_aiprogram
29+
from executorch.backends.apple.coreai.compiler.preprocess import (
30+
_convert_to_aiprogram,
31+
CoreAIBackend,
32+
)
3033
from executorch.backends.apple.coreai.partition.partitioner import CoreAIPartitioner
3134
from executorch.exir import to_edge, to_edge_transform_and_lower
3235
from executorch.exir.lowered_backend_module import executorch_call_delegate
@@ -299,15 +302,23 @@ def forward(self, x):
299302

300303

301304
class MutableBufferBoundaryTest(unittest.TestCase):
302-
"""A mutated buffer crosses the boundary as an input as well as an output.
303-
304-
coreai gives it a graph argument and a result, so both sides of the
305-
compatibility check have to count it.
306-
"""
305+
"""Mutated buffers the delegate owns stay off the ExecuTorch boundary."""
307306

308307
def test_mutated_buffer_lowers(self):
309308
_lower(_MutatedBuffer(), (torch.randn(2, 8),))
310309

310+
def test_mutated_user_input_is_rejected(self):
311+
class MutatedInput(nn.Module):
312+
def forward(self, x):
313+
x.add_(1.0)
314+
return x * 2.0
315+
316+
edge = to_edge(
317+
torch.export.export(MutatedInput(), (torch.randn(2, 8),))
318+
).exported_program()
319+
with self.assertRaisesRegex(ValueError, "cannot mutate ExecuTorch-owned"):
320+
CoreAIBackend.preprocess(edge, [])
321+
311322

312323
if __name__ == "__main__":
313324
unittest.main()

0 commit comments

Comments
 (0)