|
14 | 14 | import torch |
15 | 15 | from torch._dynamo.source import EphemeralSource |
16 | 16 | from torch._dynamo.source import LocalSource |
| 17 | +from torch._dynamo.source import TensorProperty |
| 18 | +from torch._dynamo.source import TensorPropertySource |
17 | 19 | from torch._inductor.codegen.wrapper import ( |
18 | 20 | user_defined_triton_kernel_transitive_closure_source_code, |
19 | 21 | ) |
20 | 22 | from torch._inductor.runtime.runtime_utils import next_power_of_2 |
21 | 23 | from torch._subclasses import FakeTensor |
22 | 24 | from torch._subclasses import FakeTensorMode |
| 25 | +from torch.fx.experimental.symbolic_shapes import DimDynamic |
23 | 26 | from torch.fx.experimental.symbolic_shapes import ShapeEnv |
| 27 | +from torch.fx.experimental.symbolic_shapes import free_unbacked_symbols |
24 | 28 | from torch.utils._sympy.symbol import SymT |
25 | 29 | from torch.utils._sympy.symbol import symbol_is_type |
26 | 30 |
|
@@ -150,6 +154,7 @@ def __init__( |
150 | 154 | self.specialized_vars: set[sympy.Symbol] = set() |
151 | 155 | self.specialized_strides: set[tuple[str, int]] = set() |
152 | 156 | self._symint_cache: dict[object, torch.SymInt] = {} |
| 157 | + self._foreign_symint_cache: dict[tuple[int, sympy.Expr], torch.SymInt] = {} |
153 | 158 | self.device_load_count = ( |
154 | 159 | 0 # Track number of loads in all device code for eviction policy tuning |
155 | 160 | ) |
@@ -548,17 +553,65 @@ def to_fake(self, obj: object, origin: Origin) -> object: |
548 | 553 |
|
549 | 554 | raise TypeError(f"unsupported argument type {type(obj)} ({origin})") |
550 | 555 |
|
| 556 | + def _maybe_recreate_symint( |
| 557 | + self, |
| 558 | + s: int | torch.SymInt, |
| 559 | + source: Source, |
| 560 | + ) -> int | torch.SymInt: |
| 561 | + """Create a fresh SymInt in our ShapeEnv that mirrors a foreign one.""" |
| 562 | + if isinstance(s, int): |
| 563 | + return s |
| 564 | + outer_se = s.node.shape_env |
| 565 | + if outer_se is self.shape_env: |
| 566 | + return s |
| 567 | + expr = s.node.expr |
| 568 | + cache_key = (id(outer_se), expr) |
| 569 | + cached = self._foreign_symint_cache.get(cache_key) |
| 570 | + if cached is not None: |
| 571 | + return cached |
| 572 | + if free_unbacked_symbols(expr): |
| 573 | + result = self.create_unbacked_symint() |
| 574 | + else: |
| 575 | + hint = int(shape_env_var_hints(outer_se)[expr]) |
| 576 | + new_expr = self.shape_env.create_symbol( |
| 577 | + hint, source, dynamic_dim=DimDynamic.DYNAMIC |
| 578 | + ) |
| 579 | + result = self.shape_env.create_symintnode( |
| 580 | + new_expr, hint=hint, source=source |
| 581 | + ) |
| 582 | + self._foreign_symint_cache[cache_key] = result |
| 583 | + return result |
| 584 | + |
551 | 585 | def _to_fake_tensor(self, tensor: torch.Tensor, source: Source) -> torch.Tensor: |
552 | 586 | assert CompileEnvironment.current() is self |
553 | 587 | assert not self.fake_mode.is_our_fake(tensor) |
554 | | - if isinstance(tensor, FakeTensor) or self.settings.static_shapes: |
555 | | - # When the input is already a FakeTensor (from an outer tracing |
556 | | - # context, e.g. torch.compile calling a custom op's fake impl), |
557 | | - # we cannot pass it directly because it belongs to a different |
558 | | - # FakeTensorMode and would cause "Mixing fake modes" errors. |
559 | | - # We also cannot use from_real_tensor because it tries to read |
560 | | - # concrete sizes which fails on unbacked SymInts. empty_strided |
561 | | - # re-wraps it in our mode while preserving the symbolic sizes. |
| 588 | + if isinstance(tensor, FakeTensor): |
| 589 | + # FakeTensor from an outer tracing context (e.g. make_fx, Dynamo). |
| 590 | + # Create fresh symbols in our own ShapeEnv to avoid leaking |
| 591 | + # foreign symbols whose var_to_range entries are missing, |
| 592 | + # which causes assertion failures in _maybe_evaluate_static |
| 593 | + # on PyTorch versions without optimization_hint (< 2.12). |
| 594 | + new_sizes = tuple( |
| 595 | + self._maybe_recreate_symint( |
| 596 | + s, |
| 597 | + TensorPropertySource(source, TensorProperty.SIZE, i), |
| 598 | + ) |
| 599 | + for i, s in enumerate(tensor.size()) |
| 600 | + ) |
| 601 | + new_strides = tuple( |
| 602 | + self._maybe_recreate_symint( |
| 603 | + s, |
| 604 | + TensorPropertySource(source, TensorProperty.STRIDE, i), |
| 605 | + ) |
| 606 | + for i, s in enumerate(tensor.stride()) |
| 607 | + ) |
| 608 | + result = torch.empty_strided( |
| 609 | + new_sizes, |
| 610 | + new_strides, |
| 611 | + dtype=tensor.dtype, |
| 612 | + device=tensor.device, |
| 613 | + ) |
| 614 | + elif self.settings.static_shapes: |
562 | 615 | result = torch.empty_strided( |
563 | 616 | tensor.size(), |
564 | 617 | tensor.stride(), |
|
0 commit comments