Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6207bdf
Fix crashes, wrong outputs and lowering failures in several backends …
shoumikhin Oct 29, 2026
8127c0f
Merge release/1.6 into the #24222 cherry-pick and restore three files
shoumikhin Oct 29, 2026
a8ccc45
Fix CUDA mask indexing, CUDA graph outputs and the runtime output-buf…
shoumikhin Oct 30, 2026
ee53b6f
Fix CUDA mask indexing, CUDA graph outputs and the runtime output-buf…
shoumikhin Oct 30, 2026
f2f94dc
Rebase the #24299 cherry-pick onto the #24222 cherry-pick
shoumikhin Oct 30, 2026
0dceaca
CUDA backend: per-program weights file names and antialiased resize (…
shoumikhin Oct 30, 2026
6c27e1c
CUDA backend: per-program weights file names and antialiased resize (…
shoumikhin Oct 30, 2026
44980c5
Rebase the #24314 cherry-pick onto the #24299 cherry-pick
shoumikhin Oct 30, 2026
d5b0e87
Refuse a CUDA graph replay when an input changes shape
shoumikhin Oct 30, 2026
d7322be
List the CUDA index shims in the wheel's shared library check
shoumikhin Oct 30, 2026
b315e3f
Keep Inductor's decompositions in the antialiased resize compile test
shoumikhin Oct 30, 2026
c9a926a
Delete the antialiased resize torch.compile test
shoumikhin Oct 30, 2026
d908376
Say why the antialiased resize avoids a conditional
shoumikhin Oct 30, 2026
93879d5
Refuse a hand-built data name in AOTI backends that keep data in the …
shoumikhin Oct 30, 2026
4ae4daa
Warn that CUDA data names differing only in case can share a file
shoumikhin Oct 30, 2026
a95adaf
Merge the updated #24222 cherry-pick into the #24299 cherry-pick
shoumikhin Oct 30, 2026
d22b428
Merge release/1.6 into the #24222 cherry-pick
shoumikhin Oct 30, 2026
afedcbd
Merge the updated #24299 cherry-pick into the #24314 cherry-pick
shoumikhin Oct 30, 2026
92c4baa
Correct comments on the CUDA index shims and set_output_data_ptr
shoumikhin Oct 30, 2026
2ee2baa
Merge the updated #24299 cherry-pick into the #24314 cherry-pick
shoumikhin Oct 30, 2026
1819fdf
Check the AOTI data name before compiling
shoumikhin Oct 30, 2026
b535a66
Merge the updated #24299 cherry-pick into the #24314 cherry-pick
shoumikhin Oct 30, 2026
cdb488e
Restore release/1.6 Windows Voxtral CI files that the #24299 cherry-p…
shoumikhin Oct 30, 2026
07002ff
Regenerate aoti_cuda_shims.lib with aoti_torch_dtype_element_size
shoumikhin Oct 30, 2026
553ffd1
Merge release/1.6 into the #24222 cherry-pick
shoumikhin Oct 30, 2026
b4f29e3
Merge the updated #24222 cherry-pick into the #24299 cherry-pick
shoumikhin Oct 30, 2026
530c96b
Merge the updated #24299 cherry-pick into the #24314 cherry-pick
shoumikhin Oct 30, 2026
3879895
Qualcomm AI Engine Direct - Decompose ops that read only constants fo…
shoumikhin Oct 31, 2026
0ec89ad
Qualcomm AI Engine Direct - Say that HTP decomposes every constant-on…
shoumikhin Oct 31, 2026
187f1e3
Qualcomm AI Engine Direct - Keep quantize and dequantize ops whole on…
shoumikhin Oct 31, 2026
f714c08
Qualcomm AI Engine Direct - Keep reporting kept-whole ops left on CPU
shoumikhin Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ci/scripts/wheel/test_shared_libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"aoti_torch_cuda_randint_low_out",
"aoti_torch_cuda_sort_stable",
"aoti_torch_cuda_guard_set_index",
"aoti_torch_cuda_index_Tensor",
"aoti_torch_index_put_out",
)

_QUANTIZED_KERNEL_SYMBOLS = (
Expand Down
102 changes: 98 additions & 4 deletions backends/aoti/aoti_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import contextlib
import hashlib
import os
import re
import typing
from abc import ABC, abstractmethod
from enum import Enum
Expand All @@ -27,6 +28,29 @@

class COMPILE_SPEC_KEYS(Enum):
METHOD_NAME = "method_name"
DATA_NAME = "data_name"


# The name becomes a file name on every platform, so keep it short, to portable
# characters, and clear of Windows device names such as NUL. Without re.ASCII,
# IGNORECASE lets [A-Za-z] match letters like the Kelvin sign. A leading '-'
# reads as an option on a command line. Some writers add .ptd only when it is
# missing, so "encoder" and "encoder.ptd" could both write encoder.ptd.
_DATA_NAME_PATTERN = re.compile(
r"(?!(?:CON|PRN|AUX|NUL|COM\d|LPT\d)(?:\.|$))(?!.*\.ptd$)"
r"[A-Za-z0-9_][A-Za-z0-9_.-]{0,199}",
re.IGNORECASE | re.ASCII,
)


def _checked_data_name(data_name: str) -> str:
if not _DATA_NAME_PATTERN.fullmatch(data_name):
raise ValueError(
f"Invalid data name {data_name!r}: use up to 200 ASCII letters, "
"digits, '_', '-' and '.', do not start with '.' or '-', do not end "
"with '.ptd', and avoid Windows device names such as NUL"
)
return data_name


@experimental(
Expand Down Expand Up @@ -168,7 +192,8 @@ def release_moved_tensors(
def collect_unsupported_fallback_kernels(cls, missing_fallback_kernels: Set[str]):
"""
Context manager to collect unsupported fallback kernels during compilation.
Monitors both extern kernel calls and runtime lookup.
Monitors extern kernel calls, runtime lookup, and the scatter and
index_put fallbacks, which call their kernels directly.
"""
supported_kernels = cls.get_supported_fallback_kernels()

Expand All @@ -178,6 +203,10 @@ def collect_unsupported_fallback_kernels(cls, missing_fallback_kernels: Set[str]
original_generate_fallback_kernel_with_runtime_lookup_aot = (
CppWrapperCpu.generate_fallback_kernel_with_runtime_lookup_aot
)
original_generate_scatter_fallback = CppWrapperCpu._generate_scatter_fallback
original_generate_index_put_fallback = (
CppWrapperCpu._generate_index_put_fallback
)

def generate_c_shim_extern_kernel_call_and_collect_unsupported_kernels(
self, kernel: str, *args: Any, **kwargs: Any
Expand All @@ -200,10 +229,36 @@ def generate_fallback_kernel_with_runtime_lookup_aot_and_collect_unsupported_ker
self, op_overload, *args, **kwargs
)

def collect_kernels_it_writes(original: Any) -> Any:
# These fallbacks write their shim call directly, so read the
# kernel names from the lines they write.
def generate_and_collect_unsupported_kernels(
self, *args: Any, **kwargs: Any
) -> None:
lines: List[str] = []
writeline = self.writeline
self.writeline = lambda line: (lines.append(str(line)), writeline(line))
try:
return original(self, *args, **kwargs)
finally:
self.writeline = writeline
for line in lines:
for kernel in re.findall(r"\b(aoti_torch_\w+)\(", line):
if kernel not in supported_kernels:
missing_fallback_kernels.add(kernel)

return generate_and_collect_unsupported_kernels

CppWrapperCpu.generate_c_shim_extern_kernel_call = (
generate_c_shim_extern_kernel_call_and_collect_unsupported_kernels
)
CppWrapperCpu.generate_fallback_kernel_with_runtime_lookup_aot = generate_fallback_kernel_with_runtime_lookup_aot_and_collect_unsupported_kernels
CppWrapperCpu._generate_scatter_fallback = collect_kernels_it_writes(
original_generate_scatter_fallback
)
CppWrapperCpu._generate_index_put_fallback = collect_kernels_it_writes(
original_generate_index_put_fallback
)

try:
yield
Expand All @@ -214,6 +269,12 @@ def generate_fallback_kernel_with_runtime_lookup_aot_and_collect_unsupported_ker
CppWrapperCpu.generate_fallback_kernel_with_runtime_lookup_aot = (
original_generate_fallback_kernel_with_runtime_lookup_aot
)
CppWrapperCpu._generate_scatter_fallback = (
original_generate_scatter_fallback
)
CppWrapperCpu._generate_index_put_fallback = (
original_generate_index_put_fallback
)

@classmethod
def preprocess(
Expand All @@ -225,6 +286,9 @@ def preprocess(
Preprocess the edge program and compile it using AOTInductor.
Weights are always separated from the SO file.
"""
# First, so that a backend keeping its data in the .pte refuses a data
# name before anything is compiled.
data_name = cls.data_name_from_compile_specs(compile_specs)
device_name = cls.get_device_name()
decomposition_table = cls.get_decomposition_table()
options = cls.get_aoti_compile_options(compile_specs)
Expand Down Expand Up @@ -329,9 +393,7 @@ def preprocess(
named_data_store.add_named_data(so_blob_key, so_data, 1, None)
# Determine whether to save named data externally based on backend setting
# External: save to separate .ptd file, otherwise merge with .pte file
external_tag = (
f"aoti_{device_name}_blob" if cls.save_data_externally() else None
)
external_tag = data_name if cls.save_data_externally() else None

named_data_store.add_named_data(weights_blob_key, blob_data, 1, external_tag)

Expand Down Expand Up @@ -366,6 +428,38 @@ def generate_method_name_compile_spec(
method_name.encode("utf-8"),
)

@classmethod
def generate_data_name_compile_spec(cls, data_name: str) -> CompileSpec:
"""
Generate a CompileSpec that names the external data file, written as
``<data_name>.ptd``, so several programs can share a folder.
"""
cls._check_saves_data_externally()
return CompileSpec(
COMPILE_SPEC_KEYS.DATA_NAME.value,
_checked_data_name(data_name).encode("utf-8"),
)

@classmethod
def data_name_from_compile_specs(cls, compile_specs: List[CompileSpec]) -> str:
"""
The external data file name, without ``.ptd``, set by
generate_data_name_compile_spec, or ``aoti_<device>_blob`` by default.
"""
for spec in compile_specs:
if spec.key == COMPILE_SPEC_KEYS.DATA_NAME.value:
cls._check_saves_data_externally()
return _checked_data_name(spec.value.decode("utf-8"))
return f"aoti_{cls.get_device_name()}_blob"

@classmethod
def _check_saves_data_externally(cls) -> None:
if not cls.save_data_externally():
raise ValueError(
f"{cls.__name__} keeps its data in the .pte file, so it takes no "
"data name"
)

@classmethod
def method_name_from_compile_specs(
cls,
Expand Down
13 changes: 13 additions & 0 deletions backends/aoti/common_shims_slim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ AOTITorchError aoti_torch_get_sizes(Tensor* tensor, int64_t** ret_sizes) {
return Error::Ok;
}

AOTITorchError
aoti_torch_get_size(Tensor* tensor, int64_t d, int64_t* ret_size) {
if (tensor == nullptr || ret_size == nullptr) {
return Error::InvalidArgument;
}
const auto ndim = static_cast<int64_t>(tensor->dim());
if (d < -ndim || d >= ndim) {
return Error::InvalidArgument;
}
*ret_size = tensor->size(d);
return Error::Ok;
}

AOTITorchError aoti_torch_get_strides(Tensor* tensor, int64_t** ret_strides) {
if (tensor == nullptr || ret_strides == nullptr) {
return Error::InvalidArgument;
Expand Down
5 changes: 5 additions & 0 deletions backends/aoti/common_shims_slim.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ aoti_torch_get_data_ptr(Tensor* tensor, void** ret_data_ptr);
AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_sizes(Tensor* tensor, int64_t** ret_sizes);

// Generated code reads the size of a result whose shape depends on the data
// through this, one dim at a time.
AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_size(Tensor* tensor, int64_t d, int64_t* ret_size);

AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_strides(Tensor* tensor, int64_t** ret_strides);

Expand Down
31 changes: 22 additions & 9 deletions backends/aoti/passes/replace_view_copy_with_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
# natively by inductor, so map it here like the other view-copy ops.
torch.ops.aten.split_copy.Tensor: torch.ops.aten.split.Tensor,
ops.edge.aten.split_copy.Tensor: ops.edge.aten.split.Tensor,
# Edge turns ``detach`` into ``detach_copy``, which has no c-shim either.
# Without autograd it is ``alias_copy``, which Inductor can decompose,
# unlike ``detach_copy``. A view would let two outputs share one handle,
# and ``clone`` fails Edge verification in dim order mode.
torch.ops.aten.detach_copy.default: torch.ops.aten.alias_copy.default,
ops.edge.aten.detach_copy.default: ops.edge.aten.alias_copy.default,
}


Expand All @@ -40,19 +46,26 @@ class ReplaceViewCopyWithViewPass(ExportPass):
def call(self, graph_module: fx.GraphModule) -> PassResult:
graph_changed = False

for node in graph_module.graph.nodes:
if node.op != "call_function" or node.target not in _VIEW_TARGETS:
# Control flow branches, such as those of torch.cond, are submodules.
for module in graph_module.modules():
if not isinstance(module, fx.GraphModule):
continue

if self._has_blocking_user(node, node.users.keys()):
continue
module_changed = False
for node in module.graph.nodes:
if node.op != "call_function" or node.target not in _VIEW_TARGETS:
continue

if self._has_blocking_user(node, node.users.keys()):
continue

node.target = _VIEW_TARGETS[node.target]
graph_changed = True
node.target = _VIEW_TARGETS[node.target]
module_changed = True

if graph_changed:
graph_module.graph.lint()
graph_module.recompile()
if module_changed:
module.graph.lint()
module.recompile()
graph_changed = True

return PassResult(graph_module, graph_changed)

Expand Down
Loading
Loading