2020-09-29 03:02:35 +08:00
|
|
|
//===-------------------------------------------------------*- tablegen -*-===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef TORCH_OPS
|
|
|
|
#define TORCH_OPS
|
|
|
|
|
2021-01-28 08:35:44 +08:00
|
|
|
include "npcomp/Dialect/Torch/IR/TorchTypes.td"
|
2021-04-29 05:36:46 +08:00
|
|
|
include "npcomp/Interfaces/Traits.td"
|
2021-01-28 08:35:44 +08:00
|
|
|
include "mlir/IR/SymbolInterfaces.td"
|
2021-04-27 02:42:41 +08:00
|
|
|
include "mlir/Interfaces/CastInterfaces.td"
|
2021-03-02 07:00:32 +08:00
|
|
|
include "mlir/Interfaces/ControlFlowInterfaces.td"
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
include "mlir/Interfaces/InferTypeOpInterface.td"
|
2021-03-02 09:24:15 +08:00
|
|
|
include "mlir/Interfaces/SideEffectInterfaces.td"
|
2020-09-29 03:02:35 +08:00
|
|
|
|
|
|
|
class Torch_Op<string mnemonic, list<OpTrait> traits = []>
|
|
|
|
: Op<Torch_Dialect, mnemonic, traits> {
|
|
|
|
}
|
|
|
|
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
include "npcomp/Dialect/Torch/IR/GeneratedAtenOps.td"
|
|
|
|
include "npcomp/Dialect/Torch/IR/GeneratedPrimOps.td"
|
2021-05-20 02:40:48 +08:00
|
|
|
include "npcomp/Dialect/Torch/IR/GeneratedQuantizedOps.td"
|
2020-09-29 03:02:35 +08:00
|
|
|
|
2021-01-28 08:35:44 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-02-18 03:28:51 +08:00
|
|
|
// TorchScript `torch.nn.Module` object instantiation ops.
|
2021-01-28 08:35:44 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
def Torch_NnModuleOp : Torch_Op<"nn_module", [
|
2021-02-18 03:28:51 +08:00
|
|
|
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
|
2021-01-28 08:35:44 +08:00
|
|
|
SingleBlockImplicitTerminator<"::mlir::NPCOMP::Torch::NnModuleTerminatorOp">]> {
|
|
|
|
let summary = "Constructs a torch.nn.Module";
|
|
|
|
let description = [{
|
|
|
|
This op is used to represent a torch.nn.Module when importing a
|
|
|
|
graph of Python objects.
|
|
|
|
|
|
|
|
This op returns a new torch.nn.Module as an SSA value, with a set of
|
|
|
|
declaratively specified properties.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```mlir
|
2021-02-18 03:28:51 +08:00
|
|
|
%2 = torch.nn_module {
|
|
|
|
torch.slot "b", %bool_true : !basicpy.BoolType
|
|
|
|
torch.slot "i", %num3_i64 : i64
|
|
|
|
torch.slot "f", %num : f64
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
torch.slot "t", %t : !torch.tensor
|
2021-02-18 03:28:51 +08:00
|
|
|
torch.slot "submodule", %1 : !torch.nn.Module
|
|
|
|
} : !torch.nn.Module<"my_class_name">
|
2021-01-28 08:35:44 +08:00
|
|
|
```
|
2021-02-18 03:28:51 +08:00
|
|
|
|
|
|
|
This op is tightly coupled to the `torch.class_type` op named in the
|
|
|
|
`!torch.nn.Module<"my_class_name">` type. Each slot must match precisely
|
|
|
|
with the corresponding `torch.attr` in the `torch.class_type`.
|
|
|
|
See the documentation for `torch.class_type` for information.
|
2021-01-28 08:35:44 +08:00
|
|
|
}];
|
|
|
|
|
|
|
|
let arguments = (ins);
|
|
|
|
let results = (outs Torch_NnModuleType:$result);
|
|
|
|
let regions = (region SizedRegion<1>:$region);
|
|
|
|
let verifier = "return ::verify(*this);";
|
|
|
|
|
2021-02-18 03:28:51 +08:00
|
|
|
let assemblyFormat = "$region attr-dict `:` type($result)";
|
|
|
|
|
|
|
|
let extraClassDeclaration = [{
|
|
|
|
StringRef getClassName() { return getType().getClassName(); }
|
Support multiple instances of a class in GlobalizeObjectGraph.
This happens in practice with e.g. ResNet from torchvision (multiple
instances of the same BatchNorm class).
The key observation is that for this program, and the expected set of
programs, we can convert the program to the same globalized form with a
bit more static analysis and effort to suitably monomorphize the
program. Though what we are doing here is fairly annoying to implement,
it saves any nontrivial later pass from having to do similar analyses
(or worse). E.g. shape inference would need to be object-graph aware,
mutation/lifetime analyses would have to be aware, etc. Additionally, it
would make us front-load what it means to have a !torch.nn.Module type
on an ABI boundary, which we are just not ready to handle.
I'm really, really hoping that in practice we can get away with
this, otherwise it's going to be really rough designing a representation
(and implementing everything to back it) that is convenient to transform
and gracefully scales from full object graph (in the most dynamic case)
down to a fixed set of global slots like we have here (in the most
static case, which we presume a lot of practical programs fall into).
This also involved introducing a
`torch-prepare-for-globalize-object-graph` pass that does a minimal set of
lowerings to simplify the IR into a more orthogonal and analyzable form,
and a `torch-globalize-pipeline` helper.
Recommended review order:
- updated documentation in Passes.td
- new tests in `globalize-object-graph-multiple-instances*.mlir`
- implementation of GlobalizeObjectGraph.cpp
- PrepareForGlobalizeObjectGraph.cpp + prepare-for-globalize-object-graph.mlir
- misc stuff like torch-globalize-pipeline pipeline definition.
With this, we can import, globalize, and inline resnet18 from
torchvision:
https://gist.github.com/silvasean/821586afc19b67d9fb72030b2e0adeb8
2021-03-10 12:33:21 +08:00
|
|
|
ClassTypeOp getClassType(::mlir::SymbolTable &symbolTable) {
|
|
|
|
return symbolTable.lookup<ClassTypeOp>(getClassName());
|
|
|
|
}
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
2021-01-28 08:35:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_NnModuleTerminatorOp : Torch_Op<"nn_module_terminator", [Terminator,
|
|
|
|
HasParent<"::mlir::NPCOMP::Torch::NnModuleOp">]> {
|
|
|
|
let summary = "Implicit terminator for torch.nn_module";
|
|
|
|
|
|
|
|
let arguments = (ins);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = "attr-dict";
|
|
|
|
}
|
|
|
|
|
2021-02-18 03:28:51 +08:00
|
|
|
def Torch_SlotOp : Torch_Op<"slot", [
|
2021-01-28 08:35:44 +08:00
|
|
|
HasParent<"::mlir::NPCOMP::Torch::NnModuleOp">]> {
|
2021-02-18 03:28:51 +08:00
|
|
|
let summary = "Define the value of a slot of a torch.nn.Module";
|
2021-01-28 08:35:44 +08:00
|
|
|
let description = [{
|
2021-02-18 03:28:51 +08:00
|
|
|
This op specifies that the initial value of the slot `name` of the
|
|
|
|
parent torch.nn_module should be `value`, which is allowed to be an
|
|
|
|
arbitrary Torch-compatible SSA value, including other !torch.nn.Module's.
|
2021-01-28 08:35:44 +08:00
|
|
|
}];
|
|
|
|
|
|
|
|
let arguments = (ins StrAttr:$name, AnyTorchType:$value);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$name `,` $value attr-dict `:` type($value)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-02-18 03:28:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Modeling of TorchScript class types
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
def Torch_ClassTypeOp : Torch_Op<"class_type", [
|
|
|
|
Symbol,
|
|
|
|
SingleBlockImplicitTerminator<"::mlir::NPCOMP::Torch::ClassTypeTerminatorOp">]> {
|
|
|
|
let summary = "Constructs a torch.ClassType";
|
|
|
|
let description = [{
|
|
|
|
Declares a class type. Class types are the types used to describe
|
|
|
|
TorchScript `torch.nn.Module`'s. The terminology "class type" is for
|
|
|
|
consistency with TorchScript (a better name in our context might be
|
|
|
|
"nn module subtype"). The `syn_name` of this op is the same string
|
|
|
|
as in the `!torch.nn.Module<"...">` type.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
// A simple empty torch.class_type, with corresponding torch.nn_module.
|
|
|
|
torch.class_type @empty {}
|
|
|
|
%submodule = torch.nn_module {} : !torch.nn.Module<"empty">
|
|
|
|
|
|
|
|
// A class type with many members.
|
|
|
|
torch.class_type @test {
|
|
|
|
torch.attr "b" : !basicpy.BoolType
|
|
|
|
torch.attr "i" : i64
|
|
|
|
torch.attr "f" : f64
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
torch.attr "t" : !torch.tensor
|
2021-02-18 03:28:51 +08:00
|
|
|
torch.attr "submodule" : !torch.nn.Module<"empty">
|
|
|
|
torch.method "method", @f
|
|
|
|
}
|
|
|
|
torch.nn_module {
|
|
|
|
// These must match the order and names in the `torch.class_type`.
|
|
|
|
torch.slot "b", %bool_true : !basicpy.BoolType
|
|
|
|
torch.slot "i", %num3_i64 : i64
|
|
|
|
torch.slot "f", %num : f64
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
torch.slot "t", %t : !torch.tensor
|
2021-02-18 03:28:51 +08:00
|
|
|
torch.slot "submodule", %submodule : !torch.nn.Module<"empty">
|
|
|
|
} : !torch.nn.Module<"test">
|
|
|
|
```
|
|
|
|
}];
|
|
|
|
|
|
|
|
let arguments = (ins SymbolNameAttr:$sym_name);
|
|
|
|
let results = (outs);
|
|
|
|
let regions = (region SizedRegion<1>:$region);
|
|
|
|
let verifier = "return ::verify(*this);";
|
|
|
|
|
|
|
|
let assemblyFormat = "$sym_name $region attr-dict";
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_ClassTypeTerminatorOp : Torch_Op<"class_type_terminator", [Terminator,
|
|
|
|
HasParent<"::mlir::NPCOMP::Torch::ClassTypeOp">]> {
|
|
|
|
let summary = "Implicit terminator for torch.class_type";
|
|
|
|
|
|
|
|
let arguments = (ins);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = "attr-dict";
|
|
|
|
}
|
|
|
|
|
2021-01-28 08:35:44 +08:00
|
|
|
def Torch_MethodOp : Torch_Op<"method", [
|
2021-02-18 03:28:51 +08:00
|
|
|
HasParent<"::mlir::NPCOMP::Torch::ClassTypeOp">,
|
2021-01-28 08:35:44 +08:00
|
|
|
DeclareOpInterfaceMethods<SymbolUserOpInterface>
|
|
|
|
]> {
|
2021-02-18 03:28:51 +08:00
|
|
|
let summary = "Declare a method of a torch.class_type";
|
2021-01-28 08:35:44 +08:00
|
|
|
let description = [{
|
2021-02-18 03:28:51 +08:00
|
|
|
This op declaratively specifies that the parent torch.class_type has a
|
2021-01-28 08:35:44 +08:00
|
|
|
method `name` which calls `function`. `function` is an unbound function.
|
|
|
|
That is, it explicitly takes the torch.nn.Module as a parameter (no implicit
|
|
|
|
"self" object).
|
2021-02-20 08:21:21 +08:00
|
|
|
|
|
|
|
If `private` is present, it indicates that external calls cannot be made
|
|
|
|
to this method.
|
2021-01-28 08:35:44 +08:00
|
|
|
}];
|
|
|
|
|
2021-02-20 08:21:21 +08:00
|
|
|
// We don't use sym_visibility because that only applies to Symbol's, and
|
|
|
|
// some of the related concepts like "nested" visibility are specific to
|
|
|
|
// symbols.
|
|
|
|
let arguments = (ins
|
|
|
|
StrAttr:$name,
|
|
|
|
FlatSymbolRefAttr:$function,
|
|
|
|
// `private` is a C++ keyword, so use `isPrivate`.
|
|
|
|
UnitAttr:$isPrivate
|
|
|
|
);
|
2021-01-28 08:35:44 +08:00
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-20 08:21:21 +08:00
|
|
|
(`private` $isPrivate^)? $name `,` $function attr-dict
|
2021-01-28 08:35:44 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-02-18 03:28:51 +08:00
|
|
|
def Torch_AttrOp : Torch_Op<"attr", [
|
|
|
|
HasParent<"::mlir::NPCOMP::Torch::ClassTypeOp">
|
|
|
|
]> {
|
|
|
|
let summary = "Declare an attribute of a torch.class_type";
|
|
|
|
let description = [{
|
|
|
|
This op declaratively specifies that torch.nn.Module's of the parent
|
|
|
|
torch.class_type must have an attribute `name` of type `type`.
|
2021-02-20 08:21:21 +08:00
|
|
|
|
|
|
|
If `private` is present, it indicates that the value of this attribute
|
|
|
|
cannot be accessed externally.
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
|
|
|
|
2021-02-20 08:21:21 +08:00
|
|
|
// We don't use sym_visibility because that only applies to Symbol's, and
|
|
|
|
// some of the related concepts like "nested" visibility are specific to
|
|
|
|
// symbols.
|
|
|
|
let arguments = (ins
|
|
|
|
StrAttr:$name,
|
|
|
|
TypeAttr:$type,
|
|
|
|
// `private` is a C++ keyword, so use `isPrivate`
|
|
|
|
UnitAttr:$isPrivate
|
|
|
|
);
|
2021-02-18 03:28:51 +08:00
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-20 08:21:21 +08:00
|
|
|
(`private` $isPrivate^)? $name `:` $type attr-dict
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Global slot ops
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TODO: Should these be in a separate dialect?
|
|
|
|
// At this point, they are fairly specific to torch types, but their get/set
|
|
|
|
// semantics follow Python.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
def Torch_GlobalSlotOp : Torch_Op<"global_slot", [
|
|
|
|
Symbol,
|
|
|
|
IsolatedFromAbove,
|
2021-02-26 07:54:51 +08:00
|
|
|
SingleBlockImplicitTerminator<"::mlir::NPCOMP::Torch::GlobalSlotInitOp">
|
2021-02-18 03:28:51 +08:00
|
|
|
]> {
|
|
|
|
let summary = "A slot with global storage";
|
|
|
|
let description = [{
|
|
|
|
Represents a slot with global storage. The slot semantics are the same
|
|
|
|
as Python's: getting or setting a slot is done by object identity.
|
2021-02-20 08:21:21 +08:00
|
|
|
|
|
|
|
The `typeBound` is a type that the contained type is a subtype of.
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
|
|
|
|
2021-02-20 08:21:21 +08:00
|
|
|
let arguments = (ins
|
|
|
|
SymbolNameAttr:$sym_name,
|
|
|
|
OptionalAttr<StrAttr>:$sym_visibility,
|
|
|
|
TypeAttr:$typeBound
|
|
|
|
);
|
2021-02-18 03:28:51 +08:00
|
|
|
let results = (outs);
|
2021-02-26 07:54:51 +08:00
|
|
|
let regions = (region SizedRegion<1>:$initializer);
|
2021-02-18 03:28:51 +08:00
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-26 07:54:51 +08:00
|
|
|
($sym_visibility^)? $sym_name attr-dict `:` $typeBound ($initializer^)?
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
2021-02-26 07:54:51 +08:00
|
|
|
}
|
2021-02-18 03:28:51 +08:00
|
|
|
|
2021-02-26 07:54:51 +08:00
|
|
|
def Torch_GlobalSlotInitOp : Torch_Op<"global_slot.init", [
|
|
|
|
Terminator,
|
|
|
|
HasParent<"::mlir::NPCOMP::Torch::GlobalSlotOp">]> {
|
|
|
|
let summary = "yield-like terminator for torch.global_slot initializer region";
|
|
|
|
let description = [{
|
|
|
|
The operand to this op becomes the initial value of the parent
|
|
|
|
torch.global_slot.
|
2021-02-18 03:28:51 +08:00
|
|
|
}];
|
2021-02-26 07:54:51 +08:00
|
|
|
|
|
|
|
let arguments = (ins AnyTorchType:$initialValue);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
// This bulider creates an illegal op, but is needed to appease
|
|
|
|
// ensureTerminator in the default builders for SingleBlockImplicitTerminator
|
|
|
|
// on the parent torch.global_slot op.
|
|
|
|
// TODO: Have a SingleBlockExplicitTerminator trait.
|
2021-03-09 21:58:03 +08:00
|
|
|
let builders = [OpBuilder<(ins), [{ /*nothing to do */ }]>];
|
2021-02-26 07:54:51 +08:00
|
|
|
|
|
|
|
let assemblyFormat = "$initialValue attr-dict `:` type($initialValue)";
|
2021-02-18 03:28:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_GlobalSlotGetOp : Torch_Op<"global_slot.get", []> {
|
|
|
|
let summary = "Get the value stored in a torch.global_slot";
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
FlatSymbolRefAttr:$slot
|
|
|
|
);
|
|
|
|
let results = (outs AnyTorchType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$slot attr-dict `:` type($result)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_GlobalSlotSetOp : Torch_Op<"global_slot.set", []> {
|
|
|
|
let summary = "Set the value stored in a torch.global_slot";
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
FlatSymbolRefAttr:$slot,
|
|
|
|
AnyTorchType:$value
|
|
|
|
);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$slot `=` $value attr-dict `:` type($value)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-02-02 09:59:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
// TorchScript interpreter builtin ops.
|
2021-02-02 09:59:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
// These don't correspond to a `torch::jit::Operator`, so they don't appear
|
|
|
|
// in the registry and cannot be autogenerated.
|
|
|
|
// Most of these correspond 1:1 to interpreter opcodes, though some
|
|
|
|
// (like control flow being lowered to raw branches) are not directly mapped.
|
|
|
|
// See `torch/csrc/jit/runtime/instruction.h`.
|
|
|
|
|
|
|
|
|
|
|
|
def Torch_PrimListUnpackOp: Torch_Op<"prim.ListUnpack",
|
|
|
|
[AllowsTypeRefinement]> {
|
|
|
|
let summary = "TorchScript prim::ListUnpack op";
|
|
|
|
let arguments = (ins AnyTorchType:$operand);
|
|
|
|
let results = (outs Variadic<AnyTorchType>:$results);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$operand attr-dict `:` type($operand) `->` type($results)
|
|
|
|
}];
|
|
|
|
}
|
2021-02-02 09:59:42 +08:00
|
|
|
|
2021-06-15 09:06:38 +08:00
|
|
|
def Torch_PrimTupleConstructOp: Torch_Op<"prim.TupleConstruct", [
|
|
|
|
NoSideEffect,
|
|
|
|
TypesMatchWith<"contained types correspond to operand types",
|
|
|
|
"elements", "result", "Torch::TupleType::get($_ctxt, llvm::to_vector<6>($_self))">
|
|
|
|
]> {
|
|
|
|
let summary = "TorchScript prim::TupleConstruct op";
|
|
|
|
let description = [{
|
|
|
|
Note: This op does not allow trivial type refinement, because the
|
|
|
|
operand types and the result types must be in correspondence.
|
|
|
|
}];
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
Variadic<AnyTorchType>:$elements
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
Torch_TupleType:$result
|
|
|
|
);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$elements attr-dict `:` type($elements)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-06-05 06:57:21 +08:00
|
|
|
def Torch_PrimListConstructOp: Torch_Op<"prim.ListConstruct", [
|
|
|
|
NoSideEffect,
|
|
|
|
AllowsTypeRefinement,
|
|
|
|
]> {
|
|
|
|
let summary = "TorchScript prim::ListConstruct op";
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
Variadic<AnyTorchType>:$elements
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
AnyTorchListType:$result
|
|
|
|
);
|
|
|
|
|
|
|
|
let verifier = "return ::verify(*this);";
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$elements attr-dict `:` functional-type(operands, results)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-02-02 09:59:42 +08:00
|
|
|
def Torch_PrimGetAttrOp : Torch_Op<"prim.GetAttr", []> {
|
|
|
|
let summary = "TorchScript prim::GetAttr op";
|
|
|
|
|
|
|
|
let arguments = (ins StrAttr:$name, Torch_NnModuleType:$receiver);
|
|
|
|
let results = (outs AnyTorchType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-18 03:28:51 +08:00
|
|
|
$receiver `[` $name `]` attr-dict `:` type($receiver) `->` type($result)
|
2021-02-02 09:59:42 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_PrimSetAttrOp : Torch_Op<"prim.SetAttr", []> {
|
|
|
|
let summary = "TorchScript prim::SetAttr op";
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
StrAttr:$name,
|
|
|
|
Torch_NnModuleType:$receiver,
|
|
|
|
AnyTorchType:$value
|
|
|
|
);
|
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-18 03:28:51 +08:00
|
|
|
$receiver `[` $name `]` `=` $value attr-dict `:` type($receiver) `,` type($value)
|
2021-02-02 09:59:42 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_PrimCallMethodOp : Torch_Op<"prim.CallMethod", []> {
|
|
|
|
let summary = "TorchScript prim::CallMethod op";
|
|
|
|
|
2021-02-06 06:54:04 +08:00
|
|
|
let arguments = (ins
|
|
|
|
StrAttr:$name,
|
|
|
|
Torch_NnModuleType:$receiver,
|
|
|
|
Variadic<AnyTorchType>:$operands
|
|
|
|
);
|
2021-02-02 09:59:42 +08:00
|
|
|
let results = (outs AnyTorchType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-02-18 03:28:51 +08:00
|
|
|
$receiver `[` $name `]` `(` $operands `)` attr-dict `:` type($receiver) `,` functional-type($operands, $result)
|
2021-02-06 06:54:04 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-03-02 07:00:32 +08:00
|
|
|
def Torch_PrimLoopOp : Torch_Op<"prim.Loop", [
|
|
|
|
DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorEntryOperands"]>]> {
|
|
|
|
let summary = "TorchScript prim::Loop op";
|
|
|
|
let description = [{
|
|
|
|
This op (together with prim.Loop.condition) define a looping construct
|
|
|
|
that combines `for` and `while` behavior.
|
|
|
|
|
|
|
|
See: https://github.com/pytorch/pytorch/blob/master/torch/csrc/jit/OVERVIEW.md#loops
|
|
|
|
}];
|
|
|
|
|
|
|
|
let arguments = (ins
|
|
|
|
I64:$maxTripCount,
|
|
|
|
Basicpy_BoolType:$initialCondition,
|
|
|
|
Variadic<AnyTorchType>:$iterArgsInit
|
|
|
|
);
|
|
|
|
let results = (outs Variadic<AnyTorchType>:$results);
|
|
|
|
let regions = (region SizedRegion<1>:$region);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$maxTripCount `,` $initialCondition `,` `init` `(` $iterArgsInit `)` $region
|
|
|
|
attr-dict `:` functional-type(operands, results)
|
|
|
|
}];
|
|
|
|
let verifier = [{ return RegionBranchOpInterface::verifyTypes(*this); }];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_PrimLoopConditionOp : Torch_Op<"prim.Loop.condition", [
|
|
|
|
Terminator,
|
|
|
|
HasParent<"::mlir::NPCOMP::Torch::PrimLoopOp">]> {
|
|
|
|
let summary = "yield-like terminator for torch.prim.Loop";
|
|
|
|
let description = [{
|
|
|
|
Does not correspond to any torch prim op directly (the way that they model
|
|
|
|
blocks has a built-in notion of yield-like terminator).
|
|
|
|
}];
|
|
|
|
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
let arguments = (ins
|
|
|
|
Basicpy_BoolType:$shouldContinue,
|
|
|
|
Variadic<AnyTorchType>:$iterArgs
|
|
|
|
);
|
2021-03-02 07:00:32 +08:00
|
|
|
let results = (outs);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-04-22 06:07:15 +08:00
|
|
|
$shouldContinue `,`
|
|
|
|
`iter` `(` ($iterArgs^ `:` type($iterArgs))? `)` attr-dict
|
2021-03-02 07:00:32 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-03-02 09:24:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Additional ops used to model TorchScript's Graph's / Node's.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
def Torch_DerefineOp : Torch_Op<"derefine", [
|
2021-04-27 02:42:41 +08:00
|
|
|
NoSideEffect,
|
|
|
|
DeclareOpInterfaceMethods<CastOpInterface>,
|
2021-03-02 09:24:15 +08:00
|
|
|
]> {
|
|
|
|
let summary = "De-refine a type";
|
|
|
|
let description = [{
|
|
|
|
In terms of IR structure, TorchScript allows types to vary in many
|
|
|
|
circumstances where MLIR requires pointer-identical types. In particular,
|
|
|
|
it is valid to pass any subtype in place of a type. For example, if an
|
|
|
|
`Optional[int]` is required somewhere in the IR, it is legal to pass a
|
|
|
|
value of just `int` (but not the other way around; see
|
|
|
|
`torch.prim.unchecked_cast`). In effect, every *use* can have a different
|
|
|
|
type.
|
|
|
|
|
|
|
|
This op bridges that impedance mismatch. This op allows casting a value
|
|
|
|
from one type to a type that it is a subtype of to model this behavior.
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
This op uses the TorchScript notion of subtype, which matches the
|
|
|
|
Python notion of subtype presented in PEP 483.
|
2021-03-02 09:24:15 +08:00
|
|
|
}];
|
|
|
|
|
2021-03-02 07:26:57 +08:00
|
|
|
let arguments = (ins AnyTorchType:$operand);
|
|
|
|
let results = (outs AnyTorchType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
2021-04-27 02:42:41 +08:00
|
|
|
$operand attr-dict `:` type($operand) `to` type($result)
|
2021-03-02 07:26:57 +08:00
|
|
|
}];
|
2021-04-27 02:42:41 +08:00
|
|
|
|
|
|
|
let hasCanonicalizer = 1;
|
2021-03-02 07:26:57 +08:00
|
|
|
}
|
|
|
|
|
2021-05-20 02:40:48 +08:00
|
|
|
def Torch_OperatorOp : Torch_Op<"operator", [
|
|
|
|
AllowsTypeRefinement
|
|
|
|
]> {
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
let summary = "Opaque torch operator";
|
|
|
|
let description = [{
|
|
|
|
Represents an invocation of a `torch::jit::Operator` for which we don't
|
|
|
|
have a registered MLIR operation.
|
2021-03-03 06:39:48 +08:00
|
|
|
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
The `name` attribute contains the name that the MLIR op would have
|
|
|
|
(excluding `torch.`) if we did have it registered, which allows easy
|
|
|
|
cross referencing with `JITOperatorRegistryDump.txt`.
|
2021-03-03 06:39:48 +08:00
|
|
|
}];
|
|
|
|
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
let arguments = (ins StrAttr:$name, Variadic<AnyTorchType>:$operands);
|
2021-03-03 06:39:48 +08:00
|
|
|
let results = (outs Variadic<AnyTorchType>:$results);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
Significantly restructure torch/aten import design.
This is a really major and invasive restructuring of the way we get
torch operators (`torch::jit::Operator` / `c10::OperatorHandle`) into
MLIR. Please forgive the challenging review, but due to the sheer
invasiveness, it wasn't really practical do do it in sane smaller
pieces.
This fully replaces everything that was already working on the
TorchScript path (actually, more -- we added tanh support to
TorchToLinalg in order to delete the older code paths). Additionally,
I've kept the lights on for the acap path too, including what little e2e
stuff was working before (for expediency I made a few tiny compromises
along the way that will be easy to undo when we give that path proper
attention).
Overview of the new design:
- The torch operator `somens::someunqualname.someoverloadname` is
imported as `torch.somens.someunqualname.someoverloadname` (skip the
last dotted part if the overload name is empty), OR, if we don't have
such an op registered, it is imported as
`torch.operator "somens.someunqualname.someoverloadname" (...) : ...`.
- The addition of the "overload name" is a critical element here, as
the `(ns,unqual,overload)` triple is unique, which solves a lot of
problems we were having.
- This involves having separate MLIR ops for the `trailing_` and
`.out` variants and all the different overloads. This seemed
necessary, because the set of overloads is so wild and varied and
unstructured. The previous design was leaning into some underlying
structure that just isn't there -- the default situation is
the "random overload that we want to manage on the MLIR side",
rather than that being an exception. E.g. `aten::ne` (not-equal)
has 21 overloads, only 4 of which are c10 dispatcher ops see
[gist](https://gist.github.com/silvasean/190ba918c550c956260e21254e1b8aa1),
and the "out" variant is really called `.Tensor_out` instead of
`.out` as it frequently is for other ops.
- Rationale for all being in `torch` namespace: the set of operators
are so varied and unstructured that "dialect per namespace"
doesn't result in anything resembling the typical MLIR dialect
boundary expectations. We could maybe draw the boundary at
dispatcher ops vs non-dispatcher ops, but that doesn't seem to
really result in very much useful structure at this point in time.
- Note: within the torch operator registry, we effectively have a
mini-basicpy subdialect (already type-resolved), which is reasonably
structured.
- The existing Torch op interfaces are also removed -- now that we
track the overload name, we can losslessly find the original
operator.
- Instead of `ATenRecognizeKernelsPass`, we now have a
`ReduceOpVariantsPass` that keys off certain traits (and perhaps
eventually interfaces) to reduce variants of ops to a smaller set,
ideally operating on immutable tensors and using surrounding ops to
model the mutability/aliasing aspects.
- Note: `torch.ns.unqual.overload` ops allow both immutable and
mutable tensors (unlike the previous hard distinction in the common
case). This is a premonition for a future change that will introduce a
bona fide `!torch.tensor` type that will clean up a bunch of stuff.
- `TorchToLinalg` / `TorchToStd` supercede the existing
"ATen->TCF->TCP->Linalg" path.
- The new `torch_ods_gen.py` supercedes `torch_signature_ods_gen.py`.
It should look somewhat familiar, but the benefit of hindsight has
allowed a lot of simplifications.
The overall trend seems to be to make the `torch` dialect a nice layer
independent of anything else. It feels like as a natural result of
various future changes we will be removing the reliance on basicpy+numpy
dialects and have a nice self-contained type system too that properly
models the TorchScript type system (including proper subtyping,
mutable/immutable tensors, optional dtype, etc.).
Recommended review order:
- Start at some of the new import IR, e.g. in
`frontends/pytorch/test/node_import/prim.py`,
`frontends/pytorch/test/acap_export/test_export_add3.py`, and other
tests.
- `frontends/pytorch/python/torch_mlir_utils/codegen/torch_ods_gen.py`
and associated generated files:
- `include/npcomp/Dialect/Torch/IR/GeneratedAtenOps.td`
- `include/npcomp/Dialect/Torch/IR/GeneratedPrimOps.td`
- Inspect `ReduceOpVariants.cpp` / `reduce-op-variants.mlir` and the new
traits in `include/npcomp/Dialect/Torch/IR/TorchTraits.h`
- Various code changes in the import path in
`frontends/pytorch/csrc/builder`. Probably most interesting is the new
code in `torch_to_mlir_utils.cpp` that has the logic to create the
`torch.operator` ops or `torch.ns.unqual.overload` ops.
This is the [new ResNet IR](https://gist.github.com/silvasean/5407aafb710d07612b7b5b92eabecebe),
just to be able to look at a substantial sample of IR in the new style.
2021-05-05 05:42:50 +08:00
|
|
|
$name `(` $operands `)` attr-dict `:` functional-type($operands, $results)
|
2021-04-22 06:07:15 +08:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-05-20 02:40:48 +08:00
|
|
|
def Torch_LinearParamsCreateOp : Torch_Op<"linear_params.create", [
|
|
|
|
AllowsTypeRefinement
|
|
|
|
]> {
|
|
|
|
let summary = "Create a `!torch.LinearParams`";
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTorchTensorType:$weight,
|
|
|
|
Optional<AnyTorchTensorType>:$bias
|
|
|
|
);
|
|
|
|
let results = (outs Torch_LinearParamsType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$weight (`,` $bias^)? attr-dict `:` type($weight) (`,` type($bias)^)?
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_PerTensorAffineCreateOp : Torch_Op<"per_tensor_affine.create", [
|
|
|
|
AllowsTypeRefinement
|
|
|
|
]> {
|
|
|
|
let summary = "Create a per-tensor-affine quantized tensor";
|
|
|
|
let description = [{
|
|
|
|
Create a quantized tensor.
|
|
|
|
|
|
|
|
Quantization formula is:
|
|
|
|
```
|
|
|
|
Q(x, scale, zero_point) = round(x/scale + zero_point)
|
|
|
|
```
|
|
|
|
|
|
|
|
See:
|
|
|
|
https://pytorch.org/docs/stable/quantization.html#quantized-tensors
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTorchTensorType:$int_repr,
|
|
|
|
AnyFloat:$scale,
|
|
|
|
AnyTorchIntType:$offset
|
|
|
|
);
|
|
|
|
// TODO: Limit to quantized dtypes (e.g. !torch.qint8).
|
|
|
|
let results = (outs AnyTorchTensorType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$int_repr `,` $scale `,` $offset attr-dict
|
|
|
|
`:` type($int_repr) `,` type($scale) `,` type($offset) `->` type($result)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
// TODO: Disaggregate this op into a value-semantic constant + val->nonval
|
|
|
|
// conversion if needed.
|
|
|
|
// Currently, this op can effectively hide val->nonval conversion, which makes
|
|
|
|
// it an edge case for passes that care about that such as
|
|
|
|
// torch-maximize-value-semantics.
|
|
|
|
// So the suggestion would be to lower this to a `torch.vtensor` op
|
|
|
|
// (+`torch.copy.tensor` if needed).
|
|
|
|
// In particular, currently we end up relying on convert-torch-to-std
|
|
|
|
// to effectively expose this (as part of lowering to `std.constant`) +
|
|
|
|
// hoping that some canonicalization cleans it up.
|
|
|
|
// The `torch-maximize-value-semantics` pass should be doing this
|
|
|
|
// before we convert to std at all.
|
|
|
|
def Torch_TensorOp : Torch_Op<"tensor", [
|
|
|
|
DeclareOpInterfaceMethods<InferTypeOpInterface, ["isCompatibleReturnTypes"]>,
|
|
|
|
AllowsTypeRefinement
|
|
|
|
]> {
|
|
|
|
let summary = "Create a value of !torch.tensor type from a literal";
|
|
|
|
let description = [{
|
|
|
|
Example:
|
|
|
|
```
|
|
|
|
%0 = torch.tensor(dense<0.0> : tensor<3x5xf32>) : !torch.tensor
|
|
|
|
%1 = torch.tensor(dense<0.0> : tensor<3xf32>) : !torch.vtensor<[3],f32>
|
|
|
|
```
|
|
|
|
}];
|
|
|
|
let arguments = (ins ElementsAttr:$value);
|
|
|
|
let results = (outs AnyTorchTensorType:$result);
|
|
|
|
|
|
|
|
let assemblyFormat = [{
|
|
|
|
`(` $value `)` attr-dict `:` type($result)
|
|
|
|
}];
|
|
|
|
|
|
|
|
let extraClassDeclaration = [{
|
|
|
|
// InferTypeOpInterface:
|
|
|
|
static bool isCompatibleReturnTypes(TypeRange inferred, TypeRange actual);
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_TensorStaticInfoCastOp : Torch_Op<"tensor_static_info_cast", [
|
|
|
|
DeclareOpInterfaceMethods<CastOpInterface>,
|
|
|
|
AllowsTypeRefinement,
|
|
|
|
NoSideEffect]> {
|
|
|
|
let summary = "Adds/removes static information from a tensor type.";
|
|
|
|
let description = [{
|
|
|
|
This op does not imply any runtime code. Semantically it is an identity
|
|
|
|
function. However, it statically annotates (or erases) shape and dtype
|
|
|
|
information from a tensor type.
|
|
|
|
|
|
|
|
This op *cannot* be used to add/remove value semantics from a tensor.
|
|
|
|
For converting between the value-semantic and non-value-semantic domains,
|
|
|
|
use `torch.copy.tensor`. The two ops are kept separate to prevent
|
|
|
|
canonicalizations from accidentally dropping static information. In
|
|
|
|
most cases, after running the `torch-refine-types` pass, this op becomes
|
|
|
|
a no-op (the pass will incorporate the static information into other ops
|
|
|
|
that allow type refinement).
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTorchTensorType:$operand
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
AnyTorchTensorType:$result
|
|
|
|
);
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$operand attr-dict `:` type($operand) `to` type($result)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_CopyTensorOp : Torch_Op<"copy.tensor", []> {
|
|
|
|
let summary = "Makes a copy of a tensor.";
|
|
|
|
let description = [{
|
|
|
|
Changes to the original tensor will not be reflected in the copy.
|
|
|
|
|
|
|
|
This op can be used to interconvert between value-semantic and
|
|
|
|
non-value-semantic tensors. However, this op *does not* allow
|
|
|
|
adding/removing static information about sizes/dtype. For that, use
|
|
|
|
`torch.tensor_static_info_cast`.
|
|
|
|
|
|
|
|
This op does not have the AllowsTypeRefinement trait because the operand
|
|
|
|
and result types are coupled. Only places that know how to simultaneously
|
|
|
|
update both types should be changing the type of this op.
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTorchTensorType:$operand
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
AnyTorchTensorType:$result
|
|
|
|
);
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$operand attr-dict `:` type($operand) `->` type($result)
|
|
|
|
}];
|
|
|
|
let verifier = "return ::verify(*this);";
|
|
|
|
let hasFolder = 1;
|
|
|
|
let hasCanonicalizer = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_OverwriteTensorOp : Torch_Op<"overwrite.tensor", [
|
|
|
|
AllowsTypeRefinement
|
|
|
|
]> {
|
|
|
|
let summary = "Ovewrite the contents of tensor with values from another.";
|
|
|
|
let description = [{
|
|
|
|
Replaces the contents of `overwritten` with corresponding values from
|
|
|
|
`value`.
|
|
|
|
|
|
|
|
Immediately after this op has completed, indexing `overwritten` will result
|
|
|
|
in identical values as indexing into `tensor`. Of course, later ops
|
|
|
|
might mutate `overwritten`, so this relationship need not hold for the
|
|
|
|
entire program.
|
|
|
|
|
|
|
|
This op has undefined behavior if the two tensors have different
|
|
|
|
shapes or dtypes.
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTorchTensorType:$value,
|
|
|
|
AnyTorchTensorType:$overwritten
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
);
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$value `overwrites` $overwritten attr-dict
|
|
|
|
`:` type($value) `,` type($overwritten)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_ToBuiltinTensorOp : Torch_Op<"to_builtin_tensor", [
|
|
|
|
DeclareOpInterfaceMethods<InferTypeOpInterface>
|
|
|
|
]> {
|
|
|
|
let summary = "Convert a `!torch.vtensor` to a `tensor`";
|
|
|
|
let description = [{
|
|
|
|
This op only operates on ValueTensorType, to avoid conflating conversions
|
|
|
|
between value-semantic and non-value-semantic types.
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
Torch_ValueTensorType:$operand
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
AnyTensor:$result
|
|
|
|
);
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$operand attr-dict `:` type($operand) `->` type($result)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
def Torch_FromBuiltinTensorOp : Torch_Op<"from_builtin_tensor", [
|
|
|
|
DeclareOpInterfaceMethods<InferTypeOpInterface>
|
|
|
|
]> {
|
|
|
|
let summary = "Convert a `tensor` to a `!torch.vtensor`";
|
|
|
|
let description = [{
|
|
|
|
This op only operates on ValueTensorType, to avoid conflating conversions
|
|
|
|
between value-semantic and non-value-semantic types.
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
AnyTensor:$operand
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
Torch_ValueTensorType:$result
|
|
|
|
);
|
|
|
|
let assemblyFormat = [{
|
|
|
|
$operand attr-dict `:` type($operand) `->` type($result)
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2021-06-15 02:36:10 +08:00
|
|
|
def Torch_ConstantNoneOp : Torch_Op<"constant.none",
|
|
|
|
[ConstantLike, NoSideEffect]> {
|
|
|
|
let summary = "Get the singleton None value.";
|
|
|
|
let description = [{
|
|
|
|
Not to be confused with the `mlir::NoneType`. Be careful to use
|
|
|
|
`Torch::NoneType` to avoid namespace ambiguity.
|
|
|
|
}];
|
|
|
|
let arguments = (ins);
|
|
|
|
let results = (outs Torch_NoneType:$result);
|
|
|
|
let assemblyFormat = "attr-dict";
|
|
|
|
let hasFolder = 1;
|
|
|
|
}
|
Introduce `!torch.tensor` / `!torch.vtensor` types.
This removes our reliance on the numpy dialect and avoids our off-label
use of the builtin tnesor type for modeling unknown dtypes. The
`!torch.vtensor` (`ValueTensorType`) type is a value-semantic tensor.
The `!torch.tensor` (`NonValueTensorType`) type is a non-value-semantic
tensor. The new types look as follows syntactically:
```
// Least-static-information, non-value-semantic tensor.
!torch.tensor
// Explicit form of least-static-information variant.
!torch.tensor<*,unk>
// Least-static-information, value-semantic tensor.
!torch.vtensor
// Explicit form of least-static-information variant.
!torch.vtensor<*,unk>
// Fixed-set of allowable element types, with first-class support for
// Torch's frontend signedness semantics.
!torch.tensor<*,si32>
// First-class support for unknown dtypes.
!torch.tensor<[?,?,?],unk>
// Standard MLIR representation of `?` for unknown dimensions.
!torch.tensor<[?,2,?,4],unk>
// Statically shaped / dtyped example.
!torch.vtensor<[1,2,3,4],f32>
```
This required fairly significant changes throughout the compiler, but
overall it is a big cleanup. We now have a much clearer layering of "the
Torch frontend lowering" vs "lowering to std + linalg + etc.".
At the C++ level, there is `ValueTensorType`, `NonValueTensorType`.
We also have a helper `BaseTensorType` (kind of like ShapedType) which
interoperates with those two.
Included changes:
- New `torch.tensor(dense<0.0> : tensor<5xf32>) : !torch.tensor` op for
creating torch tensor literals in the frontend.
- Consistently use signedness for the types (except i1 which I didn't
touch -- we need to sort out the situation with !basicpy.BoolType
there anyway so will be attending to that soon)
- Frontend can annotate whether an argument to the function has value
semantics. We currently require this, as our backend contract does not
currently allow us to even model the non-value-semantic case. Before,
the value-semantic assumption was randomly injected in the middle of
the pass pipeline.
- Move ArrayToTensor (now called MaximizeValueSemantics) and
RefinePublicReturn passes to torch dialect.
- The TorchToStd and TorchToLinalg passes are now type conversions from
`!torch.vtensor` to `tensor` and use the dialect conversion infra.
The overall conversion pipeline is set up following the best practices
of the "Type Conversions the Not-So-Hard Way" talk. This required
introducing `torch-func-builtin-tensorize` and
`torch-finalizing-builtin-tensorize` passes analogous to the upstream
bufferization passes with the corresponding names (mostly just
copypasta from there).
- Misc Torch-level canonicalizations -- we now cleanly layer the
lowering to std later in the pipeline, so we are gradually lessening
our reliance on random std constant folding before we get to that
point.
Recommended review order:
- New types in TorchTypes.td/TorchTypes.h/TorchDialect.cpp
- New ops in TorchOps.td / TorchOps.cpp
- Less important / more mechanical stuff
- Frontend changes.
- Pass changes/additions in `Torch/Transforms` and `Conversion/`
2021-05-21 08:07:18 +08:00
|
|
|
|
2021-06-15 23:29:06 +08:00
|
|
|
def Torch_ConstantStrOp : Torch_Op<"constant.str",
|
|
|
|
[ConstantLike, NoSideEffect]> {
|
|
|
|
let summary = "Materialize a constant str value.";
|
|
|
|
let description = [{
|
|
|
|
Note: Strings in Python (and TorchScript) are immutable.
|
|
|
|
}];
|
|
|
|
let arguments = (ins
|
|
|
|
StrAttr:$value
|
|
|
|
);
|
|
|
|
let results = (outs
|
|
|
|
Torch_StringType:$result
|
|
|
|
);
|
|
|
|
let assemblyFormat = "$value attr-dict";
|
|
|
|
let hasFolder = 1;
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:02:35 +08:00
|
|
|
#endif // TORCH_OPS
|