pytorch - 💡(How to fix) Fix `@torch.compiler.nested_compile_region` failing on a custom op with mutations

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

import torch

@torch.library.custom_op("repro::add_into", mutates_args=("d",)) def add_into(d: torch.Tensor, x: torch.Tensor) -> None: """Compile-aware in-place add: d.add_(x).""" d.add_(x)

@add_into.register_fake def _add_into_fake(d, x): # noqa: ARG001 return None

--- Scenario A: same op, called at top level (no nested_compile_region) ----

def top_level(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor: torch.ops.repro.add_into(d, x) return d + 1.0

--- Scenario B: same op, called from inside nested_compile_region ----------

@torch.compiler.nested_compile_region def nested_inner(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor: torch.ops.repro.add_into(d, x) return d + 1.0

def with_nested(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor: return nested_inner(d, x)

def main() -> int: d = torch.zeros(4, device="cpu") x = torch.ones(4, device="cpu")

# Sanity: eager.
print("eager top_level   :", top_level(d.clone(), x))
print("eager with_nested :", with_nested(d.clone(), x))

failures = 0

# Scenario A: top-level torch.compile — expected to PASS.
print()
print("--- Scenario A: torch.compile(top_level)  [no nested_compile_region] ---")
compiled_a = torch.compile(top_level, fullgraph=True)
try:
    out = compiled_a(d.clone(), x)
    print(f"PASS — compiled top-level: {out}")
except Exception as e:
    failures += 1
    print(f"UNEXPECTED FAIL ({type(e).__name__}): {e}")

# Scenario B: nested_compile_region inside torch.compile — expected to FAIL on torch 2.12.
print()
print("--- Scenario B: torch.compile(with_nested)  [calls nested_compile_region] ---")
compiled_b = torch.compile(with_nested, fullgraph=True)
try:
    out = compiled_b(d.clone(), x)
    print(f"PASS — compiled with nested: {out}")
except Exception as e:
    print(f"FAIL ({type(e).__name__}): {e}")
    failures += 1

print()
if failures == 1:
    print(
        "Summary: top-level works, nested_compile_region path fails — bug confirmed."
    )
    return 0
print(f"Summary: unexpected outcome (failures={failures}).")
return 1

if name == "main": raise SystemExit(main())

Code Example

Found a custom (non-ATen) operator whose output has alias annotations:
    repro::add_into(Tensor(a0!) d, Tensor x) -> ().
    We only support functionalizing operators whose outputs do not have alias
    annotations.

---

import torch


@torch.library.custom_op("repro::add_into", mutates_args=("d",))
def add_into(d: torch.Tensor, x: torch.Tensor) -> None:
    """Compile-aware in-place add: ``d.add_(x)``."""
    d.add_(x)


@add_into.register_fake
def _add_into_fake(d, x):  # noqa: ARG001
    return None


# --- Scenario A: same op, called at top level (no nested_compile_region) ----


def top_level(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    torch.ops.repro.add_into(d, x)
    return d + 1.0


# --- Scenario B: same op, called from inside nested_compile_region ----------


@torch.compiler.nested_compile_region
def nested_inner(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    torch.ops.repro.add_into(d, x)
    return d + 1.0


def with_nested(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    return nested_inner(d, x)


def main() -> int:
    d = torch.zeros(4, device="cpu")
    x = torch.ones(4, device="cpu")

    # Sanity: eager.
    print("eager top_level   :", top_level(d.clone(), x))
    print("eager with_nested :", with_nested(d.clone(), x))

    failures = 0

    # Scenario A: top-level torch.compile — expected to PASS.
    print()
    print("--- Scenario A: torch.compile(top_level)  [no nested_compile_region] ---")
    compiled_a = torch.compile(top_level, fullgraph=True)
    try:
        out = compiled_a(d.clone(), x)
        print(f"PASS — compiled top-level: {out}")
    except Exception as e:
        failures += 1
        print(f"UNEXPECTED FAIL ({type(e).__name__}): {e}")

    # Scenario B: nested_compile_region inside torch.compile — expected to FAIL on torch 2.12.
    print()
    print("--- Scenario B: torch.compile(with_nested)  [calls nested_compile_region] ---")
    compiled_b = torch.compile(with_nested, fullgraph=True)
    try:
        out = compiled_b(d.clone(), x)
        print(f"PASS — compiled with nested: {out}")
    except Exception as e:
        print(f"FAIL ({type(e).__name__}): {e}")
        failures += 1

    print()
    if failures == 1:
        print(
            "Summary: top-level works, nested_compile_region path fails — bug confirmed."
        )
        return 0
    print(f"Summary: unexpected outcome (failures={failures}).")
    return 1


if __name__ == "__main__":
    raise SystemExit(main())

---

eager top_level   : tensor([2., 2., 2., 2.])
eager with_nested : tensor([2., 2., 2., 2.])

--- Scenario A: torch.compile(top_level)  [no nested_compile_region] ---
PASS — compiled top-level: tensor([2., 2., 2., 2.])

--- Scenario B: torch.compile(with_nested)  [calls nested_compile_region] ---
FAIL (BackendCompilerFailed): backend='inductor' raised:
RuntimeError: Found a custom (non-ATen) operator whose output has alias annotations: repro::add_into(Tensor(a0!) d, Tensor x) -> (). We only support functionalizing operators whose outputs do not have alias annotations (e.g. 'Tensor(a)' is a Tensor with an alias annotation whereas 'Tensor' is a Tensor without. The '(a)' is the alias annotation). The alias annotation specifies that the output Tensor shares storage with an input that has the same annotation. Please check if (1) the output needs to be an output (if not, don't return it), (2) if the output doesn't share storage with any inputs, then delete the alias annotation. (3) if the output indeed shares storage with an input, then add a .clone() before returning it to prevent storage sharing and then delete the alias annotation. Otherwise, please file an issue on GitHub.

While executing %invoke_subgraph : [num_users=1] = call_function[target=torch.ops.higher_order.invoke_subgraph](args = (%subgraph_0, subgraph_0, %l_d_, %l_x_), kwargs = {})
Original traceback:
  File "/home/ilyas/transformers/_repro_nested_compile_region_mutates_args.py", line 59, in with_nested
    return nested_inner(d, x)

Use tlparse to see full graph. (https://github.com/pytorch/tlparse?tab=readme-ov-file#tlparse-parse-structured-pt2-logs)

Set TORCHDYNAMO_VERBOSE=1 for the internal stack trace (please do this especially if you're reporting a bug to PyTorch). For even more developer context, set TORCH_LOGS="+dynamo"


Summary: top-level works, nested_compile_region path fails — bug confirmed.
RAW_BUFFERClick to expand / collapse

🐛 Describe the bug

custom op with mutations fail compilation under @torch.compiler.nested_compile_region

    Found a custom (non-ATen) operator whose output has alias annotations:
    repro::add_into(Tensor(a0!) d, Tensor x) -> ().
    We only support functionalizing operators whose outputs do not have alias
    annotations.

import torch


@torch.library.custom_op("repro::add_into", mutates_args=("d",))
def add_into(d: torch.Tensor, x: torch.Tensor) -> None:
    """Compile-aware in-place add: ``d.add_(x)``."""
    d.add_(x)


@add_into.register_fake
def _add_into_fake(d, x):  # noqa: ARG001
    return None


# --- Scenario A: same op, called at top level (no nested_compile_region) ----


def top_level(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    torch.ops.repro.add_into(d, x)
    return d + 1.0


# --- Scenario B: same op, called from inside nested_compile_region ----------


@torch.compiler.nested_compile_region
def nested_inner(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    torch.ops.repro.add_into(d, x)
    return d + 1.0


def with_nested(d: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
    return nested_inner(d, x)


def main() -> int:
    d = torch.zeros(4, device="cpu")
    x = torch.ones(4, device="cpu")

    # Sanity: eager.
    print("eager top_level   :", top_level(d.clone(), x))
    print("eager with_nested :", with_nested(d.clone(), x))

    failures = 0

    # Scenario A: top-level torch.compile — expected to PASS.
    print()
    print("--- Scenario A: torch.compile(top_level)  [no nested_compile_region] ---")
    compiled_a = torch.compile(top_level, fullgraph=True)
    try:
        out = compiled_a(d.clone(), x)
        print(f"PASS — compiled top-level: {out}")
    except Exception as e:
        failures += 1
        print(f"UNEXPECTED FAIL ({type(e).__name__}): {e}")

    # Scenario B: nested_compile_region inside torch.compile — expected to FAIL on torch 2.12.
    print()
    print("--- Scenario B: torch.compile(with_nested)  [calls nested_compile_region] ---")
    compiled_b = torch.compile(with_nested, fullgraph=True)
    try:
        out = compiled_b(d.clone(), x)
        print(f"PASS — compiled with nested: {out}")
    except Exception as e:
        print(f"FAIL ({type(e).__name__}): {e}")
        failures += 1

    print()
    if failures == 1:
        print(
            "Summary: top-level works, nested_compile_region path fails — bug confirmed."
        )
        return 0
    print(f"Summary: unexpected outcome (failures={failures}).")
    return 1


if __name__ == "__main__":
    raise SystemExit(main())
eager top_level   : tensor([2., 2., 2., 2.])
eager with_nested : tensor([2., 2., 2., 2.])

--- Scenario A: torch.compile(top_level)  [no nested_compile_region] ---
PASS — compiled top-level: tensor([2., 2., 2., 2.])

--- Scenario B: torch.compile(with_nested)  [calls nested_compile_region] ---
FAIL (BackendCompilerFailed): backend='inductor' raised:
RuntimeError: Found a custom (non-ATen) operator whose output has alias annotations: repro::add_into(Tensor(a0!) d, Tensor x) -> (). We only support functionalizing operators whose outputs do not have alias annotations (e.g. 'Tensor(a)' is a Tensor with an alias annotation whereas 'Tensor' is a Tensor without. The '(a)' is the alias annotation). The alias annotation specifies that the output Tensor shares storage with an input that has the same annotation. Please check if (1) the output needs to be an output (if not, don't return it), (2) if the output doesn't share storage with any inputs, then delete the alias annotation. (3) if the output indeed shares storage with an input, then add a .clone() before returning it to prevent storage sharing and then delete the alias annotation. Otherwise, please file an issue on GitHub.

While executing %invoke_subgraph : [num_users=1] = call_function[target=torch.ops.higher_order.invoke_subgraph](args = (%subgraph_0, subgraph_0, %l_d_, %l_x_), kwargs = {})
Original traceback:
  File "/home/ilyas/transformers/_repro_nested_compile_region_mutates_args.py", line 59, in with_nested
    return nested_inner(d, x)

Use tlparse to see full graph. (https://github.com/pytorch/tlparse?tab=readme-ov-file#tlparse-parse-structured-pt2-logs)

Set TORCHDYNAMO_VERBOSE=1 for the internal stack trace (please do this especially if you're reporting a bug to PyTorch). For even more developer context, set TORCH_LOGS="+dynamo"


Summary: top-level works, nested_compile_region path fails — bug confirmed.

Versions

PyTorch version: 2.12.0+cu130 Is debug build: False CUDA used to build PyTorch: 13.0 ROCM used to build PyTorch: N/A

OS: Ubuntu 24.04.1 LTS (x86_64) GCC version: (Ubuntu 13.2.0-23ubuntu4) 13.2.0 Clang version: Could not collect CMake version: Could not collect Libc version: glibc-2.39

cc @bdhirsh @ezyang @chauhang @penguinwu @ydwu4 @bobrenjc93 @aorenste

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING