This pass replaces ops which takes torch.memory_format as an argument with 'equivalent' op which takes dim_order. This is towards the larger ExecuTorch goal to move away from torch.memory_format. There is a 1:1 mapping between the aten op and the new edge dialect dim_order op.
| 22 | |
| 23 | |
| 24 | class MemoryFormatOpsPass(ExportPass): |
| 25 | """ |
| 26 | This pass replaces ops which takes torch.memory_format as an argument with |
| 27 | 'equivalent' op which takes dim_order. This is towards the larger ExecuTorch |
| 28 | goal to move away from torch.memory_format. There is a 1:1 mapping between |
| 29 | the aten op and the new edge dialect dim_order op. |
| 30 | """ |
| 31 | |
| 32 | def call_operator(self, op, args, kwargs, meta): |
| 33 | if not (isinstance(op, EdgeOpOverload) and op in DimOrderOpsMap): |
| 34 | return super().call_operator( |
| 35 | op, |
| 36 | args, |
| 37 | kwargs, |
| 38 | meta, |
| 39 | ) |
| 40 | |
| 41 | # new kwargs with dim_order, and no memory_format for the new op |
| 42 | nkwargs = dict(copy.deepcopy(kwargs)) # orig kwargs are immutable |
| 43 | |
| 44 | # Get the target memory format for the EdgeOp, defaulting to |
| 45 | # preserve_format (clone() with no memory_format kwarg preserves |
| 46 | # the input's layout instead of forcing contiguous). |
| 47 | mem_format = nkwargs.pop("memory_format", torch.preserve_format) |
| 48 | |
| 49 | # Get input tensor and ndim |
| 50 | input_tensor: Optional[torch.Tensor] = None |
| 51 | if isinstance(args[0], ProxyValue) and args[0].is_tensor(): |
| 52 | input_tensor = args[0].to_tensor() |
| 53 | ndim = input_tensor.dim() |
| 54 | elif isinstance(args[0], torch.Tensor): |
| 55 | input_tensor = args[0] |
| 56 | ndim = input_tensor.dim() |
| 57 | elif isinstance(args[0], torch.fx.immutable_collections.immutable_list): |
| 58 | ndim = len(args[0]) |
| 59 | else: |
| 60 | assert ( |
| 61 | 0 |
| 62 | ), f"Expecting a Tensor, a ProxyValue, or a Sequence, but got {type(args[0])}" |
| 63 | |
| 64 | # Derive dim_order based on memory format |
| 65 | dim_order: List[int] |
| 66 | if mem_format in (None, torch.preserve_format): |
| 67 | # preserve_format: inherit dim_order from input tensor |
| 68 | if input_tensor is not None: |
| 69 | dim_order = [int(d) for d in input_tensor.dim_order()] |
| 70 | else: |
| 71 | # Fallback to contiguous if no single input tensor is available |
| 72 | # (e.g. list inputs like torch.stack). |
| 73 | dim_order = list(range(ndim)) |
| 74 | else: |
| 75 | # Explicit memory format (contiguous_format, channels_last, etc.) |
| 76 | dim_order = get_dim_order(mem_format, ndim) |
| 77 | |
| 78 | nkwargs["dim_order"] = dim_order |
| 79 | logger.debug( |
| 80 | f"{op.__name__} = rank: {ndim}, memory_format: {mem_format}." |
| 81 | f" {DimOrderOpsMap[op].__name__} = dim_order: {nkwargs['dim_order']}" |
no outgoing calls
no test coverage detected