|
InfiniOps
Operator Library for Accelerators
|
InfiniOps can expose PyTorch ATen kernels as an InfiniOps backend when WITH_TORCH=ON. This is intended for broad operator coverage and for validating the generated wrapper path before a platform-native implementation exists.
The generated PyTorch backend is derived from the ATen schema bundled with the locally installed PyTorch package. No network fetch is required: WITH_TORCH=ON already requires PyTorch, and PyTorch wheels ship the matching torchgen/packaged/ATen/native/native_functions.yaml. When generation is not a good fit, a PyTorch backend can also be written by hand under src/torch/.
There are two supported ways to use ATen inside InfiniOps:
scripts/torch_ops.yaml and let scripts/generate_torch_ops.py emit the base and PyTorch backend.src/torch/ops/<op>/ and call ATen manually.Prefer generation when the public InfiniOps API can mirror an ATen .out schema. Use a hand-written ATen backend when the operator already has a hand-written base, needs an InfiniOps-specific API, needs special fallback logic, or does not correspond to a single ATen .out call.
Existing hand-written ATen examples include src/torch/ops/add/ and src/torch/ops/gemm/.
The ATen generator is scripts/generate_torch_ops.py. For each selected op it tries to find one or more usable ATen .out schemas and writes generated files under generated/:
generated/base/<op>.h: an InfiniOps operator base class when no hand-written src/base/<op>.h exists.generated/torch/<op>/<op>.h and .cc: the PyTorch backend implementation.generated/torch_ops_metadata.json: metadata consumed by tests/test_torch_ops.py.These generated files are build artifacts and are not committed. CMake regenerates them at configure time when WITH_TORCH=ON.
The PyTorch implementation uses implementation index 8. Native and vendor implementations use indices 0 through 7. This convention applies to generated ATen wrappers. Hand-written ATen backends may use another explicit implementation index, but must avoid colliding with the operator's existing implementations.
torchgen installed. Vendor PyTorch forks are supported as long as they ship the matching packaged ATen schema.Add the ATen base name to scripts/torch_ops.yaml.
Use the ATen name without the _out suffix. For example:
To try an operator without editing the allowlist, pass it through INFINI_OPS_TORCH_OPS or the generator's --ops argument.
Run the generator locally or through a CMake configure:
Inspect the output under generated/base/, generated/torch/, and generated/torch_ops_metadata.json.
Build with the PyTorch backend enabled. For focused iteration, use a smoke build and explicitly allowlist the operator for both wrapper generation and ATen wrapper generation:
Platform validation should also enable the platform backend, for example WITH_NVIDIA=ON, WITH_ASCEND=ON, and so on.
Run generated coverage:
Add or update a focused handwritten test when the operator is expected to become part of the normal operator test set. The generated test proves the ATen wrapper path is wired; handwritten tests provide operator-specific coverage with stable shapes, dtypes, tolerances, and skip rules.
The generator only uses ATen .out schemas. Output tensors become InfiniOps Tensor out parameters. ATen's self parameter is exposed as input in C++.
Supported ATen scalar and container types are mapped to torch-independent InfiniOps-facing C++ types. Common examples:
| ATen schema type | InfiniOps-facing type |
|---|---|
Tensor | Tensor |
Tensor[] | std::vector<Tensor> |
Scalar / float | double |
int / SymInt | int64_t |
bool | bool |
str | std::string |
ScalarType | DataType |
int[] / SymInt[] | std::vector<int64_t> |
Optional types with stable InfiniOps representations are exposed as std::optional<...>, such as std::optional<Tensor>, std::optional<double>, or std::optional<std::vector<int64_t>>.
Optional ATen-only concepts that do not have a stable public InfiniOps representation, such as MemoryFormat?, Layout?, Device?, and Generator?, stay hidden and are forwarded to ATen as typed empty optionals.
If src/base/<op>.h already exists, the generated base is not emitted for that operator. Instead, the generator parses the existing operator() overloads and tries to bind each overload to a usable ATen schema.
This lets hand-written base classes define the public API while still using an ATen backend.
Binding rules:
This is useful when a base class intentionally exposes a smaller overload and the missing ATen parameters can safely use defaults. If the missing parameter is semantically important, add it to the base overload instead of hiding it.
ATen overload suffixes such as Tensor_out, out_x, or grad_input are not part of the public InfiniOps class name. Distinct ATen overloads for the same public operator become overloaded constructors and operator() methods on one class.
Namespace-style prefixes are represented as C++ namespaces:
special_erfinv -> infini::ops::special::Erfinvlinalg_det -> infini::ops::linalg::Detfft_fft -> infini::ops::fft::Fftinfini::ops::internalFile names keep the flat op name for now, for example generated/base/special_erfinv.h.
In-place ATen variants are normalized by the generator so they do not collide with non-in-place public operator names. Treat them carefully: they may need operator-specific tests because ATen's mutation semantics are not always a good fit for InfiniOps' explicit-output API.
The generator prints skipped operators and reasons during configure. Common causes include:
no .out variant: the ATen op has no usable _out form.unsupported ATen type: the schema uses a type that is not mapped to an InfiniOps-facing C++ type.no testable tensor input/output pair: the op does not expose both tensor inputs and tensor outputs in a form the generator can validate.duplicate visible C++ signature: two ATen overloads collapse to the same public C++ signature after hidden defaults; one is kept.existing base has no overload compatible with ATen schema: a hand-written src/base/<op>.h exists, but none of its overloads match a usable ATen schema.Do not ignore these warnings when adding a new operator. A skipped operator is not available through the PyTorch backend even if it remains listed in scripts/torch_ops.yaml.
For a generated ATen-backed operator, validate at least:
WITH_TORCH=ON and the target platform backend enabled.tests/test_torch_ops.py includes the operator and passes on CPU plus the target platform.ruff format --check, ruff check, and any touched C++ formatting checks pass.When an operator is unsupported by a vendor PyTorch fork, keep the skip local and explicit in the operator test. If the vendor backend aborts or segfaults, do not hide it in a broad generated test result; split the operator into a follow-up and document the failing backend and reason.
A hand-written ATen backend is just a normal InfiniOps backend implementation that happens to call PyTorch C++ APIs. It is compiled when WITH_TORCH=ON.
Use this path when generation is a poor fit:
Gemm..out schema..out form, but a functional ATen call can be copied into an explicit output tensor.For an operator named Foo, add:
src/base/foo.h: the public InfiniOps operator base, if it does not already exist.src/torch/ops/foo/foo.h: the PyTorch backend declaration.src/torch/ops/foo/foo.cc: the PyTorch backend implementation.tests/test_foo.py.The source files under src/torch/ are picked up by CMake when WITH_TORCH=ON. The binding generator scans src/torch as an active implementation root, so a smoke build should include the op in INFINI_OPS_OPS if the smoke allowlist is active.
You do not need to add the op to scripts/torch_ops.yaml for a hand-written backend. Only add it there if you also want the generated backend/metadata path and have checked that it does not collide with the hand-written implementation.
Declare a specialization of Operator<Foo, kDev, kIndex> for all devices represented by kDev.
Example shape, based on src/torch/ops/add/add.h:
Choose kIndex deliberately:
8.Add uses 1, and Gemm uses 2.If the base class has multiple overloads and the PyTorch backend overrides only one of them, add using Foo::operator(); in the derived class so the inherited overloads remain visible. Gemm uses this pattern.
Include the backend header and src/torch/tensor_.h. The tensor helper converts InfiniOps tensor metadata into at::Tensor views:
Use metadata cached in the base constructor, such as input_shape_ and input_strides_, when calling ToAtenTensor. Do not rely on reading shape or stride data from the runtime Tensor argument in the call path; it may have been moved by dispatch machinery.
For input tensors, ToAtenTensor currently takes void*, so existing hand-written backends use const_cast<void*>(input.data()). Output tensors pass out.data() directly.
The hand-written backend can contain arbitrary ATen-side logic. Gemm is the main example:
a, b, and c into ATen tensor views.trans_a and trans_b by calling transpose.addmm_out / baddbmm_out on CPU and NVIDIA.matmul plus copy_, mul_, and add_ on other devices.This style is appropriate when InfiniOps semantics are stable but the best ATen implementation varies by rank, device, or parameter values.
For a focused smoke build:
Add the target platform flag as needed, such as WITH_NVIDIA=ON or WITH_CAMBRICON=ON.
Then run the focused operator test:
For hand-written ATen backends, a focused operator test is required. The generated tests/test_torch_ops.py coverage is driven by generated/torch_ops_metadata.json, so it only covers generated ATen wrappers.