diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/compilation_unit.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/compilation_unit.h new file mode 100644 index 0000000000000000000000000000000000000000..a07ff6e4ad9f4afeb0b60e942c7b9e485b3b6376 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/compilation_unit.h @@ -0,0 +1,351 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +struct Def; +struct Property; +struct ClassDef; +struct SugaredValue; +struct Resolver; + +using ResolverPtr = std::shared_ptr; +struct Self { + virtual ~Self() = default; + virtual std::shared_ptr makeSugared(Value* v) const = 0; + virtual ClassTypePtr getClassType() const = 0; +}; + +// A CompilationUnit is a list of named Functions +// with helper methods to iterate the list or invoke the function. +// Classes have a CompilationUnit holding the class methods, +// and Modules have a CompilationUnit holding the Functions that +// are used to implement their Methods + +struct TORCH_API CompilationUnit { + enum class FunctionType { Method, Hook, PreHook }; + // constructor that takes a set of functions to compile using the native + // resolver + explicit CompilationUnit(const std::string& source); + CompilationUnit() = default; + + CompilationUnit& operator=(CompilationUnit&&) = default; + CompilationUnit(CompilationUnit&&) = default; + CompilationUnit& operator=(const CompilationUnit&) = delete; + CompilationUnit(const CompilationUnit&) = delete; + + Function* find_function(const c10::QualifiedName& name) const { + auto it = dict_.find(name); + if (it == dict_.end()) { + return nullptr; + } + return functions_[it->second].get(); + } + + Function& get_function(const c10::QualifiedName& name) const { + if (auto r = find_function(name)) { + return *r; + } + TORCH_CHECK(false, "attempted to get undefined function ", name.name()); + } + + void set_optimized(bool o) { + TORCH_WARN( + "CompilationUnit::set_optimized() is deprecated and has no effect. " + "Please use setGraphExecutorOptimize()"); + } + + bool is_optimized() const { + TORCH_WARN( + "CompilationUnit::is_optimized() is deprecated and always returns true. " + "Please use getGraphExecutorOptimize()"); + return true; + } + + // for historic reasons, these are defined in ir_emitter.cpp + // Returns the list of Functions just defined. + std::vector define( + const std::optional& prefix, + const std::vector& properties, + const std::vector& propResolvers, + const std::vector& definitions, + const std::vector& + defResolvers, /* determines how we handle free + variables in each definition*/ + // if non-null, the first argument to each def, is bound to this value + const Self* self, + // see [name mangling] + bool shouldMangle = false, + std::optional operator_set_version = std::nullopt); + + void define_hooks( + const std::optional& prefix, + const std::vector& hookDefs, + const std::vector& hookResolvers, + const std::vector& preHookDefs, + const std::vector& preHookResolvers, + const Self* self, + bool shouldMangle = false); + + // same as above but parse the definitions from source + // Returns the list of Functions just defined. + std::vector define( + // prefix namespace to put all the defined functions into + const std::optional& prefix, + const std::string& source, + const ResolverPtr& resolver, + const Self* self); + + void define_interface( + const c10::QualifiedName& qualifiedName, + const ClassDef& classDef, + ResolverPtr rcb, + bool is_module = false); + + Function* create_function( + c10::QualifiedName name, + std::shared_ptr graph, + bool shouldMangle = false) { + if (shouldMangle) { + name = mangle(name); + } + auto fn = std::make_unique( + std::move(name), std::move(graph), nullptr); + auto ret = fn.get(); + register_function(std::move(fn)); + return ret; + } + + std::vector get_functions() const { + return fmap(functions_, [](const std::unique_ptr& fn) { + return fn.get(); + }); + } + + /// Run a method from this compilation. + /// + /// For example: + /// @code + /// IValue output = module->run("relu_script", a, b); + /// @endcode + /// + /// To get a compile a module from a source string, see torch::jit::compile + /// + /// @param method_name The name of the method to run + /// @param args Arguments to be passed to the method + /// @return An IValue containing the return value (or values if it is a tuple) + /// from the method + template + IValue run_method(const c10::QualifiedName& method_name, Types&&... args) { + return get_function(method_name)({IValue(std::forward(args))...}); + } + + void drop_all_functions() { + dict_.clear(); + functions_.clear(); + } + + /** + * Register a class as being owned by this compilation unit. + */ + void register_type(c10::NamedTypePtr namedType) { + // TODO: class types cannot be redefined because we have no way right now + // of invalidating their methods. NamedTuples are fine though, since they + // don't have methods. + TORCH_CHECK( + 0 == classDict_.count(*namedType->name()), + "class '", + namedType->name()->qualifiedName(), + "' already defined."); + classes_.push_back(std::move(namedType)); + classDict_[*classes_.back()->name()] = classes_.size() - 1; + } + + c10::ClassTypePtr get_class(const c10::QualifiedName& name) const { + auto type = get_type(name); + if (!type) { + return nullptr; + } + return type->cast(); + } + + c10::InterfaceTypePtr get_interface(const c10::QualifiedName& name) const { + auto type = get_type(name); + if (!type) { + return nullptr; + } + return type->cast(); + } + + c10::TupleTypePtr get_named_tuple(const c10::QualifiedName& name) const { + for (const auto& cls : classes_) { + if (cls->name()->qualifiedName() == name.qualifiedName()) { + return cls->expect(); + } + } + return nullptr; + } + + c10::NamedTypePtr get_type(const c10::QualifiedName& name) const { + auto it = classDict_.find(name); + if (it == classDict_.end()) { + return nullptr; + } + return classes_[it->second]; + } + + // For testing: clear all Python-defined classes to ensure that unit tests + // have isolation. + void _clear_python_cu() { + // Delete all the associated class methods + for (const auto& type : classes_) { + if (auto cls = type->cast()) { + for (auto method : cls->methods()) { + // Tombstone the method in the compilation unit. + // Don't erase because the dict_ + auto it = dict_.find(method->qualname()); + if (it != dict_.end()) { + functions_[it->second] = nullptr; + // Erase in our big lookup table + dict_.erase(it); + } + } + // Classes can have multiple pointers to the same hook, + // need to make sure to not delete it twice + std::unordered_set hooks_to_delete; + for (const auto& hook : cls->getForwardHooks()) { + hooks_to_delete.insert(hook); + } + for (const auto& pre_hook : cls->getForwardPreHooks()) { + hooks_to_delete.insert(pre_hook); + } + for (const auto& hook : hooks_to_delete) { + // Tombstone the hook in the compilation unit. + auto it = dict_.find(hook->qualname()); + if (it != dict_.end()) { + functions_[it->second] = nullptr; + // Erase in our big lookup table + dict_.erase(it); + } + } + } + } + classes_.clear(); + classDict_.clear(); + } + + // [Internal Only] Remove method. + // Note Used for freezing. + void unsafeRemoveMethod(const c10::QualifiedName& method_name) { + auto it = dict_.find(method_name); + TORCH_CHECK( + it != dict_.end(), + "method '", + method_name.qualifiedName(), + "' does not exist."); + functions_[it->second] = nullptr; + dict_.erase(it); + } + + // [name mangling] All code objects must have a unique qualified name in a + // CompilationUnit. In Python, sometimes functions won't have unique qualified + // name (for example, nested functions). So we mangle Python functions to + // ensure that they are uniquely named. + // + // We also use mangling to distinguish different Module instances. Since each + // Module is a singleton class instance, different instances of the same + // Python Module will have different types but the same qualified name. + c10::QualifiedName mangle(const c10::QualifiedName& name) const { + auto mangled = name; + while (get_type(mangled) || find_function(mangled)) { + mangled = mangler_.mangle(mangled); + } + return mangled; + } + + private: + std::unique_ptr define( + const std::optional& prefix, + const Def& def, + const ResolverPtr& resolver, + const Self* self, + const std::unordered_map& function_table, + bool shouldMangle = false, + FunctionType type = FunctionType::Method, + std::optional version = std::nullopt) const; + + // Define a property on \p self. + struct PropertyPair; + PropertyPair define_property( + const std::optional& prefix, + const Property& prop, + const ResolverPtr& resolver, + const Self* self, + const std::unordered_map& function_table, + bool shouldMangle = false) const; + + Function& register_function(std::unique_ptr fn) { + TORCH_CHECK( + 0 == dict_.count(fn->qualname().qualifiedName()), + "method '", + fn->qualname().qualifiedName(), + "' already defined."); + functions_.emplace_back(std::move(fn)); + dict_[functions_.back()->qualname()] = functions_.size() - 1; + return *functions_.back(); + } + std::vector> functions_; + // for fast lookup + std::unordered_map dict_; + std::unordered_map classDict_; + + // [class ownership] Right now there are two relationships between classes + // and compilation units: + // 1. Classes have compilation units internally that hold their methods. + // 2. On load, the TypePtrs of any imported classes are owned by the main + // module's compilation unit. + std::vector classes_; + + mutable NameMangler mangler_; +}; + +// An owning pointer to a Function. Just a pair of a raw Function ptr and it's +// owning CU. We need this because pybind requires a ref-counted way to refer to +// Functions. +struct StrongFunctionPtr { + StrongFunctionPtr(std::shared_ptr cu, Function* function) + : cu_(std::move(cu)), function_(function) { + TORCH_INTERNAL_ASSERT(cu_); + TORCH_INTERNAL_ASSERT(function_); + } + std::shared_ptr cu_; + Function* function_; +}; + +namespace script { +// We once had a `script::` namespace that was deleted. This is for backcompat +// of the public API; new code should not use this type alias. +using CompilationUnit = ::torch::jit::CompilationUnit; +} // namespace script +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/function_impl.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/function_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..b5d336db2b6ec2495779c0063340b7348a82321f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/function_impl.h @@ -0,0 +1,180 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +struct TORCH_API GraphFunction : public Function { + GraphFunction( + c10::QualifiedName name, + std::shared_ptr graph, + std::function function_creator, + std::optional executor_execution_mode = + std::nullopt) + : name_(std::move(name)), + graph_(std::move(graph)), + executor_execution_mode_(executor_execution_mode), + function_creator_(std::move(function_creator)) {} + + bool isGraphFunction() const override { + return true; + } + + void run(Stack& stack) override; + + std::function function_creator() const { + return function_creator_; + } + + c10::intrusive_ptr runAsync( + Stack& stack, + TaskLauncher taskLauncher = at::launch) override; + + std::shared_ptr graph() const { + return graph_; + } + + std::shared_ptr optimized_graph() const; + + const c10::QualifiedName& qualname() const override { + return name_; + } + + // private/unstable api. sets the initial execution mode + // will not affect executor if there is an existing executor + // created for this function + void _set_initial_executor_execution_mode(ExecutorExecutionMode mode) { + executor_execution_mode_ = mode; + } + // private/unstable api. sets flag of whether or not to ignore amp. + // will not affect executor if there is an existing executor + // created for this function + void _set_ignore_amp(bool ignore_amp) { + force_no_amp_ = ignore_amp; + } + + // if this isn't yet defined, run its method_creator function + void ensure_defined() override; + + size_t num_inputs() const override { + return graph()->inputs().size(); + } + + Function& setSchema(FunctionSchema schema) override { + schema_ = std::make_unique(std::move(schema)); + return *this; + } + + const FunctionSchema& getSchema() const override; + + GraphExecutorState getDebugState() { + return get_executor().getDebugState(); + } + + bool is_optimized() const { + TORCH_WARN( + "GraphFunction::is_optimized() is deprecated and always returns true. " + "Please use getGraphExecutorOptimize()"); + return true; + } + + void check_single_output() { + TORCH_CHECK( + graph()->outputs().size() == 1, + "Method (but not graphs in general) require a single output. Use None/Tuple for 0 or 2+ outputs"); + } + + GraphExecutor& get_executor() { + ensure_defined(); + std::lock_guard lock(compile_mutex); + auto& executor = executors_[currentSpecialization()]; + if (executor) { + return *executor; + } + check_single_output(); + const std::string& name = name_.name(); + std::shared_ptr opt_graph = optimized_graph(); + if (!executor_execution_mode_) { + executor = GraphExecutor(opt_graph, name); + } else { + executor = GraphExecutor(opt_graph, name, *executor_execution_mode_); + } + return *executor; + } + + using Function::call; + bool call( + Stack& stack, + std::optional bailOut, + c10::function_ref f) override { + f(get_executor().getPlanFor(stack, bailOut).code); + return true; + } + + void clear_optimized_graphs() { + optimized_graphs_.fill(nullptr); + } + + private: + enum SpecializationKey { + AutocastOff, + CpuAutocastOn, + GpuAutocastOn, + CpuGpuAutocastOn, + + // This provides the number of specializations + // (Must be last entry) + TotalCount + }; + + SpecializationKey currentSpecialization() const; + + private: + c10::QualifiedName name_; + // The original, non-optimized graph + std::shared_ptr graph_; // for debugging and for inlining + + // allows users to specify Simple/Profiling Executor for function + // TODO: add more executors + mutable std::optional executor_execution_mode_; + + // if invoked on a graph that has already traced through amp + // don't invoke amp pass + mutable bool force_no_amp_ = false; + // Optimized graph, computed lazily. Used for inlining. + mutable std::array, SpecializationKey::TotalCount> + optimized_graphs_; + + // GraphFunctions are invokable from multiple threads, so this lock needs to + // be held when we're initializing graph executor for the first time or + // computing the optimized graph. We're using reentrant mutex so that we don't + // need to worry about causing a deadlock by calling one method from another + // (e.g. optimized_graph() from get_executor()). + mutable std::recursive_mutex compile_mutex; + + // executor_[0] - autocast off + // executor_[1] - autocast cpu on + // executor_[2] - autocast gpu on + // executor_[3] - autocast cpu & gpu on + std::array, SpecializationKey::TotalCount> + executors_; + + // an optional function that actually creates the method when + // ensure_defined() is called. This is used by the compiler so + // that it can construct methods out of order + std::function function_creator_; + + // if absent, then we generate a default schema based on the graph + // mutable because getSchema caches the default schema if one is requested + // before a call to setSchema + mutable std::unique_ptr schema_; +}; + +// Short hands for dynamic_cast. +TORCH_API GraphFunction* tryToGraphFunction(Function&) noexcept; +TORCH_API GraphFunction& toGraphFunction(Function&); +TORCH_API const GraphFunction& toGraphFunction(const Function&); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/method.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/method.h new file mode 100644 index 0000000000000000000000000000000000000000..28675e5bd059f5e876e1b55c94b2c0a705aca28c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/method.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +using ObjectPtr = c10::intrusive_ptr; + +// A method in a module, e.g. f in: +// +// class M(ScriptModule): +// @script_method +// def f(self, x): +// ... +// Note: because Method/Module are exposed to python these +// classes use python method naming conventions +struct TORCH_API Method : public torch::IMethod { + Method(ObjectPtr owner, Function* function); + + // the module that contains this method. + Module owner() const; + // the raw objectptr that owns this method, for when the method is owned by a + // torchbind object. + ObjectPtr raw_owner() const; + void run(Stack& stack); + void run(Stack&& stack) { + run(stack); + } + + c10::IValue operator()( + std::vector stack, + const Kwargs& kwargs = Kwargs()) const override; + + // Run method async. Invocation on this function would invokes a JIT + // interpreter that executes ops inline, one by one, on caller's thread. A + // model can utilize async op, i.e. `fork`, to launch an asynchronous task + // which will be launched on provided `taskLauncher`. + c10::intrusive_ptr run_async( + std::vector stack, + const Kwargs& kwargs = Kwargs(), + TaskLauncher taskLauncher = at::launch); + + std::shared_ptr graph() const { + return toGraphFunction(*function_).graph(); + } + + const std::string& name() const override { + return function_->name(); + } + + size_t num_inputs() const { + return function_->num_inputs(); + } + + GraphExecutor& get_executor() { + return toGraphFunction(*function_).get_executor(); + } + + Function& function() const { + return *function_; + } + + private: + void setArgumentNames(std::vector&) const override; + + // Methods are uniqued onwed by a single module. This raw pointer allows + // looking up the module. + ObjectPtr owner_; + + // Underlying unbound function + Function* function_; +}; + +namespace script { +// We once had a `script::` namespace that was deleted. This is for backcompat +// of the public API; new code should not use this type alias. +using Method = ::torch::jit::Method; +} // namespace script + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/module.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/module.h new file mode 100644 index 0000000000000000000000000000000000000000..8e9be1de48a5fd0ae0cc1bdccdc848bb1af81cc2 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/module.h @@ -0,0 +1,685 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This file contains classes which assist in desugaring Python style +// modules and their methods into flattened graphs which don't have any +// function calls. + +namespace torch::jit { + +using ::c10::Argument; +using ::c10::FunctionSchema; +using ::c10::QualifiedName; +// Map which stores filename to content. +using ExtraFilesMap = std::unordered_map; + +using ModulePtr = c10::intrusive_ptr; + +struct Module; + +template +struct slot_list_impl; + +template +struct Named { + std::string name; + T value; +}; + +using NameModule = Named; +using NameValue = Named; +using NameTensor = Named; + +namespace detail { +struct TORCH_API ModulePolicy; +struct TORCH_API ParameterPolicy; +struct TORCH_API AttributePolicy; +struct TORCH_API BufferPolicy; +template +struct NamedPolicy; +} // namespace detail + +using module_list = slot_list_impl; +using named_module_list = + slot_list_impl>; + +using parameter_list = slot_list_impl; +using named_parameter_list = + slot_list_impl>; + +using attribute_list = slot_list_impl; +using named_attribute_list = + slot_list_impl>; + +using buffer_list = slot_list_impl; +using named_buffer_list = + slot_list_impl>; + +using ModuleLookup = std::function&)>; + +struct TORCH_API Module : public Object { + explicit Module(c10::QualifiedName class_name); + Module(std::shared_ptr cu, const c10::ClassTypePtr& type); + Module() = default; + Module(const Module&) = default; + Module& operator=(const Module&) = default; + Module(Module&&) noexcept = default; + Module& operator=(Module&&) noexcept = default; + Module( + c10::QualifiedName, + std::shared_ptr cu, + bool shouldMangle = false); + Module(ModulePtr module_value) : Object(std::move(module_value)) {} + ~Module() = default; + + void set_optimized(bool o) { + TORCH_WARN( + "Module::set_optimized() is deprecated and has no effect. " + "Please use setGraphExecutorOptimize()"); + } + + bool is_optimized() const { + TORCH_WARN( + "Module::is_optimized() is deprecated and always returns true. " + "Please use getGraphExecutorOptimize()"); + return true; + } + + IValue forward(std::vector inputs, const Kwargs& kwargs = Kwargs()) { + return get_method("forward")(std::move(inputs), kwargs); + } + + // In script modules, buffers are Tensors attribute that are _not_ registered + // as parameters. This is different than in nn.Module where there is a special + // register_buffer method. With this simplification, we only need to track + // whether a slot is a parameter to be able to classify it. + void register_buffer(const std::string& name, at::Tensor v) { + bool is_param = false; + bool is_buffer = true; + std::lock_guard lock(*register_mutex_); + type()->addOrCheckAttribute(name, TensorType::get(), is_param, is_buffer); + _ivalue()->setAttr(name, std::move(v)); + } + + void register_parameter( + const std::string& name, + at::Tensor v, + bool is_buffer) { + std::lock_guard lock(*register_mutex_); + type()->addOrCheckAttribute(name, TensorType::get(), !is_buffer, is_buffer); + _ivalue()->setAttr(name, std::move(v)); + } + + void register_attribute( + const std::string& name, + const TypePtr& t, + IValue v, + bool is_param = false, + bool is_buffer = false) { + type()->addOrCheckAttribute(name, t, is_param, is_buffer); + _ivalue()->setAttr(name, std::move(v)); + } + + void register_module(const std::string& name, const Module& module) { + type()->addOrCheckAttribute(name, module.type()); + _ivalue()->setAttr(name, module._ivalue()); + } + + void apply(const std::function& fn); + + buffer_list buffers(bool recurse = true) const; + named_buffer_list named_buffers(bool recurse = true) const; + + module_list children() const; // direct modules + named_module_list named_children() const; + module_list modules() const; // all modules, including this one, recursively + named_module_list named_modules() const; + + // all tensors involved in gradient optimization + parameter_list parameters(bool recurse = true) const; + named_parameter_list named_parameters(bool recurse = true) const; + + // all members of the object, similar to iterating over dir(obj) in python + attribute_list attributes(bool recurse = true) const; + named_attribute_list named_attributes(bool recurse = true) const; + + void dump( + bool print_method_bodies, + bool print_attr_values, + bool print_param_values) const; + + std::string dump_to_str( + bool print_method_bodies, + bool print_attr_values, + bool print_param_values) const; + + /// Enables "training" mode. + void train(bool on = true); + /// Calls train(false) to enable "eval" mode. + /// Do not override this method, override `train()` instead. + void eval() { + train(/*on=*/false); + } + /// True if the module is in training mode. + bool is_training() const { + return attr("training", true).toBool(); + } + + /// Recursively casts all parameters to the given `dtype` and `device`. + /// + /// If `non_blocking` is true and the source is in pinned memory and + /// destination is on the GPU or vice versa, the copy is performed + /// asynchronously with respect to the host. Otherwise, the argument has no + /// effect. + void to(at::Device device, at::ScalarType dtype, bool non_blocking = false); + + /// Recursively casts all parameters to the given dtype. + /// + /// If `non_blocking` is true and the source is in pinned memory and + /// destination is on the GPU or vice versa, the copy is performed + /// asynchronously with respect to the host. Otherwise, the argument has no + /// effect. + void to(at::ScalarType dtype, bool non_blocking = false); + + /// Recursively moves all parameters to the given device. + /// + /// If `non_blocking` is true and the source is in pinned memory and + /// destination is on the GPU or vice versa, the copy is performed + /// asynchronously with respect to the host. Otherwise, the argument has no + /// effect. + void to(at::Device device, bool non_blocking = false); + + void save( + std::ostream& out, + const ExtraFilesMap& extra_files = ExtraFilesMap()) const; + + void save( + const std::string& filename, + const ExtraFilesMap& extra_files = ExtraFilesMap()) const; + + void _save_for_mobile( + std::ostream& out, + const ExtraFilesMap& extra_files = ExtraFilesMap(), + bool save_mobile_debug_info = false, + bool use_flatbuffer = false) const; + + void _save_for_mobile( + const std::string& filename, + const ExtraFilesMap& extra_files = ExtraFilesMap(), + bool save_mobile_debug_info = false, + bool use_flatbuffer = false) const; + + Module copy() const; + + Module deepcopy(std::optional device = std::nullopt) const; + + // Clones both the underlying `ClassType` and the module instance(data), this + // function creates a new `ClassType` and returns a new instance that has the + // same data as the current instance but with the new type, shared ClassType + // will be preserved as well + Module clone(bool inplace = false) const; + + // Clones both the underlying `ClassType` and the module instance(data), this + // function creates a new `ClassType` and returns a new instance that has the + // same data as the current instance but with the new type, shared ClassType + // will be preserved as well. Also allows the caller to specify a set of + // method and attribute names to not clone. + Module clone( + bool inplace, + const std::unordered_set& ignored_method, + const std::unordered_set& ignored_attributes) const; + + void clone_method(const Module& orig, const std::string& name); + + IValue operator()(std::vector inputs); + + template + IValue create_class(const c10::QualifiedName& name, Types&&... args) const { + return create_class(name, {IValue(std::forward(args))...}); + } + + IValue create_class(const c10::QualifiedName& name, Stack stack) const; + + inline bool operator==(const Module& y) const noexcept { + return _ivalue() == y._ivalue(); + } + + void set_delete_memory(std::shared_ptr delete_mem) { + mem_to_delete_ = std::move(delete_mem); + } + + // A set of functions to maintain input shapes through torch.jit.save and + // torch.jit.load. It only works on tensors and lists/dicts of tensors + // because tracing is only supported by these types. + void store_traced_inputs( + const std::string& func_name, + std::vector inputs) { + if (inputs.empty()) { + return; + } + auto c10_inputs = c10::impl::GenericList(AnyType::get()); + for (IValue& value : inputs) { + // Not checking whether this is traceable type as that is already checked + // higher up in the stack and changing that would require a larger + // restructuring. + c10_inputs.emplace_back(std::move(value)); + } + traced_inputs_.insert_or_assign(func_name, c10_inputs); + } + + c10::Dict retrieve_traced_inputs() + const { + return traced_inputs_; + } + + private: + Module clone_impl( + std::unordered_map& type_remap, + bool inplace, + IValue::HashIdentityIValueMap memo, + const std::unordered_set& ignored_methods, + const std::unordered_set& ignored_attributes) const; + + void clone_method( + const Module& orig, + const Function& method, + const std::unordered_map& type_remap); + + c10::QualifiedName getNameForMethod(std::string basename) const { + return QualifiedName(*type()->name(), std::move(basename)); + } + + void to_impl( + const std::optional& device, + const std::optional& dtype, + bool non_blocking); + + // Extra handle for the module to delete when itself is deleted + std::shared_ptr mem_to_delete_; + + // Map of function names to the traced inputs that they have been traced with + c10::Dict traced_inputs_; + + // Mutex to keep registring buffer or parameter thread safe. + std::shared_ptr register_mutex_ = std::make_shared(); +}; + +// C++ equivalent api of `torch.jit.freeze`. See documentation there for +// details. +TORCH_API Module freeze( + const Module& module, + const std::optional>& preserved_attrs = + std::nullopt, + bool optimize_numerics = true); + +// C++ equivalent api of `torch.jit.optimize_for_inference`. See documentation +// there for details. +TORCH_API Module optimize_for_inference( + Module& module, + const std::vector& other_methods = {}); + +enum class FusionBehavior { STATIC, DYNAMIC }; + +using FusionStrategy = std::vector>; +// clang-format off +/* +Sets the type and number of specializations that can occur during fusion. + +Usage: provide a list of pairs (type, depth) where type is one of STATIC or DYNAMIC +and depth is an integer. + +Behavior - static vs dynamic: + In STATIC fusion, fused ops are compiled to have fixed input shapes. The shape is determined + based on some initial profiling runs. + In DYNAMIC fusion, fused ops are compiled to have variable input shapes, so that multiple + shapes are possible. + +In both cases, we also recompile on new striding behavior, device, or dtype. + +Behavior - fallback functions & depth: + When an input doesn't match the format required by the specialized compiled op, it will run + a fallback function. Fallback functions are recursively be compiled and specialized based + on the observed tensor shapes. Since compilation can be slow, the "depth" parameter is provided to + limit the number of specializations that can be compiled, before giving up on recompiling and + falling back to a completely un-fused, un-specialized implementation. + +The list of (type, depth) pairs controls the type of specializations and the number of +specializations. For example: [(STATIC, 2), (DYNAMIC, 2)] indicates that the first +two specializations will use static fusions, the following two specializations will use +dynamic fusion, and any inputs that satisfy none of the 4 options will run an +unfused implementation. + +NB: in the future, if more as more fusion backends are added there may be more granular +apis for specific fusers. +*/ +// clang-format on +TORCH_API FusionStrategy getFusionStrategy(); +// returns previous strategy +TORCH_API FusionStrategy setFusionStrategy(FusionStrategy& fusion_strategy); + +namespace detail { + +struct TORCH_API SlotCursor { + Module module_; + int64_t i_; // slot offset, -1 indicates the module itself +}; + +} // namespace detail + +// This iterator allows the (optionally recursive) enumeration of +// the members of a Module. It performs a depth-first pre-order +// traversal of the module. The Policy template parameter determines +// which slots of the object should be included. For instance, +// when iterating parameters, we return the parameter tensors, +// but skip modules, buffers, and other attributes. +// See ModulePolicy for comments about Policy object's API. +template +struct slot_iterator_impl { + using SlotCursor = detail::SlotCursor; + using value_type = typename Policy::value_type; + slot_iterator_impl( + Module root, + bool recurse, // if true, do a depth-first search, otherwise, just look at + // slots of root + bool return_module) // if true include root itself as the first thing + // visited (used in modules()) + : cursors_({SlotCursor{std::move(root), return_module ? -1 : 0}}), + recurse_(recurse) { + // advance iterator to first valid element (or the end, if empty) + while_not_valid_next(); + } + // empty cursors_, represents end of iteration + slot_iterator_impl() : recurse_(false) {} + value_type operator*() const { + return Policy::create(cursors_, cur()); + } + value_type operator->() const { + return **this; + } + slot_iterator_impl& operator++() { + next_valid(); + return *this; + } + slot_iterator_impl operator++(int) { + // this is really expensive, should we delete it so people don't use it + // instead of prefix? + slot_iterator_impl old = *this; + ++(*this); + return old; + } + + private: + // return_module() is a corner case where instead of returning a submodule + // of root, we are returning root itself, because we are iterating modules(), + // which contains the root module itself. + // It is represented with a single SlotCursor whose index is -1. + bool return_module() const { + return top().i_ == -1; + } + const SlotCursor& top() const { + return cursors_.back(); + } + SlotCursor& top() { + return cursors_.back(); + } + IValue cur() const { + return return_module() ? top().module_._ivalue() + : top().module_._ivalue()->getSlot(top().i_); + } + + // advance to the next slot in a depth first pre-order traversal of the + // modules slots. This function does not guarantee the next slot is a + // valid element of the iteration. That is done by valid(). + // invariant: !cursors_.empty() + void next() { + // we just returned the module itself, advance i_ to 0 so we are now + // at the first slot of the module. + if (return_module()) { + ++top().i_; + return; + } + // the last traversal action advanced beyond the number of slots in the + // module so continue the iteration in the parent. + if (top().i_ >= int64_t(top().module_._ivalue()->type()->numAttributes())) { + cursors_.pop_back(); + if (!cursors_.empty()) { + ++top().i_; + } + return; + } + // if the current thing is a module, we have to scan it for recursive + // traversals. We do this by adding a new SlotCursor to track the traversal. + if (recurse_ && + top().module_._ivalue()->type()->getAttribute(top().i_)->is_module()) { + cursors_.emplace_back(SlotCursor{cur().toModule(), 0}); + return; + } + // common case: advance to the next slot. + ++top().i_; + } + // is the current position of the iterator a valid one? + // otherwise, we have to continue advancing. + bool valid() const { + return top().i_ < + int64_t(top().module_._ivalue()->type()->numAttributes()) && + Policy::valid( + top().module_._ivalue()->type(), + top().i_, + top().module_._ivalue()->getSlot(top().i_)); + } + void while_not_valid_next() { + // advance iteration until we are either at the end (cursors_.empty()) + // or in a valid state. return_module() is a special case, + // and is always considered valid, regardless of Policy, because it is + // it is only true when we are iterating modules. + while (!cursors_.empty() && !return_module() && !valid()) { + next(); + } + } + void next_valid() { + // avoid crashing if this is empty + if (cursors_.empty()) { + return; + } + // advance to next element, which is maybe not valid + next(); + while_not_valid_next(); + } + + std::vector cursors_; + bool recurse_; + + friend inline bool operator!=( + const slot_iterator_impl& a, + const slot_iterator_impl& b) { + // we are finished iteration when we have no more iteration SlotCursors. + // end is always an empty iterator with no cursors. + return (a.cursors_.empty() != b.cursors_.empty()); + } +}; + +// This type represents lists of parameters, attributes, and +// submodules contained in the module. It is abstract because +// they are not stored directly in std::vectors but inside the +// module's IValue object itself. +template +struct slot_list_impl { + using iterator = slot_iterator_impl; + using const_iterator = slot_iterator_impl; + using value_type = typename iterator::value_type; + slot_iterator_impl begin() const { + return slot_iterator_impl(module_, recurse_, return_module_); + } + slot_iterator_impl end() const { + return slot_iterator_impl(); + } + size_t size() const { + if (!size_) { + size_ = size_t(0); + for ([[maybe_unused]] const value_type& _ : *(this)) { + ++*size_; + } + } + return *size_; + } + + slot_list_impl(Module module, bool recurse, bool return_module) + : module_(std::move(module)), + recurse_(recurse), + return_module_(return_module), + size_(std::nullopt) { + if (!recurse && !return_module && Policy::all_slots) { + size_ = module_.num_slots(); + } + } + + private: + Module module_; + bool recurse_; + bool return_module_; + // size of this list, cached on first request + // when we need to filter the slot list + mutable std::optional size_; + friend struct Module; +}; + +namespace detail { + +// slot_iterator_impl always iterate over all the slots in a module, +// the Policy template argument determines slots should be returned and their +// types +struct TORCH_API ModulePolicy { + // the type of the value being returned + using value_type = Module; + + // the logic for creating the type being returned, given the raw IValue + // of that object. + static value_type create( + const std::vector& cursors, + IValue v) { + return Module(std::move(v).toObject()); + } + // is slot i in typ something that this iterator should return, otherwise, + // we skip it. + static bool valid(const ClassTypePtr& typ, size_t i, const IValue& v) { + return typ->getAttribute(i)->is_module(); + } + // are we going to return everything? If so, we can optimize the calculate + // of the size of the list. + static constexpr bool all_slots = false; +}; + +struct TORCH_API ParameterPolicy { + using value_type = at::Tensor; + static value_type create( + const std::vector& cursors, + IValue v) { + return std::move(v).toTensor(); + } + static bool valid(const ClassTypePtr& typ, size_t i, const IValue& v) { + return typ->is_parameter(i) && v.isTensor(); + } + static constexpr bool all_slots = false; +}; + +struct TORCH_API BufferPolicy { + using value_type = at::Tensor; + static value_type create( + const std::vector& cursors, + IValue v) { + return std::move(v).toTensor(); + } + static bool valid(const ClassTypePtr& typ, size_t i, const IValue& v) { + return typ->getAttribute(i)->isSubtypeOf(*TensorType::get()) && + typ->is_buffer(i); + } + static constexpr bool all_slots = false; +}; + +struct TORCH_API AttributePolicy { + using value_type = IValue; + static value_type create( + const std::vector& cursors, + IValue v) { + return v; + } + static bool valid(const ClassTypePtr& typ, size_t i, const IValue& v) { + return true; + } + static constexpr bool all_slots = true; +}; + +// take a Policy object, and make a version of it that returns the slot. +// along with the fully qualified name of that slot. This is used for the named_ +// variants like named_parameters(). +template +struct NamedPolicy { + using value_type = Named; + static value_type create( + const std::vector& cursors, + IValue v) { + std::string name; + if (cursors.size() == 1) { + name = (cursors.back().i_ == -1) ? "" : nameFragment(cursors.back()); + } else { + std::ostringstream ss; + for (const auto i : c10::irange(cursors.size())) { + if (i > 0) { + ss << "."; + } + ss << nameFragment(cursors[i]); + } + name = ss.str(); + } + return value_type{std::move(name), Policy::create(cursors, std::move(v))}; + } + static bool valid(const ClassTypePtr& t, size_t i, const IValue& v) { + return Policy::valid(t, i, v); + } + static constexpr bool all_slots = Policy::all_slots; + + private: + static std::string nameFragment(const detail::SlotCursor& f) { + return f.module_.type()->getAttributeName(f.i_); + } +}; + +} // namespace detail + +TORCH_API bool& getInlineEverythingMode(); + +namespace script { +// We once had a `script::` namespace that was deleted. This is for backcompat +// of the public API; new code should not use this type alias. +using Module = ::torch::jit::Module; +using ExtraFilesMap = ::torch::jit::ExtraFilesMap; +} // namespace script + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/object.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/object.h new file mode 100644 index 0000000000000000000000000000000000000000..8f0d11d718747412bd88ec87c237e1bddc26c1bf --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/api/object.h @@ -0,0 +1,200 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace torch::jit { + +struct Resolver; +using ResolverPtr = std::shared_ptr; + +using ObjectPtr = c10::intrusive_ptr; + +// Throw this in C++ land if `attr` fails. This will be converted to a Python +// AttributeError by the Python binding code +class ObjectAttributeError : public std::runtime_error { + public: + ObjectAttributeError(const std::string& what) : std::runtime_error(what) {} +}; + +struct TORCH_API Object { + Object() = default; + Object(const Object&) = default; + Object& operator=(const Object&) = default; + Object(Object&&) noexcept = default; + Object& operator=(Object&&) noexcept = default; + Object(ObjectPtr _ivalue) : _ivalue_(std::move(_ivalue)) {} + Object(std::shared_ptr cu, const c10::ClassTypePtr& type); + Object( + c10::QualifiedName, + std::shared_ptr cu, + bool shouldMangle = false); + + ObjectPtr _ivalue() const { + TORCH_INTERNAL_ASSERT(_ivalue_); + return _ivalue_; + } + + c10::ClassTypePtr type() const { + return _ivalue()->type(); + } + + struct Property { + std::string name; + Method getter_func; + std::optional setter_func; + }; + + void setattr(const std::string& name, c10::IValue v) { + if (_ivalue()->type()->hasConstant(name)) { + TORCH_CHECK( + false, + "Can't set constant '", + name, + "' which has value:", + _ivalue()->type()->getConstant(name)); + } else if (auto slot = _ivalue()->type()->findAttributeSlot(name)) { + const c10::TypePtr& expected = _ivalue()->type()->getAttribute(*slot); + TORCH_CHECK( + v.type()->isSubtypeOf(*expected), + "Expected a value of type '", + expected->repr_str(), + "' for field '", + name, + "', but found '", + v.type()->repr_str(), + "'"); + _ivalue()->setSlot(*slot, std::move(v)); + } else { + TORCH_CHECK(false, "Module has no attribute '", name, "'"); + } + } + + c10::IValue attr(const std::string& name) const { + if (auto r = _ivalue()->type()->findAttributeSlot(name)) { + return _ivalue()->getSlot(*r); + } + if (auto r = _ivalue()->type()->findConstantSlot(name)) { + return _ivalue()->type()->getConstant(*r); + } + std::stringstream err; + err << _ivalue()->type()->repr_str() << " does not have a field with name '" + << name.c_str() << "'"; + throw ObjectAttributeError(err.str()); + } + + c10::IValue attr(const std::string& name, c10::IValue or_else) const { + if (auto r = _ivalue()->type()->findAttributeSlot(name)) { + return _ivalue()->getSlot(*r); + } + if (auto r = _ivalue()->type()->findConstantSlot(name)) { + return _ivalue()->type()->getConstant(*r); + } + return or_else; + } + + bool hasattr(const std::string& name) const { + return _ivalue()->type()->hasAttribute(name) || + _ivalue()->type()->hasConstant(name); + } + + // each object owns its methods. The reference returned here + // is guaranteed to stay valid until this module has been destroyed + Method get_method(const std::string& name) const { + if (auto method = find_method(name)) { + return *method; + } + TORCH_CHECK(false, "Method '", name, "' is not defined."); + } + + const std::vector get_methods() const { + return c10::fmap(type()->methods(), [&](Function* func) { + return Method(_ivalue(), func); + }); + } + + bool has_property(const std::string& name) const { + for (const auto& prop : type()->properties()) { + if (prop.name == name) { + return true; + } + } + return false; + } + + const Property get_property(const std::string& name) const { + for (const auto& prop : type()->properties()) { + if (prop.name == name) { + std::optional setter = std::nullopt; + if (prop.setter) { + setter = Method(_ivalue(), prop.setter); + } + return Property{ + prop.name, Method(_ivalue(), prop.getter), std::move(setter)}; + } + } + TORCH_CHECK(false, "Property '", name, "' is not defined."); + } + + const std::vector get_properties() const { + return c10::fmap(type()->properties(), [&](ClassType::Property prop) { + std::optional setter = std::nullopt; + if (prop.setter) { + setter = Method(_ivalue(), prop.setter); + } + return Property{ + std::move(prop.name), + Method(_ivalue(), prop.getter), + std::move(setter)}; + }); + } + + std::optional find_method(const std::string& basename) const; + + /// Run a method from this module. + /// + /// For example: + /// @code + /// IValue output = module->run("relu_script", a, b); + /// @endcode + /// + /// To get a compile a module from a source string, see torch::jit::compile + /// + /// @param method_name The name of the method to run + /// @param args Arguments to be passed to the method + /// @return An IValue containing the return value (or values if it is a tuple) + /// from the method + template + IValue run_method(const std::string& method_name, Types&&... args) { + return get_method(method_name)({IValue(std::forward(args))...}); + } + + // so that C++ users can easily add methods + void define(const std::string& src, const ResolverPtr& resolver = nullptr); + + size_t num_slots() const { + return _ivalue()->slots().size(); + } + + // shallow copy the object + Object copy() const; + + // Copies all the attributes of the object recursively without creating new + // `ClassType`, including deepcopy of Tensors + Object deepcopy() const; + + private: + // mutable be we lazily initialize in module_object. + mutable ObjectPtr _ivalue_; +}; + +namespace script { +// We once had a `script::` namespace that was deleted. This is for backcompat +// of the public API; new code should not use this type alias. +using Object = ::torch::jit::Object; +} // namespace script +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend.h new file mode 100644 index 0000000000000000000000000000000000000000..a6b567c85480f20d06cfa9448e13b42639fede0b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include + +namespace torch::jit { +namespace { +// NOLINTNEXTLINE(clang-diagnostic-unneeded-internal-declaration) +inline c10::FunctionSchema getIsAvailableSchema() { + c10::Argument self("self", c10::AnyType::get()); + c10::Argument available("available", c10::BoolType::get()); + c10::FunctionSchema preprocessor_schema( + "is_available", + /*overload_name=*/"", + /*arguments=*/{self}, + /*returns=*/{available}); + return preprocessor_schema; +} + +constexpr static auto kBackendsNamespace = "__backends__"; + +// NOLINTNEXTLINE(clang-diagnostic-unneeded-internal-declaration) +inline c10::FunctionSchema getCompileSchema() { + c10::Argument self("self", c10::AnyType::get()); + c10::Argument mod("processed", c10::AnyType::get()); + auto any_dict_ty = + c10::DictType::create(c10::StringType::get(), c10::AnyType::get()); + c10::Argument method_compile_spec("method_compile_spec", any_dict_ty); + c10::Argument handles("handles", any_dict_ty); + + c10::FunctionSchema compile_schema( + "compile", + /*overload_name=*/"", + /*arguments=*/{self, mod, method_compile_spec}, + /*returns=*/{handles}); + return compile_schema; +} + +// NOLINTNEXTLINE(clang-diagnostic-unneeded-internal-declaration) +inline c10::FunctionSchema getExecuteSchema() { + auto any_list_ty = c10::ListType::create(c10::AnyType::get()); + c10::Argument self("self", c10::AnyType::get()); + c10::Argument handle("handle", c10::AnyType::get()); + c10::Argument input("input", any_list_ty); + c10::Argument output("output", any_list_ty); + return c10::FunctionSchema( + "execute", + /*overload_name=*/"", + /*arguments=*/{self, handle, input}, + /*returns=*/{output}); +} + +template +std::function getIsAvailableFunc() { + return [](Stack& stack) { + auto self = pop(stack).toCustomClass(); + auto ret = self->is_available(); + push(stack, ret); + }; +} + +template +std::function getCompileFunc() { + return [](Stack& stack) { + auto method_compile_spec = pop(stack).toGenericDict(); + auto processed = pop(stack); + auto self = pop(stack).toCustomClass(); + auto ret = self->compile(processed, method_compile_spec); + push(stack, ret); + }; +} + +template +std::function getExecuteFunc() { + return [](Stack& stack) { + auto args = pop(stack); + auto handle = pop(stack); + auto self = pop(stack); + auto backend = self.toCustomClass(); + auto res = backend->execute(handle, args.toList()); + push(stack, res); + }; +} +} // namespace + +// Static registration API for backends. +template +class backend { + static_assert( + std::is_base_of_v, + "torch::jit::backend requires T to inherit from PyTorchBackendInterface"); + std::string backend_name_; + + public: + // Registers a new backend with /p name, and the given /p preprocess + // function. + backend(const std::string& name) : backend_name_(name) { + static auto cls = torch::class_(kBackendsNamespace, name) + .def(torch::init<>()) + ._def_unboxed( + "is_available", + getIsAvailableFunc(), + getIsAvailableSchema()) + ._def_unboxed( + "compile", + getCompileFunc(), + getCompileSchema()) + ._def_unboxed( + "execute", + getExecuteFunc(), + getExecuteSchema()); + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_handler.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..4128832e7a078a456e247544724247c69dd184f2 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_handler.h @@ -0,0 +1,138 @@ +#pragma once +#include + +#include +#include +#include + +#include + +namespace torch::jit { + +/* + * BackendDebugHandleManager is responsible for issuing debug handles to + * backends. Debug handles are associated with nodes of a graph. + * BackendDebugHandleManager also maintains a map + * [debug-handle, DebugInfoTuple = {source range, inlined callstack ptr]} that + * will help generate a callstack for exception raised using debug handles. + * Effectively debug handles are something that is given to backend and later + * when an exception occurs in the backend, backend can tell, using debug + * handle, that an exception occurred here. Then the runtime can generate + * callstack correspoding to the exception. + * There are two parts to BackendDebugHandleManager: + * 1. static std::atomic debug_handle + * 2. Map of [debug-handle, DebugInfoTuple] + * + * About 1: + * Why do they have to be unique. The reason is that by ensuring + * uniqueness of debug handles, we remove the burden of another layer of + * mapping where we need to say this set of debug handles were generated for + * this lowered module or this bytecode function. This simplifies the API for + * serialization since debug handles can uniquely identify DebugInfoTuple. + * Thus simplifies the runtime API for throwing exception. Exception throwing + * only needs to know debug_handle and not which module or method threw it. + * There are 2 issues to keep in mind, though,for static std::atomic + * debug_handle: A. Performance implications of using atomic variable. However + * this is only used for compilation so we assume to absorb some of that + * penalty. Plus if there is no contention then we should have less to worry + * about. B. If repeated compilation is part of a long running process then we + * may overflow int64_t. We may detect and fail on this. For now this is not + * done. + * + * Now about 2: + * There are two usecases for [debug-handle, DebugInfoTuple] + * A. During bytecode generation the DebugInfoTuple corresponding to the nodes + * of the inlined graph being serialized, are stored in this object and a + * unique debug handle is returned. This unique debug handle is stored in + * mobile_debug info for pytorch lite models. It will be used for raising + * exceptions as well as profiling. B. During backend lowering, each backend's + * preprocess/compile method can compile method's graph and serialize those + * methods. Once the method is lowered to backend, graph is essentially lost. + * Without access to graph it is hard to generate model level debug info. Thus + * the debug handles provide a way to map nodes of the graph to the model level + * debug info. + * + * During byte-code model serialization, [debug-handle, DebugInfoTuple] is + * serialized. Now we know a. debug handles and b. how to map debug handles to + * model source code. Thus we can either do eager symbolication by converting + * debug handles to corresponding source code at runtime, or do lazy + * symbolicattion offline. + * + * Note that it is not necessary to serialize [debug-handle, DebugInfoTuple] + * corresponding to lowered backend if the lowering process, that is + * preprocess/compile, and execution happens in the same session, then eager + * symbolication can be employed. + * + * Now how does BackendDebugHandleManager capture all of the above? + * By providing two API. + * 1. getNextDebugHandle which given a Node* returns a unique debug handle, + * that will uniquely identify DebugInfoTuple. + * and + * 2. getCallStackPtrMap which returns the map + * [debug-handle, DebugInfoTuple] + * + * 1 provides debug handles to backends and 2 provides runtime a way to map + * debug handles to source level debug info. + * + * So why does debug handle map to DebugInfoTuple = {source range and inlined + * cs}? {debug_handle, source_range_tag, serialized_callstack} Take this + * example: class L(nn.Module): def __init__(self) -> None: + * ... + * def forward(self, x): + * return x * 5 + * class M(nn.Module): + * def __init__(self) -> None: + * ... + * def forward(self, x): + * return x - 2 + * class N(nn.Module): + * def __init__(self) -> None: + * self.m = M() + * def forward(self, x): + * return self.m(x) + 3 + * m = torch.jit.script(N()) + * Once you inline m's forward method, m.forward.graph will look something + * like this + * graph(%self...): + * %x = aten::mul(..) + * %x = aten::sub(x, ..) + * %y = aten::add(x, ..) + * .. + * Inlined callstack ptr for these two nodes will look like: + * aten::mul's inlined CS (callstack): [N.forward, source range] -> [M.forward, + * source range] aten::sub's inlined CS (callstack): [N.forward, source range] + * aten::add's inlined CS: null + * mul node's inlined CS contains only information about the callsites' source + * range The information about mul node's source range ('return x * 5') is not + * available in its inlined CS. It is rather part of node's source range + * instead of inlined CS. Thus to get full stack: [N.forward, source range] -> + * [M.forward, source range] -> [aten::mul's source range] We need to track + * mul's source range and inlined CS both. + */ + +using BackendDebugInfoMapType = + std::unordered_map; + +/* + * This class is used to generate debug info map. + * backend's preprocess will call generate_debug_handles (see + * backend_detail.cpp), which uses debug_handle_manager to generate debug + * handles. When lowering process finishes, calling stopRecording will + * return debug info map from debug_handle_manager + */ +class TORCH_API BackendDebugInfoRecorder { + public: + BackendDebugInfoRecorder() = default; + int64_t getNextDebugHandle(const Node* node); + // Reason this is not done as RAII is that work done in stopRecording + // can throw, and throwing with dtor will call terminate and thus voids any + // exception catching at a higher level. + BackendDebugInfoMapType stopRecording(); + NodeToDebugHandle generate_debug_handles(const std::shared_ptr& graph); + + private: + static std::atomic unique_debug_handle_; + BackendDebugInfoMapType handles_to_inlined_callstack_ptrs_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_info.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_info.h new file mode 100644 index 0000000000000000000000000000000000000000..d6740b6c50466abb9a322bdda302ecef2007831f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_debug_info.h @@ -0,0 +1,63 @@ +#pragma once + +#ifndef BUILD_LITE_INTERPRETER +#include +#endif +#include + +namespace torch::jit { + +constexpr static auto kBackendUtilsNamespace = "backendutils"; +constexpr static auto kBackendDebugInfoClass = "BackendDebugInfo"; + +#ifndef BUILD_LITE_INTERPRETER +/* + * Custom class for holding debug information in lowered modules, intended + * purely for keeping this information to be later serialized outside of the + * lowered module itself. + * Its usage pattern is: + * 1. LoweredModule declares an instance of this class in __backend_debug_info + * 2. During serialization, __backend_debug_info is used to obtain the debug + * information. + * 3. The contents of LoweredModule.__backend_debug_info are not serialized + * within the LoweredModule itself. + */ +class TORCH_API PyTorchBackendDebugInfo : public torch::CustomClassHolder { + public: + PyTorchBackendDebugInfo() = default; + + std::optional& getDebugInfoMap() { + return debug_info_map_; + } + + void setDebugInfoMap(BackendDebugInfoMapType&& debug_info_map) { + debug_info_map_ = std::move(debug_info_map); + } + + private: + std::optional debug_info_map_; +}; + +#else + +/* + * Dummy instance exists for the following reason: + * __backend_debug_info is of type BackendDebugInfo which is a torchbind' + * class backed by cpp class PyTorchBackendDebugInfo. + * PyTorchBackendDebugInfo, depends on ir.h., scope.h, source_range etc. + * We dont include this on lite interpreter side. Thus on lite interpreter side + * we cannot have valid definition of PyTorchBackendDebugInfo. However we do not + * need valid instance of __backend_debug_info in lite interpreter anyway as we + * dont serialize this info as part of LowerdModule as mentioned ealrier. + * However since LoweredModule has registered attribute of __backend_debug_info + * we still need to make sure that BackendDebugInfo is registered with + * TorchScript. However in this instance it does not have to be backed by + * PyTorchBackendDebugInfo, so we create a dummy PyTorchBackendDebugInfoDummy + * just for this purpose. + */ +class PyTorchBackendDebugInfoDummy : public torch::CustomClassHolder { + public: + PyTorchBackendDebugInfoDummy() = default; +}; +#endif +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_detail.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_detail.h new file mode 100644 index 0000000000000000000000000000000000000000..e69a93ebb148ec130b1f8d3f2523b53258b93c42 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_detail.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +#include + +namespace torch::jit { + +using DebugHandleType = int64_t; + +using NodeToDebugHandle = std::unordered_map; + +using BackendDebugHandleGenerator = + std::function&)>; + +namespace detail { + +using BackendPreprocessFunction = std::function&, + const BackendDebugHandleGenerator& generate_debug_handles)>; + +TORCH_API void registerBackendPreprocessFunction( + const std::string& name, + const BackendPreprocessFunction& preprocess); + +bool hasBackendPreprocessFunction(const std::string& name); + +BackendPreprocessFunction getBackendPreprocessFunction(const std::string& name); + +TORCH_API Module codegen_backend_module( + const std::string& backend_name, + const Module& orig_module, + const c10::Dict& method_compile_spec, + const c10::DictTypePtr& any_dict_ty); +} // namespace detail +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_exception.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_exception.h new file mode 100644 index 0000000000000000000000000000000000000000..d964f1bfcf008625da4db52b643c664d10c6f6cf --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_exception.h @@ -0,0 +1,56 @@ +#pragma once +#include + +#include + +namespace c10 { +class TORCH_API BackendRuntimeException : public c10::Error { + public: + // Use debug_handle to throw exception + BackendRuntimeException( + SourceLocation loc, + std::string msg, + int64_t debug_handle) + : c10::Error(loc, std::move(msg)) { + debug_handles.push_back(debug_handle); + } + // If rethrowing, can push another debug_handle + // This is useful in couple of scenarios. + // 1. A submodule is lowered and lite interperter has CallMethod + // to lowered module's method. In this case lowered module will throw with + // a handle, plus there will be another debug handle corresponding + // to the CallMethod node in lite interpreter. Both together give complete + // trace. This function allows lite interpreter to rethrow with debug + // handle it has for CallMethod. + // 2. Another scenarios is when lite interperter can make function calls or + // the lowered backend also has function call ability. Thus we have + // multiple function frames. Now we need a stack of handles to symbolicate + // entire stack trace. + void pushDebugHandle(int64_t debug_handle) { + debug_handles.push_back(debug_handle); + } + const std::vector& getDebugHandles() { + return debug_handles; + } + + private: + // Stores stack of debug handles. + std::vector debug_handles; +}; + +} // namespace c10 +#define TORCH_DELEGATED_BACKEND_THROW(cond, msg, debug_handle) \ + if (C10_UNLIKELY_OR_CONST(!(cond))) { \ + throw ::c10::BackendRuntimeException( \ + {__func__, __FILE__, static_cast(__LINE__)}, \ + msg, \ + debug_handle); \ + } + +#define TORCH_DELEGATED_BACKEND_RETHROW(e, debug_handle) \ + do { \ + e.pushDebugHandle(debug_handle); \ + throw; \ + } while (false) + +#define DEBUG_HANDLE_UNKNOWN -1 diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_init.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_init.h new file mode 100644 index 0000000000000000000000000000000000000000..7f2aac18bd04f8b71f8706d1256372f5a8f7c5ce --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_init.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace torch::jit { +// Initialize Python bindings for JIT to_ functions. +void initJitBackendBindings(PyObject* module); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_interface.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..331497f929d4c2efd4075374adf4f11d862027f8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_interface.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace torch::jit { + +// Interface for a JIT backend. +class TORCH_API PyTorchBackendInterface : public torch::CustomClassHolder { + public: + PyTorchBackendInterface() noexcept; + ~PyTorchBackendInterface() override; + + // Returns true if the backend is available to process delegation calls. + virtual bool is_available() = 0; + + // Compile the module contained in \p processed using the details provided in + // \p method_compile_spec for each module method that should be compiled for + // the backend. \p method_compile_spec should be of type Dict. + // \returns a dictionary of type Dict that contains a backend + // handle each method that can run on the backend (i.e. each key in \p + // method_compile_spec). + virtual c10::impl::GenericDict compile( + c10::IValue processed, + c10::impl::GenericDict method_compile_spec) = 0; + + // Execute the method specified by \p handle using \p inputs. \returns the + // outputs as a tuple. + virtual c10::impl::GenericList execute( + c10::IValue handle, + c10::impl::GenericList inputs) = 0; +}; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_preprocess.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_preprocess.h new file mode 100644 index 0000000000000000000000000000000000000000..da4ebd5a93754a58901eff92f2c96e7a87f70cce --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_preprocess.h @@ -0,0 +1,16 @@ +#pragma once + +#include +namespace torch::jit { +class backend_preprocess_register { + std::string backend_name_; + + public: + backend_preprocess_register( + const std::string& name, + const detail::BackendPreprocessFunction& preprocess) + : backend_name_(name) { + detail::registerBackendPreprocessFunction(name, preprocess); + } +}; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_resolver.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_resolver.h new file mode 100644 index 0000000000000000000000000000000000000000..9dd4483725766400afe341a3c4e311e77247fa90 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/backends/backend_resolver.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace torch::jit { +// Create a Resolver for use in generating LoweredModules for specific backends. +TORCH_API std::shared_ptr loweredModuleResolver(); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/codegen/cuda/interface.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/codegen/cuda/interface.h new file mode 100644 index 0000000000000000000000000000000000000000..926e4cb5d265c7a5d0851a8bceb6f12c76a754e8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/codegen/cuda/interface.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +/* + * This file contains APIs for cuda fuser; + * + * We use an empty static struct to hold the function pointers, which are + * registered separately. This is to support cpu-only compilation. + * Registration is done in torch/csrc/jit/codegen/cuda/register_interface.cpp + */ + +namespace torch::jit::fuser::cuda { + +TORCH_API std::atomic& getCudaFusionGuardMode(); + +TORCH_API bool getSingletonFusion(); +TORCH_API bool setSingletonFusion(bool value); +TORCH_API bool getHorizontalFusion(); +TORCH_API bool setHorizontalFusion(bool value); + +// dummy struct to allow API registration +struct CudaFuserInterface { + void (*fn_compile_n)(Node*) = nullptr; + void (*fn_run_n_s)(const Node*, Stack&) = nullptr; + void (*fn_fuse_graph)(std::shared_ptr&) = nullptr; + bool (*fn_can_fuse_n)(const Node*) = nullptr; + void (*fn_insert_profile_inodes)(ProfilingRecord* pr) = nullptr; + bool (*fn_profile_n)(const Node*) = nullptr; + bool (*fn_skip_n)(const std::string&, bool flip) = nullptr; +}; + +// Get interface, this is used by registration and user facing API internally +TORCH_API CudaFuserInterface* getFuserInterface(); + +TORCH_API void compileFusionGroup(Node* fusion_node); +TORCH_API void runFusionGroup(const Node* fusion_node, Stack& stack); +TORCH_API void fuseGraph(std::shared_ptr&); +TORCH_API bool canFuseNode(const Node* node); +TORCH_API void InsertProfileNodesForCUDAFuser(ProfilingRecord* pr); +TORCH_API bool profileNode(const Node* node); + +TORCH_API bool skipNode(const std::string& symbol_str, bool flip = true); + +TORCH_API bool isEnabled(); +TORCH_API bool setEnabled(bool is_enabled); +TORCH_API bool canBeEnabled(); + +} // namespace torch::jit::fuser::cuda diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_log.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_log.h new file mode 100644 index 0000000000000000000000000000000000000000..6282e53f4bff1773e7f8a8249e444c8e5da45d87 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_log.h @@ -0,0 +1,126 @@ +#pragma once +#include +#include +#include +#include +#include + +// `TorchScript` offers a simple logging facility that can enabled by setting an +// environment variable `PYTORCH_JIT_LOG_LEVEL`. + +// Logging is enabled on a per file basis. To enable logging in +// `dead_code_elimination.cpp`, `PYTORCH_JIT_LOG_LEVEL` should be +// set to `dead_code_elimination.cpp` or, simply, to `dead_code_elimination` +// (i.e. `PYTORCH_JIT_LOG_LEVEL=dead_code_elimination`). + +// Multiple files can be logged by separating each file name with a colon `:` as +// in the following example, +// `PYTORCH_JIT_LOG_LEVEL=dead_code_elimination:guard_elimination` + +// There are 3 logging levels available for your use ordered by the detail level +// from lowest to highest. + +// * `GRAPH_DUMP` should be used for printing entire graphs after optimization +// passes +// * `GRAPH_UPDATE` should be used for reporting graph transformations (i.e. +// node deletion, constant folding, etc) +// * `GRAPH_DEBUG` should be used for providing information useful for debugging +// the internals of a particular optimization pass or analysis + +// The default logging level is `GRAPH_DUMP` meaning that only `GRAPH_DUMP` +// statements will be enabled when one specifies a file(s) in +// `PYTORCH_JIT_LOG_LEVEL`. + +// `GRAPH_UPDATE` can be enabled by prefixing a file name with an `>` as in +// `>alias_analysis`. +// `GRAPH_DEBUG` can be enabled by prefixing a file name with an `>>` as in +// `>>alias_analysis`. +// `>>>` is also valid and **currently** is equivalent to `GRAPH_DEBUG` as there +// is no logging level that is higher than `GRAPH_DEBUG`. + +namespace torch::jit { + +struct Node; +struct Graph; + +enum class JitLoggingLevels { + GRAPH_DUMP = 0, + GRAPH_UPDATE, + GRAPH_DEBUG, +}; + +TORCH_API std::string get_jit_logging_levels(); + +TORCH_API void set_jit_logging_levels(std::string level); + +TORCH_API void set_jit_logging_output_stream(std::ostream& out_stream); + +TORCH_API std::ostream& get_jit_logging_output_stream(); + +TORCH_API std::string getHeader(const Node* node); + +TORCH_API std::string log_function(const std::shared_ptr& graph); + +TORCH_API ::torch::jit::JitLoggingLevels jit_log_level(); + +// Prefix every line in a multiline string \p IN_STR with \p PREFIX. +TORCH_API std::string jit_log_prefix( + const std::string& prefix, + const std::string& in_str); + +TORCH_API std::string jit_log_prefix( + ::torch::jit::JitLoggingLevels level, + const char* fn, + int l, + const std::string& in_str); + +TORCH_API bool is_enabled( + const char* cfname, + ::torch::jit::JitLoggingLevels level); + +TORCH_API std::ostream& operator<<( + std::ostream& out, + ::torch::jit::JitLoggingLevels level); + +#define JIT_LOG(level, ...) \ + if (is_enabled(__FILE__, level)) { \ + ::torch::jit::get_jit_logging_output_stream() \ + << ::torch::jit::jit_log_prefix( \ + level, __FILE__, __LINE__, ::c10::str(__VA_ARGS__)); \ + } + +// tries to reconstruct original python source +#define SOURCE_DUMP(MSG, G) \ + JIT_LOG( \ + ::torch::jit::JitLoggingLevels::GRAPH_DUMP, \ + MSG, \ + "\n", \ + ::torch::jit::log_function(G)); +// use GRAPH_DUMP for dumping graphs after optimization passes +#define GRAPH_DUMP(MSG, G) \ + JIT_LOG( \ + ::torch::jit::JitLoggingLevels::GRAPH_DUMP, MSG, "\n", (G)->toString()); +// use GRAPH_UPDATE for reporting graph transformations (i.e. node deletion, +// constant folding, CSE) +#define GRAPH_UPDATE(...) \ + JIT_LOG(::torch::jit::JitLoggingLevels::GRAPH_UPDATE, __VA_ARGS__); +// use GRAPH_DEBUG to provide information useful for debugging a particular opt +// pass +#define GRAPH_DEBUG(...) \ + JIT_LOG(::torch::jit::JitLoggingLevels::GRAPH_DEBUG, __VA_ARGS__); +// use GRAPH_EXPORT to export a graph so that the IR can be loaded by a script +#define GRAPH_EXPORT(MSG, G) \ + JIT_LOG( \ + ::torch::jit::JitLoggingLevels::GRAPH_DEBUG, \ + MSG, \ + "\n\n", \ + (G)->toString(), \ + ""); + +#define GRAPH_DUMP_ENABLED \ + (is_enabled(__FILE__, ::torch::jit::JitLoggingLevels::GRAPH_DUMP)) +#define GRAPH_UPDATE_ENABLED \ + (is_enabled(__FILE__, ::torch::jit::JitLoggingLevels::GRAPH_UPDATE)) +#define GRAPH_DEBUG_ENABLED \ + (is_enabled(__FILE__, ::torch::jit::JitLoggingLevels::GRAPH_DEBUG)) +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_opt_limit.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_opt_limit.h new file mode 100644 index 0000000000000000000000000000000000000000..c013431a19cd3e3a6773f4b36dc6627ddc3d21a7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/jit_opt_limit.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +// `TorchScript` offers a simple optimization limit checker +// that can be configured through environment variable `PYTORCH_JIT_OPT_LIMIT`. +// The purpose is to limit how many optimization you can make per pass. +// This is useful for debugging any passes. + +// Opt limit checker is enabled on a per file basis (hence per pass). For +// example, in `constant_propagation.cpp`, `PYTORCH_JIT_OPT_LIMIT` should be set +// to `constant_propagation=` or, simply, to +// `constant_propagation=` where is the number of +// optimizations you want to make for the pass. (i.e. +// `PYTORCH_JIT_OPT_LIMIT="constant_propagation="`). + +// Multiple files can be configured by separating each file name with a colon +// `:` as in the following example, +// `PYTORCH_JIT_OPT_LIMIT="constant_propagation=:dead_code_elimination="` + +// You can call opt limiter by calling JIT_OPT_ALLOWED. It will return true if +// we haven't reached the optimization limit yet. Otherwise, it will return +// false. Typical usage: + +// if (!JIT_OPT_ALLOWED) { +// GRAPH_DUMP(...); //supplied from jit_log +// return; +// } + +namespace torch::jit { + +TORCH_API bool opt_limit(const char* pass_name); + +#define JIT_OPT_ALLOWED opt_limit(__FILE__) + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/add_if_then_else.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/add_if_then_else.h new file mode 100644 index 0000000000000000000000000000000000000000..6495e1eaed5838bc5ae742739195e4ae1ca47919 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/add_if_then_else.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API bool AddIfThenElseOp(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/annotate_warns.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/annotate_warns.h new file mode 100644 index 0000000000000000000000000000000000000000..5a683c2b5034bd7f17ea20626c3591e21a5a798a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/annotate_warns.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void AnnotateWarns(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/autocast.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/autocast.h new file mode 100644 index 0000000000000000000000000000000000000000..ea3de368404c9c620ee032c2bd77e968ec917dbd --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/autocast.h @@ -0,0 +1,13 @@ + +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void Autocast(const std::shared_ptr& graph); + +TORCH_API bool setAutocastMode(bool value); +TORCH_API bool autocastEnabled(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/bailout_graph.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/bailout_graph.h new file mode 100644 index 0000000000000000000000000000000000000000..b526d418b1f7c82ff4f92c769e96a4c434da9a11 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/bailout_graph.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace torch::jit { + +// Replaces prim::Guard nodes with prim::BailOut nodes and +// computes sets of inputs needed to resume execution at +// bailout points +TORCH_API void InsertBailOuts(std::shared_ptr graph); + +// Builds a bailout graph into `target` (which is an empty graph) +// for a given bailout point `bailout_index` +// from the original graph `orig` (the original unoptimized graph) +// BailOut graphs allow Interpreter to resume +// execution of the (un/de)optimized graph (i.e. +// a graph that doesn't rely on any assumptions derived from +// on profiling information) from a given BailOut point +// should any of the assumptions fail for an actual input. +TORCH_API std::shared_ptr BuildBailOutGraphFrom( + int64_t bailout_index, + const std::shared_ptr& orig, + const std::shared_ptr& target); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/batch_mm.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/batch_mm.h new file mode 100644 index 0000000000000000000000000000000000000000..51bc1a9337bfde2303879dd185aaafc2b904e466 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/batch_mm.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void BatchMM(std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize.h new file mode 100644 index 0000000000000000000000000000000000000000..46d941aabab22a47b5b4eb7c145789c1ee503426 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API std::shared_ptr Canonicalize( + const std::shared_ptr& graph, + bool keep_unique_names = true); + +TORCH_API void CanonicalizeOutputs(std::shared_ptr& graph); + +TORCH_API std::optional firstOrLastUse(Value* v, bool find_first); + +TORCH_API bool isBeforeOrAfter( + const Use& a, + const Use& b, + bool checking_before); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize_graph_fuser_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize_graph_fuser_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..6e95cc199d2b261e04bdcd52d4a95ffdaceb4a01 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/canonicalize_graph_fuser_ops.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void CanonicalizeOps(const std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/check_strict_fusion.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/check_strict_fusion.h new file mode 100644 index 0000000000000000000000000000000000000000..a2c2280d5d8d147dd05ccc19b292faf66fa6f369 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/check_strict_fusion.h @@ -0,0 +1,10 @@ + +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void CheckStrictFusion(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_profiling.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_profiling.h new file mode 100644 index 0000000000000000000000000000000000000000..30ae1427f793ab90e457cb121f4c9b575cf7ec3d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_profiling.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +TORCH_API void unprofileGraphInputs(const std::shared_ptr& graph); +TORCH_API void unprofileBlock(Block* start_block); +// Unprofiles all the node outputs in a block. + +TORCH_API void ClearProfilingInformation(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_undefinedness.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_undefinedness.h new file mode 100644 index 0000000000000000000000000000000000000000..8ac62643131a2ec0ab26166b12016bb5c4e07ab0 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/clear_undefinedness.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +// Undefinedness makes argument matching fail for regular tensor operations +// if 1+ arguments are undefined or possibly undefined tensors. +// Technically, undefined tensors are **not** tensors as the regular tensor +// operations do not know how to handle them. +// However, in practice, there are guards and conversion operators that +// **always** gate regular operations if undefined tensors may be present +// Eventually, we would love to move to the world where we use optionals +// in lieu of undefined tensors. +// When this happens, this pass will be removed +TORCH_API void ClearUndefinedness(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/common_subexpression_elimination.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/common_subexpression_elimination.h new file mode 100644 index 0000000000000000000000000000000000000000..680b72228fc1220ef64bac5d48cf07ae699c6324 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/common_subexpression_elimination.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API bool EliminateCommonSubexpression( + const std::shared_ptr& graph); +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/concat_opt.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/concat_opt.h new file mode 100644 index 0000000000000000000000000000000000000000..040ade790be6b0ed04c6e13d641d22cee7be1730 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/concat_opt.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace torch::jit { + +// Eliminates common inputs among `aten::cat` ops. +TORCH_API bool EliminateConcatCommonInputs(const std::shared_ptr& graph); + +// Expands `aten::cat` ops into `aten::copy` ops and eliminates redudancies +// in the buffers used for concatenation if possible. +TORCH_API void ExpandConcatAndEliminateRedundancy( + const std::shared_ptr& graph); + +TORCH_API bool CombineConcats(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_pooling.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_pooling.h new file mode 100644 index 0000000000000000000000000000000000000000..20478e3ee27329399847f51ad3e54cf230fbcdea --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_pooling.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void ConstantPooling(const std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_propagation.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_propagation.h new file mode 100644 index 0000000000000000000000000000000000000000..858da81458ba9edf61437ed3c89915307c51d9bc --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/constant_propagation.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace torch::jit { + +// Runs constant propagation on all objects unless ignore_custom_classes is +// specified as true, in which case user defined classes are skipped. This is +// useful to prevent early fusion of packing operations, which end up lowering +// away information about their constructors (e.g. packed::linear_clamp_prepack +// and prepacked::conv2d_clamp_prepack) +// Returns True if the pass made a change to the graph +TORCH_API bool ConstantPropagation( + std::shared_ptr& graph, + bool ignore_custom_classes = false); + +// runs constant propagation only on ops that have non-aliasing inputs & outputs +// Returns True if the pass made a change to the graph +TORCH_API bool ConstantPropagationImmutableTypes(std::shared_ptr& graph); + +// Runs the node if its inputs are constants. Callers of this function must +// make their own determination if constant prop is appropriate - for example +// non-deterministic ops or ops with side effects. If ignore_custom_classes is +// specified, nodes that output user defined classes are not run. +TORCH_API std::optional runNodeIfInputsAreConstant( + const Node* node, + bool ignore_custom_classes = false, + AliasDb* db = nullptr); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_autodiff_subgraphs.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_autodiff_subgraphs.h new file mode 100644 index 0000000000000000000000000000000000000000..6cfc8fda6be7fb1d0ed256dd5d6ea242fe9a7369 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_autodiff_subgraphs.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +namespace torch::jit { + +// insert GraphExecutor nodes that group together +// subgraphs that are differentiable by the jit's autodiff passes +// threshold - minimum number of nodes that will appear in a block +// returns all differentiable blocks that have been found +TORCH_API std::vector CreateAutodiffSubgraphs( + const std::shared_ptr& graph, + size_t threshold = 2); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_functional_graphs.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_functional_graphs.h new file mode 100644 index 0000000000000000000000000000000000000000..49a9ae52378cb2574dd55c9bf9040760b28c5ca5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/create_functional_graphs.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API void CreateFunctionalGraphs(const std::shared_ptr& graph); + +TORCH_API void InlineFunctionalGraphs(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dead_code_elimination.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dead_code_elimination.h new file mode 100644 index 0000000000000000000000000000000000000000..21a29c8fa41d7114b506bf40949cd7573a5b78ed --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dead_code_elimination.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace torch::jit { + +// If given a top-level graph, DCE will construct do alias analysis that allows +// for "smarter" dead code elimination (we will eliminate mutable ops if we can +// prove the mutated values are not used). Otherwise, we will not allow DCE to +// eliminate mutable ops. +// +// So, prefer to use the graph version if you can. +enum class DCESideEffectPolicy : uint8_t { + // default behavior: dead code elimination will check if a node has side + // effects + // and not delete it if it does. + DONT_DELETE_NODES_WITH_SIDE_EFFECTS, + // with this flag, dead code elimination will not check if a node has side + // effects and treat nodes with side effects like any other node, + // i.e. delete them if their outputs aren't used anywhere. + ALLOW_DELETING_NODES_WITH_SIDE_EFFECTS +}; + +TORCH_API void EliminateDeadCode( + const std::shared_ptr& graph, + DCESideEffectPolicy sideEffectPolicy = + DCESideEffectPolicy::DONT_DELETE_NODES_WITH_SIDE_EFFECTS); +TORCH_API void EliminateDeadCode( + Block* block, + bool recurse = true, + DCESideEffectPolicy sideEffectPolicy = + DCESideEffectPolicy::DONT_DELETE_NODES_WITH_SIDE_EFFECTS); + +// Invoke the user-provided callback on all live values before deleting anything +TORCH_API void EliminateDeadCode( + Block* block, + std::function&)> cb, + DCESideEffectPolicy sideEffectPolicy = + DCESideEffectPolicy::DONT_DELETE_NODES_WITH_SIDE_EFFECTS); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/decompose_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/decompose_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..a1a20d5e1e714e707242012adc8e5c04470c2aed --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/decompose_ops.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void DecomposeOps(std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/device_type_analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/device_type_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..e2a6c966006ec2fe0f3b80d38fe7f9dd4f95c038 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/device_type_analysis.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace torch::jit { +struct Graph; + +// Propagates Device type info throughout the given graph. +TORCH_API bool DeviceTypePropagation(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dtype_analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dtype_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..9fa4fbd34d43ad96eb8baf9df82eb46ca62eaefe --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/dtype_analysis.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { +struct Graph; + +// Propagate tensor properties (e.g., dtype, device, is_contiguous, layout) +// propagation on all tensor objects. Currently, we only support dtype +// propagation +TORCH_API bool DtypePropagation(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/eliminate_no_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/eliminate_no_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..c2e5bc8b4f46024b62df16b1e99faff765094efd --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/eliminate_no_ops.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace torch::jit { + +// Remove ops that do nothing on the forward pass (like aten::detach). +// This pass is invoked as a part of freeze_module. +// This function also takes a set of custom ops to eliminate. All ops in this +// set must take their output as their first input, i.e. x = f(x, ...) +TORCH_API bool EliminateNoOps( + std::shared_ptr& graph, + std::unordered_set custom_ops = {}); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/erase_number_types.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/erase_number_types.h new file mode 100644 index 0000000000000000000000000000000000000000..847b5e60c95efb904fa8b6bf3bf54ea394f02927 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/erase_number_types.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace torch::jit { + +// Erase NumberType information. This is necessary for and only used in +// exporting to ONNX. This pass ensures that no remaining Values have +// NumberType types, replacing them with tensors. +// The following things are done to erase NumberType info: +// - NumberType outputs are changed to DynamicType. +// - prim::Constant nodes which are numbers get changed into 0-dim tensors of +// the corresponding type +// - prim::TensorToNum, aten::Float, aten::Int and prim::NumToTensor nodes +// are erased. +// +// The pass assumes that DCE will be called sometime after. +TORCH_API void EraseNumberTypes(const std::shared_ptr& graph); +TORCH_API void EraseNumberTypesOnBlock(Block* block); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fixup_trace_scope_blocks.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fixup_trace_scope_blocks.h new file mode 100644 index 0000000000000000000000000000000000000000..8061e9e78005e77e73377cf2481c531ee1082748 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fixup_trace_scope_blocks.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +// Directly after tracing, we have an ill-formed graph with blocks inserted. +// Example: +// +// graph(%self : ClassType, +// %input.1 : Float(3, 4)): +// %1 : ClassType = prim::GetAttr[name="relu1"](%self) +// %2 : ClassType = prim::GetAttr[name="relu2"](%self) +// %3 : ClassType = prim::GetAttr[name="rrr"](%2) +// = prim::TracedModuleForward[scope="__module.relu1"]() +// block0(): +// %input : Float(3, 4) = aten::relu(%input.1), +// -> () +// = prim::TracedModuleForward[scope="__module.relu2"](), +// block0(): +// = prim::TracedModuleForward[scope="__module.relu2.rrr"](), +// block0(): +// %6 : Float(3, 4) = aten::relu(%input), +// -> () +// -> () +// return (%6) +// +// In this pass, we: +// 1) Lift Value defs to as high of a scope as needed to ensure that +// they dominate all their uses. For example, `input` in the above +// graph needs to be lifted to the top-level block so that its use +// in the second `relu` operator is dominated. +// 2) Lambda lift the blocks. This ensures that all values used within +// each scope have their defs captured. +// 3) Convert the scope blocks into methods on their respective Modules, +// and convert TracedModuleForward nodes to CallMethod nodes into those +// methods. +// +// Then, we'll have a well-formed graph with proper method calls. +TORCH_API void FixupTraceScopeBlocks( + std::shared_ptr& graph, + Module* self); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_conv_bn.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_conv_bn.h new file mode 100644 index 0000000000000000000000000000000000000000..097e8aa38c5b34cea9d1639271f126da0c166e3d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_conv_bn.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace torch::jit { + +/** \brief Fold Conv2d-BatchNorm2d into Conv2d in all methods of this + * module and all its submodules, forward is included by default. + * + * The weight and bias of the Conv2d are correspondingly updated. Should only be + * used on modules in eval mode. + */ +TORCH_API Module FoldConvBatchNorm(const Module& module); + +struct TORCH_API ConvBNParameters { + at::Tensor conv_w; + at::Tensor conv_b; + at::Tensor bn_rm; + at::Tensor bn_rv; + double bn_eps = 0.0; + at::Tensor bn_w; + at::Tensor bn_b; +}; + +/** + * Given the current weight and bias tensors of a Conv module and parameters + * of the BatchNorm module we're folding with, compute the updated values + * for the weight and bias. + * + * The function is basically copied from torch/nn/utils/fusion.py + */ +TORCH_API std::tuple computeUpdatedConvWeightAndBias( + const ConvBNParameters& p); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_linear_bn.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_linear_bn.h new file mode 100644 index 0000000000000000000000000000000000000000..995cec434218cbb9b66289cd222e5917b652dacb --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fold_linear_bn.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace torch::jit { + +struct TORCH_API LinearBNParameters { + at::Tensor linear_w; + at::Tensor linear_b; + at::Tensor bn_rm; + at::Tensor bn_rv; + double bn_eps = 0.0; + at::Tensor bn_w; + at::Tensor bn_b; +}; + +/** + * Given the current weight and bias tensors of a Linear module and parameters + * of the BatchNorm module we're folding with, compute the updated values + * for the weight and bias. + * + * The function is basically copied from torch/nn/utils/fusion.py + */ +TORCH_API std::tuple computeUpdatedLinearWeightAndBias( + const LinearBNParameters& p); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/freeze_module.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/freeze_module.h new file mode 100644 index 0000000000000000000000000000000000000000..a0a6ff8b03ead742bfca8a5b7525bc715bb10079 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/freeze_module.h @@ -0,0 +1,34 @@ +/** \brief This file defines freezing Torchscript module API. + * + * This API has python-binding and can be invoked directly or as a part of + * general optimization pipeline. + */ +#pragma once + +#include +#include + +/** \brief Freeze Module, i.e., Assume all attributes are constants. + * + * Freezing module is a functionality that allows the JIT to internalize + * immutable attributes. Combined with inlining, the module is aggressively + * optimized and significant overhead is optimized away. The freezeModule API + * produces a cloned frozen module. + */ + +namespace torch::jit { + +TORCH_API Module freeze_module( + const Module& module, + std::vector preservedAttrs = std::vector(), + bool freezeInterfaces = true, + bool preserveParameters = false); + +// Clone-free version of freeze_module. This modifies the module inplace. +// Use this version to avoid extra memory usage incurred by cloning the module. +TORCH_API void freeze_module_inplace( + Module* module, + std::vector preservedAttrs = std::vector(), + bool freezeInterfaces = true, + bool preserveParameters = false); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_concat_linear.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_concat_linear.h new file mode 100644 index 0000000000000000000000000000000000000000..0954c03017cc0e6afae6fcb5a9d238e731aa6a84 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_concat_linear.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace torch::jit { + +// Concats multiple linear ops with the same Tensor input +// into a single linear op. +TORCH_API bool FrozenConcatLinear(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_add_relu_fusion.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_add_relu_fusion.h new file mode 100644 index 0000000000000000000000000000000000000000..309628b7a09b51d06fd8582550d1e094b1609a70 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_add_relu_fusion.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API extern std::function&)>& +getFuseFrozenConvAddReluImpl(); + +TORCH_API void FuseFrozenConvAddRelu(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_folding.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_folding.h new file mode 100644 index 0000000000000000000000000000000000000000..636e5e5a868083e44f7d9b04d008ba76dca9ea6d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_conv_folding.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace torch::jit { + +// Fuses Convolution -> Batchnorm into a single Convolution by +// folding batchnorm weights into conv weights. +// This pass only works on Frozen Graphs; otherwise it is a No-Op. +TORCH_API bool FoldFrozenConvBatchnorm(std::shared_ptr& graph); + +// Fuses Convolution -> Add/Sub into a single Convolution by +// folding add constant tensor into conv weights. +// This pass only works on Frozen Graphs; otherwise it is a No-Op. +TORCH_API bool FoldFrozenConvAddOrSub(std::shared_ptr& graph); + +// Fuses Convolution -> Mul/Div into a single Convolution by +// folding add constant tensor into conv weights. +// This pass only works on Frozen Graphs; otherwise it is a No-Op. +TORCH_API bool FoldFrozenConvMulOrDiv(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_graph_optimizations.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_graph_optimizations.h new file mode 100644 index 0000000000000000000000000000000000000000..4852b27913b20bf13514677c3cf2bb064136ed7f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_graph_optimizations.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +/** \brief Runs a set of Optimizations that Optimize Frozen Graphs + * + * Currently this set of optimizations is: + * - FoldFrozenConvBatchnorm + * - FoldFrozenConvAddOrSub + * - FoldFrozenConvMulOrDiv + * - FoldFrozenLinearBatchnorm + */ + +namespace torch::jit { + +TORCH_API void OptimizeFrozenGraph( + std::shared_ptr& graph, + bool optimize_numerics = true); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_folding.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_folding.h new file mode 100644 index 0000000000000000000000000000000000000000..677096daf7a0fbf247f2fce3f0a8769694af470c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_folding.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace torch::jit { + +// Fuses Linear -> BatchNormNd into a single Linear by +// folding batchnorm weights into linear weights. +// This pass only works on Frozen Graphs; otherwise it is a No-Op. +TORCH_API bool FoldFrozenLinearBatchnorm(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_transpose.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_transpose.h new file mode 100644 index 0000000000000000000000000000000000000000..179ffd807b5679a5fef32566d90856e0c8129d1f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_linear_transpose.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace torch::jit { + +// Transposes the weight matrix for frozen linear modules. +// and converts it into a matmul +TORCH_API bool FrozenLinearTranspose(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_ops_to_mkldnn.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_ops_to_mkldnn.h new file mode 100644 index 0000000000000000000000000000000000000000..adbd8f357f1e55fc5dafd890e7357ded6c1c2071 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/frozen_ops_to_mkldnn.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace torch::jit { + +// Converts operators & their parameters to mkldnn if it is profitable +// Currently encompassing Conv2d and Conv3d, and Linear +// Op must be in float32 and mkldnn must be built +// This pass only works on frozen graph +TORCH_API void ConvertFrozenOpsToMKLDNN(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_linear.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_linear.h new file mode 100644 index 0000000000000000000000000000000000000000..9ad0c3d101c4b71b32f016c89b6e5d95924ef0a4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_linear.h @@ -0,0 +1,22 @@ +/** \brief Fusing linear patterns as single at::linear for easier pattern + * matching in later passes + */ +#pragma once + +#include + +namespace torch::jit { + +/** \brief Match the at::linear pattern and fuse it into a single at::linear + * This pass fuse the addmm or matmul + add generated by JIT back to linear + * This pass can be deleted once the JIT can emit the aten::linear in the future + */ +TORCH_API void FuseLinear(std::shared_ptr& graph); + +/** Swap functional linear CallFunctions to aten::linear + */ +TORCH_API void SwapFunctionalLinear(std::shared_ptr& graph); +/** Swap all functional linear CallFunctions in module + */ +TORCH_API void SwapFunctionalLinear(Module& module); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_relu.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_relu.h new file mode 100644 index 0000000000000000000000000000000000000000..0254b46236b16c0fa074b32c254d2c488b4a223e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/fuse_relu.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace torch::jit { +TORCH_API void FuseAddRelu(script::Module& module); +TORCH_API void FuseAddRelu(std::shared_ptr& graph); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_fuser.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_fuser.h new file mode 100644 index 0000000000000000000000000000000000000000..0a922b046836a0abe4bb768c6b825df6ffb1b031 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_fuser.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API bool canFuseOnCPULegacy(); +TORCH_API void overrideCanFuseOnCPULegacy(bool value); + +// NB: Be sure to run DCE before fusion, because dead instructions +// can prevent fusion opportunities from being exploited. +// On Windows will noop, NYI +TORCH_API void FuseGraph( + std::shared_ptr& graph, + bool strict_fuser_check = false); + +// \brief Custom fusion pass using a node-level callback to +// determine the inclusion of nodes in a subgraph. +// +// This helper omits aliased inputs and fusion across control flow +// boundaries. +// +// \arg graph The graph to be modified in-place +// \arg is_fusable A callback run on each fusable node in the graph. +// \arg kind The label given to the resultant fused subgraph +// \arg arg_limit The maximum number of args the resultant fused subgraph +// should have. Note: This will likely develop into a general +// post condition on the fused subgraph. +TORCH_API void CustomFuseGraph( + std::shared_ptr& graph, + const std::function& is_fusable, + Symbol kind, + size_t arg_limit = std::numeric_limits::max()); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_rewrite_helper.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_rewrite_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..3ceadb9026c97a3966a15abf8f7b821a22f5052c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/graph_rewrite_helper.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +namespace torch::jit::graph_rewrite_helper { + +std::string getFuncName(Value* func_value); +Value* getValue( + const std::string& name, + const std::unordered_map& match_vmap, + const std::unordered_map& vmap); +std::optional getIValue( + const std::string& name, + const std::unordered_map& match_vmap, + const std::unordered_map& vmap); +TORCH_API void replaceConvolutionWithAtenConv(std::shared_ptr& graph); + +bool isClampFusable( + const Match& match, + const std::unordered_map& vmap); + +// This struct contains a compiled IR patterns slated for use in the +// findPatternMatches function. The struct encapsulates the common +// information from parseIR that is used in conjunction with the +// pattern matching facility. A const instance of this struct can +// also be stored away to cache the compiled IR pattern and reduce +// runtime cost +struct PatternInfo { + std::string pattern_string; + std::unique_ptr pattern_graph; + std::unordered_map vmap; + std::vector filters; + + static PatternInfo parse_from_str( + std::string pattern_string, + const std::vector& filters = {}) { + PatternInfo rv{ + std::move(pattern_string), + std::make_unique(), + decltype(vmap){}, + filters}; + parseIR(rv.pattern_string, rv.pattern_graph.get(), rv.vmap); + return rv; + } +}; + +} // namespace torch::jit::graph_rewrite_helper diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/guard_elimination.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/guard_elimination.h new file mode 100644 index 0000000000000000000000000000000000000000..99234224cea2fed88191d8ded3731b585c79a204 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/guard_elimination.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace torch::jit { + +TORCH_API void EliminateRedundantGuards(std::shared_ptr graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/hoist_conv_packed_params.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/hoist_conv_packed_params.h new file mode 100644 index 0000000000000000000000000000000000000000..6a6a7ada7f920a23a72fe99ed716c8ac2045c293 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/hoist_conv_packed_params.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +void HoistConvPackedParams(script::Module& m); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_autodiff_subgraphs.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_autodiff_subgraphs.h new file mode 100644 index 0000000000000000000000000000000000000000..9f6a8dc1e25e213f42641726e3969630fb72970f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_autodiff_subgraphs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API bool canRunWithAutograd(Node* node); + +TORCH_API void InlineAutodiffSubgraphs( + std::shared_ptr& graph, + size_t threshold = 5); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_fork_wait.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_fork_wait.h new file mode 100644 index 0000000000000000000000000000000000000000..51f9db549a3d06e493d33164ccf28979c31cf421 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_fork_wait.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace torch::jit { + +// Inline Fork and Wait calls. This is used, for example, in ONNX export, where +// we do not support the explicit parallelism structures and would rather +// just have a flat graph. This inlines the forked section in the fork() +// callsite and replaces uses of the result of wait() calls with the values +// produced from the (now-inlined) forked section. +TORCH_API void InlineForkWait(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_forked_closures.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_forked_closures.h new file mode 100644 index 0000000000000000000000000000000000000000..11c40b07e87f2a9a79393b00a609d68c82c7014d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inline_forked_closures.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API void inlineForkedClosures(std::shared_ptr& to_clean); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inliner.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inliner.h new file mode 100644 index 0000000000000000000000000000000000000000..1c9adeccf322aafbd08896402d594b1f32164f08 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inliner.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace torch::jit { + +// Inline function and method calls. +TORCH_API void Inline(Graph& graph); + +TORCH_API GraphFunction* tryToGraphFunction(Node* n); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inplace_check.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inplace_check.h new file mode 100644 index 0000000000000000000000000000000000000000..0ad78137f473ddd3c293e93b4163b07a8937cc65 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/inplace_check.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void CheckInplace(std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/insert_guards.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/insert_guards.h new file mode 100644 index 0000000000000000000000000000000000000000..a32fb1952e722b390ad3ae752d1e694f2d6129dd --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/insert_guards.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace torch::jit { + +TORCH_API void InsertGuards(std::shared_ptr graph); + +TORCH_API void RemoveProfilingNodes(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/integer_value_refinement.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/integer_value_refinement.h new file mode 100644 index 0000000000000000000000000000000000000000..df3d8526860fad49c3fbfe7733fd73b731b4f585 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/integer_value_refinement.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace torch::jit { + +// return true if graph is modified +TORCH_API bool RefineIntegerValues(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lift_closures.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lift_closures.h new file mode 100644 index 0000000000000000000000000000000000000000..1778c599be47ae7ed61901397bfcf7de0efc46e3 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lift_closures.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API void liftClosures(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/liveness.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/liveness.h new file mode 100644 index 0000000000000000000000000000000000000000..97b59f32222eff2ebef638e422a84a186e320e06 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/liveness.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +using SparseBitVector = ::c10::SparseBitVector<256>; + +// BuildLivenessSets computes "bailout" liveness which is equivalent to +// "{LIVE_IN} or {GEN}" or "{LIVE_OUT} - {KILL}" +TORCH_API std::unordered_map> BuildLivenessSets( + std::shared_ptr graph); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/loop_unrolling.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/loop_unrolling.h new file mode 100644 index 0000000000000000000000000000000000000000..dbf7ef80bd77c256a463fccc6dd2e4737e63b739 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/loop_unrolling.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace torch::jit { + +// return true if graph is modified +TORCH_API bool UnrollLoops(std::shared_ptr& graph); + +// Only unrolls constant loops. Will unroll them regardless of loop block size +TORCH_API bool UnrollConstantLoops(std::shared_ptr& graph); + +TORCH_API Node* PeelLoop(Node* n, size_t times); + +// return true if graph is modified +TORCH_API bool PeelProfilingLoops(const std::shared_ptr& graph); + +struct TORCH_API LoopsPeeler { + LoopsPeeler(std::function callback, size_t num_iterations = 1) + : callback_(std::move(callback)), num_iterations_(num_iterations) {} + + bool run(const std::shared_ptr& graph); + + private: + void collectLoop(Node* n); + void collectLoops(Block* block); + void peelLoops(); + + std::function callback_ = nullptr; + Node* in_loop_ = nullptr; + std::list loops_to_peel_; + size_t num_iterations_ = 1; +}; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_grad_of.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_grad_of.h new file mode 100644 index 0000000000000000000000000000000000000000..528220875c08f66a403c623577c9423a76dcd1f8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_grad_of.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace torch::jit { + +// This pass removes 'grad_of' nodes, replacing them with conditionals of +// the form: +// if any_defined(inputs): +// outputs = +// else: +// outputs = undefineds +TORCH_API void LowerGradOf(Graph& g); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_graph.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_graph.h new file mode 100644 index 0000000000000000000000000000000000000000..48308d122f6e0a26d395d1c66337ef616ffa896e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_graph.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace torch::jit { + +using ModulePtr = c10::intrusive_ptr; + +// Given a graph with of a method which first argument is %self, lower it to a +// graph where all attributes accesses are replaced with explicit inputs of the +// graph (rather than results of prim::GetAttr executed on %self). +// +// Returns a tuple (graph, parameters) where the last module.parameters.size() +// inputs to the graph are the trainable parameters used in this method. The +// remaining inputs are the true inputs to the function. +TORCH_API std::pair, std::vector> LowerGraph( + Graph& graph, + const ModulePtr& self); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_tuples.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_tuples.h new file mode 100644 index 0000000000000000000000000000000000000000..76867894930665c17a419d723407cab2f3c1da87 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/lower_tuples.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace torch::jit { + +// removes tuples where TupleConstruct and TupleUnpack are matched +// but leaves tuples in place across if statements, loops, and as inputs/outputs +TORCH_API void LowerSimpleTuples(const std::shared_ptr& graph); + +// removes _all_ tuples and raises an error if some cannot be removed +// this is used by ONNX to ensure there are not tuples before conversion, +// but will not work on graphs whose inputs contain tuples. +TORCH_API void LowerAllTuples(const std::shared_ptr& graph); + +TORCH_API void LowerSimpleTuples(Block* block); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/metal_rewrite.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/metal_rewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..d819c0a07421bbaa14c78011b57362879be31e1b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/metal_rewrite.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include +#include +#include + +namespace torch::jit { +TORCH_API void metalInsertPrePackedOps(std::shared_ptr& graph); +TORCH_API void metalInsertPrePackedOps(script::Module& module); +TORCH_API void metalFusePrePackedConvWithClamp(script::Module& module); +TORCH_API void metalFoldPrePackingOps(script::Module& module); +TORCH_API script::Module metalOptimizeForMobile( + const script::Module& module, + const std::vector& preserved_methods); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mkldnn_rewrite.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mkldnn_rewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..b51f29f0de714c56f71bd0cc4ff70b873bd11bc1 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mkldnn_rewrite.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +#if AT_MKLDNN_ENABLED() + +#include + +#endif // AT_MKLDNN_ENABLED() + +namespace torch::jit { + +#if AT_MKLDNN_ENABLED() + +namespace mkldnn { + +const static std::map> + fusion_rewrite_map = { + {"none", {}}, + {"relu", {}}, +}; + +} // namespace mkldnn + +#endif // AT_MKLDNN_ENABLED() + +void FuseConvWithEltwise(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mobile_optimizer_type.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mobile_optimizer_type.h new file mode 100644 index 0000000000000000000000000000000000000000..d11f288dca343308bf2167c89a3d6b2d0792a569 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/mobile_optimizer_type.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +enum class MobileOptimizerType : int8_t { + CONV_BN_FUSION, + INSERT_FOLD_PREPACK_OPS, + REMOVE_DROPOUT, + FUSE_ADD_RELU, + HOIST_CONV_PACKED_PARAMS, + CONV_1D_TO_2D, + VULKAN_AUTOMATIC_GPU_TRANSFER, +}; diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/normalize_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/normalize_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..885076584427546e6cd92525fb9dc3195ac1ed8e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/normalize_ops.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace torch::jit { + +// This pass converts aten ops to a normalized form. It is +// run immediately after IR generation in both the tracer and compiler, +// so downstream consumers of the IR do not need handle ops in their +// pre-normalized form. +// Currently only handles normalization of op aliases. +TORCH_API void NormalizeOps(const std::shared_ptr& graph); + +const std::unordered_map& getOperatorAliasMap(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onednn_graph_fuser.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onednn_graph_fuser.h new file mode 100644 index 0000000000000000000000000000000000000000..3dfe479e5c6a24402063d2058c31984e9686b3cb --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onednn_graph_fuser.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include + +namespace torch::jit { + +namespace fuser::onednn { + +static std::atomic onednn_enabled{true}; + +static std::atomic& getLlgaEnabled() { + return onednn_enabled; +} + +TORCH_API void fuseGraph(std::shared_ptr& g); + +} // namespace fuser::onednn + +struct C10_EXPORT RegisterLlgaFuseGraph + : public PassManager { + static bool setEnabled(bool enabled) { + TORCH_CHECK( + AT_MKLDNN_ENABLED(), + "Running oneDNN Graph fuser is only supported with MKLDNN builds."); + bool oldState = fuser::onednn::getLlgaEnabled(); + fuser::onednn::getLlgaEnabled() = enabled; + if (enabled) { + registerPass(fuser::onednn::fuseGraph); + } else { + clearPass(); + } + return oldState; + } + + static bool isEnabled() { + return fuser::onednn::getLlgaEnabled(); + } + + // override PassManager::registerPass to register pre-pass + static bool registerPass(GraphPass p) { + if (!isRegistered()) { + passID(registerPrePass(std::move(p)), true); + isRegistered(true); + return false; + } + return true; + } + + // override PassManager::clearPass to clear pre-pass + static void clearPass() { + if (isRegistered()) { + clearPrePass(passID()); + isRegistered(true); + } + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onnx.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onnx.h new file mode 100644 index 0000000000000000000000000000000000000000..3d4356fc0e1408c8e1080a34dc4d55b103a2dd67 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/onnx.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +TORCH_API std::shared_ptr ToONNX( + std::shared_ptr& state, + ::torch::onnx::OperatorExportTypes operator_export_type); +TORCH_API py::dict BlockToONNX( + Block* old_block, + Block* new_block, + ::torch::onnx::OperatorExportTypes operator_export_type, + py::dict& env, + py::set& values_in_env, + bool is_sub_block = false); +TORCH_API void NodeToONNX( + Node* old_node, + Block* new_block, + ::torch::onnx::OperatorExportTypes operator_export_type, + py::dict& env, + py::set& values_in_env); +TORCH_API void RemovePrintOps(std::shared_ptr& graph); +TORCH_API void PreprocessCaffe2Ops(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/pass_manager.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/pass_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..efb19de59b5306ad09d096c8ee332204865c59e9 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/pass_manager.h @@ -0,0 +1,134 @@ +#pragma once + +#include + +/* `getCustomPrePasses()` returns a vector of passes that will be executed + * after differentiation but before any fusion. This is the de-facto location + * for compiler backends to insert passes. + * + * `getCustomPostPasses()` returns a vector of passes that will be + * executed after differentiation and after fusion (if any). This is the + * location for fusion cleanup passes if they are needed. + * + * Static registration of a pass can be done by creating a global + * `Register{Pre,Post}Pass r(Pass)` variable in a compilation unit. + * + * pass_manager.h uses a Meyer's singleton to store a vector of `Pass`es, which + * modify the IR graph in place. + */ + +namespace torch::jit { + +// A pass modifies a Graph in place. +using GraphPass = std::function&)>; + +// Since Passes are std::functions, we associate a UUID to each pass, this way +// if we want to deregister a pass, we have something to reference it by. +using GraphPassNameType = unsigned int; + +// Graph pass entries have a name associated with them +using GraphPassEntry = std::pair; + +// Return currently registered passes. Passes are stored in a static vector +TORCH_API std::vector>& +getCustomPostPasses(); +TORCH_API std::vector>& +getCustomPrePasses(); + +TORCH_API GraphPassNameType registerPostPass(GraphPass p); +TORCH_API GraphPassNameType registerPrePass(GraphPass p); + +// Look up pass by name passed in, remove it from registered passes +TORCH_API void clearPostPass(GraphPassNameType p); +TORCH_API void clearPrePass(GraphPassNameType p); + +// Remove all passes +TORCH_API void clearAllPostPasses(); +TORCH_API void clearAllPrePasses(); + +// LEGACY CALL +struct TORCH_API RegisterPostPass { + RegisterPostPass(GraphPass p); +}; + +using RegisterPass = RegisterPostPass; + +/* + * PassManager is a wrapper on the register/clear PostPass functions above. It + * will register the pass provided in "registerPass" and will hold on to its + * associated name that way clearPass can be later called and will delete the + * pass used to register when called. + * + * PassManager is templated because we want static variables based on a + * particular GraphPass. When deriving from PassManager, you should send as the + * template parameter your derived class as you would for the curiously + * recurring template pattern. This template parameter isn't actually used and + * is simply done to prevent static members from being shared across derived + * types. + */ +template +struct C10_EXPORT PassManager { + private: + // We want this class to be abstract because it's + virtual void abstract() = 0; + + protected: + /* + * isRegistered() will return if a pass has been registered + * isRegistered(true) will change the value of the internal static bool + * + * There's an internal static bool to this function to keep track of the + * state, this is so when functions are derived from this class, they don't + * have to worry about initializing the static members. + */ + static bool isRegistered(bool flip_bit = false) { + static bool val = false; + if (flip_bit) + val = !val; + return val; + } + + /* + * name() will return the name of the registered pass + * name(pass_name, true) will set the name of the pass + * Similarly to isRegistered we use an internal static variable to hold the + * name. + */ + static GraphPassNameType passID( + GraphPassNameType PassID = 0, + bool set = false) { + static GraphPassNameType pass_id = 0; + if (set) + pass_id = PassID; + return pass_id; + } + + public: + // registerPass(pass) will register the pass provided and set the + // name/isRegistered functions appropriately, it returns a bool value + // indicating whether the given pass is already registered previously. + static bool registerPass(GraphPass p) { + if (!isRegistered()) { + // If we don't already have a registered pass, register pass + // hold on to its name, change isRegistered to true + passID(registerPostPass(std::move(p)), true); + isRegistered(true); + return false; + } + return true; + } + + // Calls ClearPostPass(passID()) + static void clearPass() { + // If the pass is registered, clear it and change isRegistered to false. + if (isRegistered()) { + clearPostPass(passID()); + isRegistered(true); + } + } + + // clang-tidy requires virtual destructor; + virtual ~PassManager() = default; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole.h new file mode 100644 index 0000000000000000000000000000000000000000..c8c1fedc2fa6e3693eb699756189d60096a78e14 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace torch::jit { + +// return true if graph is modified +TORCH_API bool PeepholeOptimize( + const std::shared_ptr& graph, + bool disable_shape_peepholes = false); +// return true if graph is modified +TORCH_API bool PeepholeOptimize( + Block* block, + bool disable_shape_peepholes = false); +// return true if graph is modified +TORCH_API bool FuseAddMM(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_alias_sensitive.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_alias_sensitive.h new file mode 100644 index 0000000000000000000000000000000000000000..d98b8ac58a1264218b08e93a454e9748279c086b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_alias_sensitive.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace torch::jit { + +// Peephole Optimizes alias sensitive peepholes +// Currently this is invoked as part of PeepholeOptimize +// return true if graph is modified +// Optimizes on TensorType if shape_peepholes is true +TORCH_API bool PeepholeOptimizeAliasSensitive( + const std::shared_ptr& graph, + bool shape_peepholes); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_dict_idioms.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_dict_idioms.h new file mode 100644 index 0000000000000000000000000000000000000000..39a36808e61b66e080cdf96a4191f4cc02ba73f9 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_dict_idioms.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace torch::jit { + +// Peephole Optimizes Dict Ops such as len() and __getitem__ +// 1. getitem optimizations +// Given a function like this: +// def foo(): +// d = {0 : 1} +// x = d[0] +// return x +// This pass produces (after dead code elimination): +// def foo(a, b): +// return 1 +// +// This optimization can only happen if the dict is not modified +// and the dict has constant, non overlapping keys. +// +// 2. len optimizations +// Given a function like this: +// def foo(): +// d = {0 : 1} +// return len(d) +// This pass produces (after dead code elimination): +// def foo(): +// return 1 +// +// This has the same requirements as the getitem optimizations. +// +// Currently this is invoked as part of PeepholeOptimize +// return true if graph is modified. +TORCH_API bool PeepholeOptimizeDictIdioms(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_list_idioms.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_list_idioms.h new file mode 100644 index 0000000000000000000000000000000000000000..283b50dd61d4d20c5db56d5da15ea0034cb0d07d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_list_idioms.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace torch::jit { + +// Peephole Optimizes List ops such as len(li) and li[1]. +// 1. Construct/Unpack optimizations +// Given a function like this: +// def foo(a, b): +// li = [a, b] +// x, y = li +// return x, y +// This pass produces (after dead code elimination): +// def foo(a, b): +// return a, b +// +// This is only applied to lists that are not modified. +// +// 2. getitem optimizations +// Given a function like this: +// def foo(a, b): +// li = [a, b] +// x = li[0] +// return x +// This pass produces (after dead code elimination): +// def foo(a, b): +// return a +// +// This optimization can only happen if the list is not modified. +// +// 3. len optimizations +// Given a function like this: +// def foo(): +// li = [1, 2] +// return len(li) +// This pass produces (after dead code elimination): +// def foo(): +// return 2 +// +// This has the same requirements as the getitem optimizations. +// +// 4. ListConstruct + ListConstruct +// Given a function like this: +// def foo(): +// return [1, 2] + [3, 4] +// This pass produces (after dead code elimination): +// def foo(): +// return [1, 2, 3, 4] +// +// This is only applied to lists that are not modified. +// +// 5. Slice +// Given a function like this: +// def foo(): +// return [1, 2, 3, 4, 5][0:2] +// This pass produces (after deadcode elimination): +// def foo(): +// return [1, 2] +// +// Currently this is invoked as part of PeepholeOptimize +// return true if graph is modified. +// If `refine_list_len` is true will attempt to refine the len of lists through +// len comparisons and assertions. This does not generally optimize pytorch +// programs so it is not called by default in PeepholeOptimize. +TORCH_API bool PeepholeOptimizeListIdioms( + const std::shared_ptr& graph, + bool refine_list_len = false); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_non_tensor.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_non_tensor.h new file mode 100644 index 0000000000000000000000000000000000000000..d2eb1f50f6c84345a67ed70412cdac7f8558acee --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/peephole_non_tensor.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace torch::jit { + +// return true if graph is modified +// Optimizing General Graph Patterns that +// are not covered in peephole.cpp and peephole_list_idioms +TORCH_API bool PeepholeOptimizeNonTensor(const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/prepack_folding.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/prepack_folding.h new file mode 100644 index 0000000000000000000000000000000000000000..f33888ce12ecdcec771e1cbe278a25f15f8dac6f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/prepack_folding.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +using PrePackingOpsFilterFn = std::function; + +void PrePackingOpsFolder( + script::Module& m, + const PrePackingOpsFilterFn& is_foldable_op, + const std::string& attr_prefix); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/dedup_module_uses.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/dedup_module_uses.h new file mode 100644 index 0000000000000000000000000000000000000000..4094704129a36cc77d73bee8878848493c2d6b34 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/dedup_module_uses.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace torch::jit { + +/** Recursively deduplicate multiple uses of the same module by + * creating an instance clone for each use of the module, which means + * the type will be the same as before and all the attributes will be + * copied, then we'll change the use of the original module to the use + * of cloned module in the Graph. + * + * This is done to ensure that modules can survive destructive passes + * without changing model behavior. For example, here: + * + * x = self.conv1(x) + * x = self.relu(x) + * x = self.conv2(x) + * x = self.relu(x) + * + * self.relu needs to be deduplicated for potential future destructive passes + * to work properly. + */ +TORCH_API void DedupModuleUses(Module& module); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/finalize.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/finalize.h new file mode 100644 index 0000000000000000000000000000000000000000..8325a32110b823a221b057592b047af940d8cbf6 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/finalize.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +/** \brief Backend specific pass to fuse dequantize - op - quantize calls + * as quantized_op calls. + * + * Right now this is a fusion for fbgemm backend and only works for quantized + * conv op, we'll extend to more ops and more backends in the future. + * + * Currently supported fusion: + * q(conv2d(dq(a), dq(w), dq(b))) --> to_nchw(fbgemm_conv2d(prepack(to_nhwc(a)), + * prepack(to_nhwc(w)), + * prepack(to_nhwc(b)))) + * + * q(linear(dq(a), dq(w), dq(b))) --> to_nchw(fbgemm_linear(prepack(to_nhwc(a)), + * prepack(to_nhwc(w)), + * prepack(to_nhwc(b)))) + * + * \param graph the graph we want to apply fusion + */ +TORCH_API void QuantFusion( + std::shared_ptr& graph, + QuantType quant_type = QuantType::STATIC); + +/** \brief Insert prepack and unpack function in graph + * We want add pack/unpack functions for quantized weight because later we want + * to fold the packed weight as an attribute of the module, in order to reduce + * the cost of packing the weight on the fly in quantized models. + * + * Each quantized op has it's corresponding prepack/unpack function, + * right now, we only need to do prepack/unpack for quantized::linear + * and quantized::conv2d. + */ +TORCH_API void InsertPrepackUnpack(std::shared_ptr& graph); + +/** \brief Insert pack and unpack function in all graphs + * of module + * + * Go through graphs of all the methods of all child modules + * and call InsertPrepackUnpack on the graph. + */ +TORCH_API void InsertPrepackUnpack(Module& module); + +TORCH_API script::Module Finalize( + script::Module& module, + QuantType quant_type = QuantType::STATIC, + const std::vector& preserved_attrs = + std::vector()); + +TORCH_API void FoldQuantizedPrepackingOps(Module& module); + +TORCH_API Module FinalizeOnDevicePTQ( + Module& module, + QuantType quant_type, + const std::string& method_name); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/fusion_passes.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/fusion_passes.h new file mode 100644 index 0000000000000000000000000000000000000000..c741d9cdb7e5647e5bc504638d69b2136bf33c11 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/fusion_passes.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace torch::jit { +TORCH_API void FuseQuantizedAddRelu(std::shared_ptr& graph); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/helper.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/helper.h new file mode 100644 index 0000000000000000000000000000000000000000..d6a0a326f25b74dd91b478ac36527c6fc144ddca --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/helper.h @@ -0,0 +1,214 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +#include + +namespace torch::jit { + +using graph_rewrite_helper::getFuncName; + +// Vector of a module and the name of its method +using ModuleMethodVector = std::vector>; +// Map of quantization parameter name and value +// for example _scale, _zero_point, +// _scalar_type and _axis(for per channel quantization) +using QParamVector = std::vector>; + +// =========== helper functions for Value ========= +// Check if a value is weight, since we need to use weight observer +// for weight +TORCH_API bool isWeight(Value* v); + +// Check if a value is bias for conv and linear, which we do not +// quantize +TORCH_API bool isBiasOfConvOrLinear(Value* v); + +TORCH_API bool isEmbeddingBagNonInput(Value* v); + +// Get the use as scalar input of clamp ops for the input value +std::optional getClampScalarInputUse(Value* v); + +// For a given value `v`, get the list of values that we need to check +// if they are observed/quantized or not, if so, we can say the +// `v` is also observed/quantized, since we can derive +// the quantization parameters for `v` given the list of values +TORCH_API std::vector getPassThroughInputs(Value* v); + +// Clones the method by the name of orig_method_name into new_method_name method +TORCH_API void cloneMethod( + Module& module, + const std::string& orig_method_name, + const std::string& new_method_name); + +// Check if a value in the graph is a Scalar value +TORCH_API bool isScalar(Value* v); + +// Check if value is the input of the graph +TORCH_API bool hitGraphInput(Value* value); + +// Converts a mangled name, such as +// __torch__.torch.ao.nn.quantized.modules.conv.___torch_mangle_7.Conv2d +// into an unmangled name, such as +// __torch__.torch.ao.nn.quantized.modules.conv.Conv2d +TORCH_API std::string removeTorchMangle(const std::string& orig_name); + +// Return the module name that corresponds to the value. +TORCH_API std::optional getModuleName(Value* value); + +// =========== helper functions for Node ========= +TORCH_API bool isSingleInputGeneralShapeAtenFunction(Node* n); + +TORCH_API bool isSingleInputGeneralValueAtenFunction(Node* n); + +TORCH_API bool isSingleInputGeneralCallFunction(Node* n); + +TORCH_API bool isSingleInputGeneralAtenFunction(Node* n); + +TORCH_API bool isClamp(Node* n); + +// Check if the node will produce the same result regardless of whether +// the input tensor is quantized or not, example: aten::size +TORCH_API bool isTensorInfoNode(Node* n); + +// Check if this the propagate op that has single input, e.g. aten::cat +TORCH_API bool isPropagateQuantSingleInputOp(Node* n); + +// Check if this is the propagate op that has two inputs, e.g. aten::add +TORCH_API bool isPropagateQuantBinaryOp(Node* n); + +// Check if this is the node that we'll quantize or not quantize depending on +// whether the input of the node is quantized, example: aten::cat +TORCH_API bool isPropagateQuantOp(Node* n); + +// Check if the node is a binary op like aten::add and aten::mul and +// if the input 1 is a scalar, these ops will be quantized to +// quantized::{op}_scalar +TORCH_API bool isBinaryOpWithScalarInput(Node* n); + +TORCH_API std::optional> getFixedQParams( + Node* n); + +// We don't want to analyze the graph for some `builtin` CallFunctions +// like `linear` because we want to preserve the op boundary +TORCH_API bool userDefinedCallFunction(Node* n); + +// Check if the node has scalar input +TORCH_API bool hasScalarInput(Node* n); + +// Check if a node is quantizable +TORCH_API bool nodeQuantizable( + Node* n, + QuantType quant_type = QuantType::STATIC); + +// Nodes which only require quantization of weight value, eg. embedding_bag +bool isWeightOnlyStaticQuantOp(Node* n); + +// Check if a use of the value is quantizable, this depends on +// both the use node and the offset +TORCH_API bool useQuantizable(const Use& use, QuantType quant_type); + +// Given a CallFunction node, extract the graph of the called function +TORCH_API std::shared_ptr getCallFunctionGraph(Node* n); + +// Check if `use` is a CallFunction of name `func_name` and if value +// `v` is the nth argument (if provided) of the function +bool matchCallFuncToUse( + const Use& use, + const std::string& func_name, + std::optional nth_arg); + +// Check if `use` is a AtenFunction of name `func_name` and if value +// `v` is the nth argument (if provided) of the function +bool matchAtenFuncToUse( + const Use& use, + const std::string& func_name, + std::optional nth_arg); + +// =========== helper functions for Block ========= +// checks if a block will always raise an Exception +TORCH_API bool alwaysRaisesException(Block* block); + +// =========== helper functions for Module ========== +// TODO: remove +TORCH_API std::vector getModuleAccessPath( + Value* instance, + Value* self); +// TODO: remove +TORCH_API Module +findChildModule(const Module& module, const std::vector& path); + +// Given an CallMethod node, get the module instance corresponding +// to the instance Value +// TODO: refactor all current uses of this function to the Opt one +TORCH_API Module getInvokedModule(Module& module, Node* n, Value* self); + +// Given an CallMethod node, get the module instance corresponding +// to the instance Value if the instance is a module, otherwise return +// std::nullopt +std::optional getInvokedModuleOpt( + const Module& module, + Node* n, + Value* self); + +// ==================== filter functions for matches ============== +// filter to check Value `vname` is a constant of int value `value` +bool is_int_constant( + const Match& match, + const std::unordered_map& vmap, + const std::string& vname, + int value); + +// filter to check if the %alpha argument of aten::add is constant 1 +bool aten_add_alpha_is_one( + const Match& match, + const std::unordered_map& vmap); + +// filter to check if the functional in CallFunction is relu +bool is_functional_relu( + const Match& match, + const std::unordered_map& vmap); + +// filter to check if the module is torch.nn.ReLU +bool is_relu_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_linear_module( + const Match& match, + const std::unordered_map& vmap); + +// TODO: add a macro to declare the filters +bool is_conv1d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_conv2d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_conv3d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_conv_transpose1d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_conv_transpose2d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_batchnorm2d_module( + const Match& match, + const std::unordered_map& vmap); + +bool is_batchnorm3d_module( + const Match& match, + const std::unordered_map& vmap); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_observers.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_observers.h new file mode 100644 index 0000000000000000000000000000000000000000..7dbac9cfca6704c1bd2543e79cb6d66d1f3d2e70 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_observers.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +namespace std { + +template <> +struct hash { + inline size_t operator()(const torch::jit::Module& arg) const { + return std::hash>()(arg._ivalue()); + } +}; + +} // namespace std + +namespace torch::jit { + +using QConfig = std::tuple; +using QConfigDict = std::unordered_map>; + +/** \brief Insert observer module and observer function call for + * the Tensors that needs to be observed. + * + * For each Tensor that needs to be observed in the method, insert observer + * module to the input module and add forward calls of observer to the specified + * method. + * + * \param module the input module + * \param method_name the method we want to insert observers for + * \param qconfig_dict the qconfig dictionary that specifies how + * each module is going to be quantized + * \param inplace whether we want to do inplace modification to the input module + * or clone the module + * \param is_dynamic whether the dynamic quantization script is being used. + */ +TORCH_API Module InsertObservers( + Module& module, + const std::string& method_name, + const QConfigDict& qconfig_dict, + bool inplace, + QuantType quant_type = QuantType::STATIC); + +/** \brief Insert observer module and observer method for + * the Tensors that needs to be observed. + * + * For each Tensor that needs to be observed in the method, insert observer + * module to the input module and observe_ methods to the module. + * This method is clone of mehtod_name with forward calls of observer added. + * + * \param module the input module + * \param method_name the method we want to insert observers for + * \param qconfig_dict the qconfig dictionary that specifies how + * each module is going to be quantized + * \param inplace whether we want to do inplace modification to the input module + * or clone the module + * \param is_dynamic whether the dynamic quantization script is being used. + */ +TORCH_API Module InsertObserversForOnDevicePTQ( + Module& module, + const std::string& method_name, + const QConfigDict& qconfig_dict, + bool inplace, + QuantType quant_type = QuantType::STATIC); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_quant_dequant.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_quant_dequant.h new file mode 100644 index 0000000000000000000000000000000000000000..9bda42edae41349e0f38fd0de1a16a72940875af --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/insert_quant_dequant.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +/** Replicate quantize node for prim::If blocks, so that we can match + * quantization patterns in prim::If blocks + */ +TORCH_API void ReplicateQuant(std::shared_ptr& graph); + +/** Replicate dequantize node for each use, so that we can match + * quantization patterns + */ +TORCH_API void ReplicateDeQuant(std::shared_ptr& graph); + +/** \brief Insert quantize - dequantize calls to the Tensors + * that are observed in insert_observers pass + * + * For each Tensor that is observed, get the observer module and call + * calculate_qparam on the observer module to get quantization parameters + * and add quantize - int_repr - dequantize function calls using these + * parameters we also have special handling for quantizing "bias" right now. + * + * \param module the input module + * \param method_name the method we want to insert quantization calls for + */ +TORCH_API Module InsertQuantDeQuant( + Module& module, + const std::string& method_name, + bool inplace, + bool debug, + QuantType quant_type = QuantType::STATIC); + +TORCH_API Module InsertQuantDeQuantOnDevicePTQ( + Module& module, + const std::string& method_name, + bool inplace, + bool debug, + QuantType quant_type = QuantType::STATIC); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_patterns.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_patterns.h new file mode 100644 index 0000000000000000000000000000000000000000..549741ac6ed90302c08e30e2cc049cd02da0d9a7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_patterns.h @@ -0,0 +1,1264 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +struct QuantFusionInfo { + std::string quantized_op_name; + std::string pattern; + std::string replacement; + std::vector filters = {}; +}; + +namespace { +std::string getExtraArgList(std::vector extra_args) { + return std::accumulate( + extra_args.begin(), + extra_args.end(), + std::string(), + [](const std::string& acc, const std::string& arg) { + return acc + ", " + arg; + }); +} + +// Get the pattern we want to replace the match with +std::string getAtenOpPattern( + const std::string& graph_header, + const std::string& op_name, + const std::vector& extra_op_args, + bool scalar_args = false) { + std::vector _extra_op_args = extra_op_args; + std::string aten_op_pattern = graph_header; + if (scalar_args) { + for (const auto& extra_arg : _extra_op_args) { + aten_op_pattern + .append(R"( + )") + .append(extra_arg) + .append("_scalar = aten::item(") + .append(extra_arg) + .append(")"); + } + + for (auto& _extra_op_arg : _extra_op_args) { + _extra_op_arg.append("_scalar"); + } + } + const auto& extra_op_arg_list = getExtraArgList(std::move(_extra_op_args)); + aten_op_pattern += R"( + %r = )"; + aten_op_pattern += op_name + "(" + "%a_quant" + extra_op_arg_list + ")"; + aten_op_pattern += R"( + return (%r) )"; + return aten_op_pattern; +} + +// generate ops for quantize pattern for a scalar value +std::string getQuantizeForScalar(const std::string& value) { + // 6 is `torch.float` ScalarType, we are creating a float scalar + // tensor from a scalar value + std::string quantize_pattern = R"( + )" + + value + "_float_scalar_type : int = prim::Constant[value=6]()"; + quantize_pattern += R"( + )" + + value + "_none : None = prim::Constant()"; + quantize_pattern += R"( + )" + + value + "_tensor : Tensor = aten::scalar_tensor(" + value + ", " + value + + "_float_scalar_type"; + for ([[maybe_unused]] const auto i : c10::irange(3)) { + quantize_pattern += ", " + value + "_none"; + } + quantize_pattern += ")"; + quantize_pattern += + R"( + )" + + value + "_quant = aten::quantize_per_tensor(" + value + "_tensor" + + getExtraArgList( + {value + "_scale", value + "_zero_point", value + "_dtype"}) + + ")"; + return quantize_pattern; +} + +std::string getDequantize(const std::string& value) { + return R"( + )" + + value + "_dequant = aten::dequantize(" + value + "_quant)"; +} + +std::string getItem(const std::string& value) { + return R"( + )" + + value + "_scalar : float = aten::item(" + value + "_dequant)"; +} + +// Patterns for the ops that inherit parameters from input +std::string getInputTensorQParamOpPattern( + const std::string& op_name, + const std::vector& extra_op_args) { + const auto& extra_op_arg_list = getExtraArgList(extra_op_args); + std::string op_pattern = "graph(%a_quant" + extra_op_arg_list + "):" + R"( + %a_dequant = aten::dequantize(%a_quant) + %r = )" + + op_name + "(" + "%a_dequant" + extra_op_arg_list + ")" + R"( + %r_scale : float = aten::q_scale(%a_quant) + %r_zero_point : int = aten::q_zero_point(%a_quant) + %r_dtype : int = prim::dtype(%a_quant) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + return op_pattern; +} + +// QuantFusionInfo for the ops that inherit parameters from input +QuantFusionInfo getInputTensorQParamOpFusionInfo( + const std::string& op_name, + const std::vector& extra_op_args) { + std::string op_pattern = + getInputTensorQParamOpPattern(op_name, extra_op_args); + const auto& extra_op_arg_list = getExtraArgList(extra_op_args); + std::string graph_header = "graph(%a_quant" + extra_op_arg_list + "):"; + std::string op_replacement = + getAtenOpPattern(graph_header, op_name, extra_op_args); + + return {op_name, std::move(op_pattern), std::move(op_replacement)}; +} + +// quant fusion for ops like `quantized::add_scalar`, `quantized::mul_scalar` +QuantFusionInfo getBinaryOpScalarFusionInfo( + const std::string& op_name, + const std::vector& extra_op_args, + const std::string& quantized_op_name, + const std::vector& extra_quantized_op_args, + const std::vector& filters = {}) { + std::string op_pattern = + getInputTensorQParamOpPattern(op_name, extra_op_args); + + const auto& extra_op_arg_list = getExtraArgList(extra_op_args); + std::string graph_header = "graph(%a_quant" + extra_op_arg_list + "):"; + std::string op_replacement = getAtenOpPattern( + graph_header, quantized_op_name, extra_quantized_op_args); + + return {op_name, std::move(op_pattern), std::move(op_replacement), filters}; +} + +QuantFusionInfo getClampOpFusionInfo( + const std::string& op_name, + const std::vector& extra_op_args) { + std::vector header_args = extra_op_args; + std::vector input_qparams = {"_scale", "_zero_point", "_dtype"}; + for (const auto& arg : extra_op_args) { + for (const auto& qparam : input_qparams) { + header_args.push_back(arg + qparam); + } + } + for (const auto& qparam : input_qparams) { + header_args.push_back("%r" + qparam); + } + const auto& extra_header_arg_list = getExtraArgList(std::move(header_args)); + std::string graph_header = "graph(%a_quant" + extra_header_arg_list + "):"; + std::string op_pattern = graph_header; + for (const auto& arg : extra_op_args) { + op_pattern += getQuantizeForScalar(arg); + op_pattern += getDequantize(arg); + op_pattern += getItem(arg); + } + op_pattern += getDequantize("%a"); + op_pattern += R"( + %r = )"; + std::vector scalar_extra_args; + scalar_extra_args.reserve(extra_op_args.size()); + for (const auto& arg : extra_op_args) { + scalar_extra_args.push_back(arg + "_scalar"); + } + op_pattern += op_name + "(" + "%a_dequant" + + getExtraArgList(std::move(scalar_extra_args)) + ")"; + // IR pattern common to all ops that inherit qparam from input + op_pattern += R"( + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string aten_op_pattern = + getAtenOpPattern(graph_header, op_name, extra_op_args); + + return {op_name, std::move(op_pattern), std::move(aten_op_pattern)}; +} + +// Patterns for the ops that has fixed quantization parameters +QuantFusionInfo getFixedQParamOpFusionInfo( + const std::string& op_name, + const std::vector& extra_op_args, + bool is_symmetric) { + const auto& extra_op_arg_list = getExtraArgList(extra_op_args); + std::string graph_header = "graph(%a_quant" + extra_op_arg_list + "):"; + std::string op_pattern = graph_header; + op_pattern += R"( + %a_dequant = aten::dequantize(%a_quant) + %r = )"; + op_pattern += op_name + "(" + "%a_dequant" + extra_op_arg_list + ")"; + // IR pattern common to all ops with fixed quantization parameters for + // asymetric quantization + std::string asym_fixed_qparam_op_suffix = R"( + %r_scale : float = prim::Constant[value=0.00390625]() + %r_zero_point : int = prim::Constant[value=0]() + %r_dtype : int = prim::Constant[value=13]() + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string sym_fixed_qparam_op_suffix = R"( + %r_scale : float = prim::Constant[value=0.0078125]() + %r_zero_point : int = prim::Constant[value=128]() + %r_dtype : int = prim::Constant[value=13]() + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + op_pattern += + is_symmetric ? sym_fixed_qparam_op_suffix : asym_fixed_qparam_op_suffix; + + std::string aten_op_pattern = + getAtenOpPattern(graph_header, op_name, extra_op_args); + + return {op_name, std::move(op_pattern), std::move(aten_op_pattern)}; +} + +// filter that checks %b_scalar is a scalar +bool input_b_is_scalar( + const Match& match, + const std::unordered_map& vmap) { + const auto& match_vmap = match.values_map; + auto b_scalar = match_vmap.at(vmap.at("b_scalar")); + return isScalar(b_scalar); +} + +// Patterns for ops that require observation for output quantization parameters +// Example: +// +// before fusion: +// +// graph(%a_quant, %r_scale, %r_zero_point, %r_dtype): +// %a_dequant = aten::dequantize(%a_quant) +// %r = {op_name}(%a_dequant, {extra_args}) +// %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, +// %r_dtype) return (%r_quant) +// +// after fusion: +// +// graph(%a_quant, %r_scale, %r_zero_point, %r_dtype): +// %r_quant = {quantized_op_name}(%a_quant, {extra_args}, %r_scale, +// %r_zero_point) return (%r_quant) +QuantFusionInfo getObservedQParamOpFusionInfo( + const std::string& fp_op_name, + const std::string& q_op_name, + const std::vector& fp_extra_args, + const std::vector& q_extra_args) { + const auto& fp_extra_arg_list = getExtraArgList(fp_extra_args); + const auto& q_extra_arg_list = getExtraArgList(q_extra_args); + + std::string op_pattern = "graph(%a_quant" + fp_extra_arg_list + + ", %r_scale, %r_zero_point, %r_dtype):" + R"( + %a_dequant = aten::dequantize(%a_quant) + %r = )" + + fp_op_name + "(" + "%a_dequant" + fp_extra_arg_list + ")" + R"( + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string aten_op_pattern = "graph(%a_quant" + fp_extra_arg_list + + ", %r_scale, %r_zero_point, %r_dtype):" + R"( + %r_quant = )" + + q_op_name + "(%a_quant" + q_extra_arg_list + + ", %r_scale, %r_zero_point)" + R"( + return (%r_quant) )"; + + return {q_op_name, std::move(op_pattern), std::move(aten_op_pattern)}; +} + +} // namespace + +static std::vector quant_fusion_pattern_and_replacements() { + // aten::conv1d + std::string conv1d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv1d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv1d - aten::relu + std::string conv1d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv1d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv1d - aten::relu_ + std::string conv1d_inplace_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv1d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu_(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::conv1d + std::string quantized_conv1d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv1d(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // quantized::conv1d_relu + std::string quantized_conv1d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv1d_relu(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // aten::conv2d + std::string conv2d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv2d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv2d - aten::relu + std::string conv2d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv2d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv2d - aten::relu_ + std::string conv2d_inplace_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv2d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu_(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::conv2d + std::string quantized_conv2d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv2d(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // quantized::conv2d_relu + std::string quantized_conv2d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv2d_relu(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // aten::conv3d + std::string conv3d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv3d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv3d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv3d - aten::relu + std::string conv3d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv3d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv3d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // aten::conv3d - aten::relu_ + std::string conv3d_inplace_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv3d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %conv_out = aten::conv3d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + %r = aten::relu_(%conv_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::conv3d + std::string quantized_conv3d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv3d(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // quantized::conv3d_relu + std::string quantized_conv3d_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %dilation, %groups): + %r_quant = quantized::conv3d_relu(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // aten::conv_transpose1d + std::string conv_transpose1d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %output_padding, %groups, %dilation): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv_transpose1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv_transpose1d(%a_dequant, %w_dequant, %b, %stride, %padding, %output_padding, %groups, %dilation) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::conv_transpose1d + std::string quantized_conv_transpose1d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %output_padding, %groups, %dilation): + %r_quant = quantized::conv_transpose1d(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // aten::conv_transpose2d + std::string conv_transpose2d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %output_padding, %groups, %dilation): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::conv_transpose2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv_transpose2d(%a_dequant, %w_dequant, %b, %stride, %padding, %output_padding, %groups, %dilation) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::conv_transpose1d + std::string quantized_conv_transpose2d = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype, %stride, %padding, %output_padding, %groups, %dilation): + %r_quant = quantized::conv_transpose2d(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r_quant) )"; + + std::string add_relu = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add(%a_dequant, %b_dequant, %alpha) + %r_relu = aten::relu(%r_add) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string add_inplace_relu = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add(%a_dequant, %b_dequant, %alpha) + %r_relu = aten::relu_(%r_add) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string inplace_add_relu = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add_(%a_dequant, %b_dequant, %alpha) + %r_relu = aten::relu(%r_add) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string inplace_add_inplace_relu = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add_(%a_dequant, %b_dequant, %alpha) + %r_relu = aten::relu_(%r_add) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string quantized_add_relu = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %r = quantized::add_relu(%a_quant, %b_quant, %scale, %zero_point) + return (%r) )"; + + // aten::linear + std::string linear = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::linear(%a_dequant, %w_dequant, %b) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string linear_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %linear_out = aten::linear(%a_dequant, %w_dequant, %b) + %r = aten::relu(%linear_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string linear_inplace_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype): + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %linear_out = aten::linear(%a_dequant, %w_dequant, %b) + %r = aten::relu_(%linear_out) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // quantized::linear + std::string quantized_linear = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype): + %r = quantized::linear(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r) )"; + + std::string quantized_linear_relu = R"( +graph(%a_quant, %packed_params, %r_scale, %r_zero_point, %r_dtype): + %r = quantized::linear_relu(%a_quant, %packed_params, %r_scale, %r_zero_point) + return (%r) )"; + + std::string cat = R"( +graph(%input_quant, %dim, %r_scale, %r_zero_point, %r_dtype): + %input_dequant = aten::dequantize(%input_quant) + %r = aten::cat(%input_dequant, %dim) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string quantized_cat = R"( +graph(%input_quant, %dim, %r_scale, %r_zero_point, %r_dtype): + %r_quant = quantized::cat(%input_quant, %dim, %r_scale, %r_zero_point) + return (%r_quant) )"; + + // aten::add + std::string add = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add(%a_dequant, %b_dequant, %alpha) + %r = aten::quantize_per_tensor(%r_add, %scale, %zero_point, %dtype) + return (%r) )"; + + // TODO: add %dtype after when https://github.com/pytorch/pytorch/issues/34351 + // is fixed + // quantized::add + std::string quantized_add = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %r = quantized::add(%a_quant, %b_quant, %scale, %zero_point) + return (%r) )"; + + // aten::add_ + std::string inplace_add = R"( +graph(%a_quant, %b_quant, %alpha, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_add = aten::add_(%a_dequant, %b_dequant, %alpha) + %r = aten::quantize_per_tensor(%r_add, %scale, %zero_point, %dtype) + return (%r) )"; + + auto add_scalar = getBinaryOpScalarFusionInfo( + "aten::add", + {"%b_scalar", "%alpha"}, + "quantized::add_scalar", + {"%b_scalar"}, + {aten_add_alpha_is_one, input_b_is_scalar}); + + auto add_scalar_out = getBinaryOpScalarFusionInfo( + "aten::add_", + {"%b_scalar", "%alpha"}, + "quantized::add_scalar_out", + {"%b_scalar", "%a_quant"}, + {aten_add_alpha_is_one, input_b_is_scalar}); + + // quantized::add_scalar_relu -- fusing quantized::add_scalar + // and aten::relu + auto quantized_add_scalar_relu_pattern = R"( +graph(%a_quant, %b_scalar): + %r_add = quantized::add_scalar(%a_quant, %b_scalar) + %r = aten::relu(%r_add) + return (%r) )"; + + auto quantized_add_scalar_inplace_relu_pattern = R"( +graph(%a_quant, %b_scalar): + %r_add = quantized::add_scalar(%a_quant, %b_scalar) + %r = aten::relu_(%r_add) + return (%r) )"; + + auto quantized_add_scalar_relu_replacement = R"( +graph(%a_quant, %b_scalar): + %r = quantized::add_scalar_relu(%a_quant, %b_scalar) + return (%r) )"; + + // quantized::add_scalar_relu_out -- fusing quantized::add_scalarOut + // and aten::relu + auto quantized_add_scalar_relu_out_pattern = R"( +graph(%a_quant, %b_scalar): + %r_add = quantized::add_scalar_out(%a_quant, %b_scalar, %a_quant) + %r = aten::relu(%r_add) + return (%r) )"; + + auto quantized_add_scalar_inplace_relu_out_pattern = R"( +graph(%a_quant, %b_scalar): + %r_add = quantized::add_scalar_out(%a_quant, %b_scalar, %a_quant) + %r = aten::relu_(%r_add) + return (%r) )"; + + auto quantized_add_scalar_relu_out_replacement = R"( +graph(%a_quant, %b_scalar): + %r = quantized::add_scalar_relu_out(%a_quant, %b_scalar, %a_quant) + return (%r) )"; + + // quantized::batch_norm + std::string batch_norm = R"( +graph(%a_quant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7, %scale, %zero_point, %scalar_type): + %a_dequant = aten::dequantize(%a_quant) + %r_bn = aten::batch_norm(%a_dequant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7) + %r = aten::quantize_per_tensor(%r_bn, %scale, %zero_point, %scalar_type) + return (%r) )"; + std::string quantized_batch_norm = R"( +graph(%a_quant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7, %scale, %zero_point, %scalar_type): + %r = quantized::batch_norm(%a_quant, %weight, %bias, %mean, %var, %eps, %scale, %zero_point) + return (%r) )"; + + std::string batch_norm_relu = R"( +graph(%a_quant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7, %scale, %zero_point, %scalar_type): + %a_dequant = aten::dequantize(%a_quant) + %bn_out = aten::batch_norm(%a_dequant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7) + %relu = aten::relu(%bn_out) + %r = aten::quantize_per_tensor(%relu, %scale, %zero_point, %scalar_type) + return (%r) )"; + std::string batch_norm_inplace_relu = R"( +graph(%a_quant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7, %scale, %zero_point, %scalar_type): + %a_dequant = aten::dequantize(%a_quant) + %bn_out = aten::batch_norm(%a_dequant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7) + %relu = aten::relu_(%bn_out) + %r = aten::quantize_per_tensor(%relu, %scale, %zero_point, %scalar_type) + return (%r) )"; + + std::string quantized_batch_norm_relu = R"( +graph(%a_quant, %weight, %bias, %mean, %var, %training, %eaf, %eps, %7, %scale, %zero_point, %scalar_type): + %r = quantized::batch_norm_relu(%a_quant, %weight, %bias, %mean, %var, %eps, %scale, %zero_point) + return (%r) )"; + + // aten::mul + std::string mul = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul(%a_dequant, %b_dequant) + %r = aten::quantize_per_tensor(%r_mul, %scale, %zero_point, %dtype) + return (%r) )"; + + // aten::mul_ + std::string inplace_mul = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul_(%a_dequant, %b_dequant) + %r = aten::quantize_per_tensor(%r_mul, %scale, %zero_point, %dtype) + return (%r) )"; + + // quantized::mul + std::string quantized_mul = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %r = quantized::mul(%a_quant, %b_quant, %scale, %zero_point) + return (%r) )"; + + auto mul_scalar = getBinaryOpScalarFusionInfo( + "aten::mul", + {"%b_scalar"}, + "quantized::mul_scalar", + {"%b_scalar"}, + {input_b_is_scalar}); + + auto mul_scalar_out = getBinaryOpScalarFusionInfo( + "aten::mul_", + {"%b_scalar"}, + "quantized::mul_scalar_out", + {"%b_scalar", "%a_quant"}, + {input_b_is_scalar}); + + // quantized::mul_relu + std::string mul_relu = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul(%a_dequant, %b_dequant) + %r_relu = aten::relu(%r_mul) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string mul_inplace_relu = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul(%a_dequant, %b_dequant) + %r_relu = aten::relu_(%r_mul) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string inplace_mul_relu = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul_(%a_dequant, %b_dequant) + %r_relu = aten::relu(%r_mul) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string inplace_mul_inplace_relu = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %a_dequant = aten::dequantize(%a_quant) + %b_dequant = aten::dequantize(%b_quant) + %r_mul = aten::mul_(%a_dequant, %b_dequant) + %r_relu = aten::relu_(%r_mul) + %r = aten::quantize_per_tensor(%r_relu, %scale, %zero_point, %dtype) + return (%r) )"; + + std::string quantized_mul_relu = R"( +graph(%a_quant, %b_quant, %scale, %zero_point, %dtype): + %r = quantized::mul_relu(%a_quant, %b_quant, %scale, %zero_point) + return (%r) )"; + + // quantized::mul_scalar_relu -- fusing quantized::mul_scalar + // and aten::relu + auto quantized_mul_scalar_relu_pattern = R"( +graph(%a_quant, %b_scalar): + %r_mul = quantized::mul_scalar(%a_quant, %b_scalar) + %r = aten::relu(%r_mul) + return (%r) )"; + + auto quantized_mul_scalar_inplace_relu_pattern = R"( +graph(%a_quant, %b_scalar): + %r_mul = quantized::mul_scalar(%a_quant, %b_scalar) + %r = aten::relu_(%r_mul) + return (%r) )"; + + auto quantized_mul_scalar_relu_replacement = R"( +graph(%a_quant, %b_scalar): + %r = quantized::mul_scalar_relu(%a_quant, %b_scalar) + return (%r) )"; + + // quantized::mul_scalar_relu_out -- fusing quantized::mul_scalarOut + // and aten::relu + auto quantized_mul_scalar_relu_out_pattern = R"( +graph(%a_quant, %b_scalar): + %r_mul = quantized::mul_scalar_out(%a_quant, %b_scalar, %a_quant) + %r = aten::relu(%r_mul) + return (%r) )"; + + auto quantized_mul_scalar_inplace_relu_out_pattern = R"( +graph(%a_quant, %b_scalar): + %r_mul = quantized::mul_scalar_out(%a_quant, %b_scalar, %a_quant) + %r = aten::relu_(%r_mul) + return (%r) )"; + + auto quantized_mul_scalar_relu_out_replacement = R"( +graph(%a_quant, %b_scalar): + %r = quantized::mul_scalar_relu_out(%a_quant, %b_scalar, %a_quant) + return (%r) )"; + + // quantized::elu + std::string elu = R"( +graph(%a_quant, %alpha, %scale, %input_scale, %r_scale, %r_zero_point, %r_dtype): + %a_dequant = aten::dequantize(%a_quant) + %r = aten::elu(%a_dequant, %alpha, %scale, %input_scale) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + std::string quantized_elu = R"( +graph(%a_quant, %alpha, %scale, %input_scale, %r_scale, %r_zero_point, %r_dtype): + %r_quant = quantized::elu(%a_quant, %r_scale, %r_zero_point, %alpha, %scale, %input_scale) + return (%r_quant) )"; + + std::string elu_ = R"( +graph(%a_quant, %alpha, %scale, %input_scale, %r_scale, %r_zero_point, %r_dtype): + %a_dequant = aten::dequantize(%a_quant) + %r = aten::elu_(%a_dequant, %alpha, %scale, %input_scale) + %r_quant = aten::quantize_per_tensor(%r, %r_scale, %r_zero_point, %r_dtype) + return (%r_quant) )"; + + // ============= General Ops that inherit quantization parameters from input + // tensor ============= + auto avg_pool1d = getInputTensorQParamOpFusionInfo( + "aten::avg_pool1d", + {"%kernel_size", + "%stride", + "%padding", + "%ceil_mode", + "%count_include_pad"}); + + auto avg_pool2d = getInputTensorQParamOpFusionInfo( + "aten::avg_pool2d", + {"%kernel_size", + "%stride", + "%padding", + "%ceil_mode", + "%count_include_pad", + "%divisor_override"}); + + auto avg_pool3d = getInputTensorQParamOpFusionInfo( + "aten::avg_pool3d", + {"%kernel_size", + "%stride", + "%padding", + "%ceil_mode", + "%count_include_pad", + "%divisor_override"}); + + auto adaptive_avg_pool1d = getInputTensorQParamOpFusionInfo( + "aten::adaptive_avg_pool1d", {"%output_size"}); + + auto adaptive_avg_pool2d = getInputTensorQParamOpFusionInfo( + "aten::adaptive_avg_pool2d", {"%output_size"}); + + auto adaptive_avg_pool3d = getInputTensorQParamOpFusionInfo( + "aten::adaptive_avg_pool3d", {"%output_size"}); + + auto mean1 = getInputTensorQParamOpFusionInfo("aten::mean", {"%dim"}); + + auto mean2 = getInputTensorQParamOpFusionInfo( + "aten::mean", {"%dim", "%keepdim", "%out"}); + + auto upsample_nearest1d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest1d", {"%output_size", "%scale_factors"}); + + auto upsample_nearest2d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest2d", {"%output_size", "%scale_factors"}); + + auto upsample_nearest3d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest3d", {"%output_size", "%scale_factors"}); + + auto upsample_linear1d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_linear1d", + {"%output_size", "%align_corners", "%scale_factors"}); + + auto upsample_bilinear2d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_bilinear2d", + {"%output_size", "%align_corners", "%scale_factors"}); + + auto upsample_trilinear3d_vec = getInputTensorQParamOpFusionInfo( + "aten::upsample_trilinear3d", + {"%output_size", "%align_corners", "%scale_factors"}); + + auto upsample_nearest1d = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest1d", {"%output_size", "%scales"}); + + auto upsample_nearest2d = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest2d", {"%output_size", "%scale_h", "%scale_w"}); + + auto upsample_nearest3d = getInputTensorQParamOpFusionInfo( + "aten::upsample_nearest3d", + {"%output_size", "%scale_d", "%scale_h", "%scale_w"}); + + auto upsample_linear1d = getInputTensorQParamOpFusionInfo( + "aten::upsample_linear1d", {"%output_size", "%align_corners", "%scales"}); + + auto upsample_bilinear2d = getInputTensorQParamOpFusionInfo( + "aten::upsample_bilinear2d", + {"%output_size", "%align_corners", "%scale_h", "%scale_w"}); + + auto upsample_trilinear3d = getInputTensorQParamOpFusionInfo( + "aten::upsample_trilinear3d", + {"%output_size", "%align_corners", "%scale_d", "%scale_h", "%scale_w"}); + + auto clamp = getClampOpFusionInfo("aten::clamp", {"%min", "%max"}); + + auto hardtanh = getClampOpFusionInfo("aten::hardtanh", {"%min", "%max"}); + + auto hardtanh_ = getClampOpFusionInfo("aten::hardtanh_", {"%min", "%max"}); + + auto leaky_relu = + getInputTensorQParamOpFusionInfo("aten::leaky_relu", {"%negative_slope"}); + + auto leaky_relu_ = getInputTensorQParamOpFusionInfo( + "aten::leaky_relu_", {"%negative_slope"}); + + // Ops with fixed quantization parameters + auto hardsigmoid = getFixedQParamOpFusionInfo("aten::hardsigmoid", {}, false); + + auto hardsigmoid_ = + getFixedQParamOpFusionInfo("aten::hardsigmoid_", {}, false); + + auto sigmoid = getFixedQParamOpFusionInfo("aten::sigmoid", {}, false); + + auto sigmoid_ = getFixedQParamOpFusionInfo("aten::sigmoid_", {}, false); + + auto tanh = getFixedQParamOpFusionInfo("aten::tanh", {}, true); + + auto tanh_ = getFixedQParamOpFusionInfo("aten::tanh_", {}, true); + + auto hardswish = getObservedQParamOpFusionInfo( + "aten::hardswish", "quantized::hardswish", {}, {}); + + auto hardswish_ = getObservedQParamOpFusionInfo( + "aten::hardswish_", "quantized::hardswish", {}, {}); + + auto layer_norm = getObservedQParamOpFusionInfo( + "aten::layer_norm", + "quantized::layer_norm", + {"%normalized_shape", "%weight", "%bias", "%eps", "%cudnn_enabled"}, + {"%normalized_shape", "%weight", "%bias", "%eps"}); + + auto group_norm = getObservedQParamOpFusionInfo( + "aten::group_norm", + "quantized::group_norm", + {"%num_groups", "%weight", "%bias", "%eps", "%cudnn_enabled"}, + {"%num_groups", "%weight", "%bias", "%eps"}); + + auto instance_norm = getObservedQParamOpFusionInfo( + "aten::instance_norm", + "quantized::instance_norm", + {"%weight", + "%bias", + "%running_mean", + "%running_var", + "%use_input_stats", + "%momentum", + "%eps", + "%cudnn_enabled"}, + {"%weight", "%bias", "%eps"}); + + return { + {"quantized::conv1d", std::move(conv1d), std::move(quantized_conv1d)}, + {"quantized::conv1d_relu", std::move(conv1d_relu), quantized_conv1d_relu}, + {"quantized::conv1d_relu", + std::move(conv1d_inplace_relu), + std::move(quantized_conv1d_relu)}, + {"quantized::conv2d", std::move(conv2d), std::move(quantized_conv2d)}, + {"quantized::conv2d_relu", std::move(conv2d_relu), quantized_conv2d_relu}, + {"quantized::conv2d_relu", + std::move(conv2d_inplace_relu), + std::move(quantized_conv2d_relu)}, + {"quantized::conv3d", std::move(conv3d), std::move(quantized_conv3d)}, + {"quantized::conv3d_relu", std::move(conv3d_relu), quantized_conv3d_relu}, + {"quantized::conv3d_relu", + std::move(conv3d_inplace_relu), + std::move(quantized_conv3d_relu)}, + {"quantized::conv_transpose1d", + std::move(conv_transpose1d), + std::move(quantized_conv_transpose1d)}, + {"quantized::conv_transpose2d", + std::move(conv_transpose2d), + std::move(quantized_conv_transpose2d)}, + {"quantized::linear", std::move(linear), std::move(quantized_linear)}, + {"quantized::linear_relu", std::move(linear_relu), quantized_linear_relu}, + {"quantized::linear_relu", + std::move(linear_inplace_relu), + std::move(quantized_linear_relu)}, + {"quantized::add_relu", + std::move(add_relu), + quantized_add_relu, + {aten_add_alpha_is_one}}, + {"quantized::add_relu", + std::move(add_inplace_relu), + quantized_add_relu, + {aten_add_alpha_is_one}}, + {"quantized::add_relu", + std::move(inplace_add_relu), + quantized_add_relu, + {aten_add_alpha_is_one}}, + {"quantized::add_relu", + std::move(inplace_add_inplace_relu), + std::move(quantized_add_relu), + {aten_add_alpha_is_one}}, + std::move(add_scalar), + std::move(add_scalar_out), + // note that these must come after quantized::add_scalar and + // quantized::add_scalar_out patterns + {"quantized::add_scalar_relu", + quantized_add_scalar_relu_pattern, + quantized_add_scalar_relu_replacement}, + {"quantized::add_scalar_relu", + quantized_add_scalar_inplace_relu_pattern, + quantized_add_scalar_relu_replacement}, + {"quantized::add_scalar_relu_out", + quantized_add_scalar_relu_out_pattern, + quantized_add_scalar_relu_out_replacement}, + {"quantized::add_scalar_relu_out", + quantized_add_scalar_inplace_relu_out_pattern, + quantized_add_scalar_relu_out_replacement}, + {"quantized::add", + std::move(add), + quantized_add, + {aten_add_alpha_is_one}}, + {"quantized::add", + std::move(inplace_add), + std::move(quantized_add), + {aten_add_alpha_is_one}}, + {"quantized::cat", std::move(cat), std::move(quantized_cat)}, + {"quantized::batch_norm", + std::move(batch_norm), + std::move(quantized_batch_norm)}, + {"quantized::batch_norm_relu", + std::move(batch_norm_relu), + quantized_batch_norm_relu}, + {"quantized::batch_norm_relu", + std::move(batch_norm_inplace_relu), + std::move(quantized_batch_norm_relu)}, + std::move(mul_scalar), + std::move(mul_scalar_out), + // note that these must come after quantized::mul_scalar and + // quantized::mul_scalar_out patterns + {"quantized::mul_scalar_relu", + quantized_mul_scalar_relu_pattern, + quantized_mul_scalar_relu_replacement}, + {"quantized::mul_scalar_relu", + quantized_mul_scalar_inplace_relu_pattern, + quantized_mul_scalar_relu_replacement}, + {"quantized::mul_scalar_relu_out", + quantized_mul_scalar_relu_out_pattern, + quantized_mul_scalar_relu_out_replacement}, + {"quantized::mul_scalar_relu_out", + quantized_mul_scalar_inplace_relu_out_pattern, + quantized_mul_scalar_relu_out_replacement}, + {"quantized::mul_relu", std::move(mul_relu), quantized_mul_relu}, + {"quantized::mul_relu", std::move(mul_inplace_relu), quantized_mul_relu}, + {"quantized::mul_relu", std::move(inplace_mul_relu), quantized_mul_relu}, + {"quantized::mul_relu", + std::move(inplace_mul_inplace_relu), + std::move(quantized_mul_relu)}, + {"quantized::mul", std::move(mul), quantized_mul}, + {"quantized::mul", std::move(inplace_mul), std::move(quantized_mul)}, + std::move(hardswish), + std::move(hardswish_), + std::move(layer_norm), + std::move(group_norm), + std::move(instance_norm), + {"quantized::elu", std::move(elu), quantized_elu}, + {"quantized::elu_", std::move(elu_), std::move(quantized_elu)}, + std::move(avg_pool1d), + std::move(avg_pool2d), + std::move(avg_pool3d), + std::move(adaptive_avg_pool1d), + std::move(adaptive_avg_pool2d), + std::move(adaptive_avg_pool3d), + std::move(mean1), + std::move(mean2), + std::move(upsample_nearest1d), + std::move(upsample_nearest2d), + std::move(upsample_nearest3d), + std::move(upsample_linear1d), + std::move(upsample_bilinear2d), + std::move(upsample_trilinear3d), + std::move(upsample_nearest1d_vec), + std::move(upsample_nearest2d_vec), + std::move(upsample_nearest3d_vec), + std::move(upsample_linear1d_vec), + std::move(upsample_bilinear2d_vec), + std::move(upsample_trilinear3d_vec), + std::move(clamp), + std::move(hardtanh), + std::move(hardtanh_), + std::move(leaky_relu), + std::move(leaky_relu_), + // fixed qparam ops + std::move(hardsigmoid), + std::move(hardsigmoid_), + std::move(sigmoid), + std::move(sigmoid_), + std::move(tanh), + std::move(tanh_), + }; +} + +inline std::vector +dynamic_quantized_linear_pattern_and_replacements() { + std::string linear_dynamic = R"( +graph(%packed_params, %a): + %w_quant : Tensor, %b : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::linear(%a, %w_dequant, %b) + return (%r) )"; + + // This pattern ignores reduce range + // Set the reduce range to default to true, since qnnpack backend ignores this + // argument. + std::string quantized_linear_dynamic = R"( +graph(%packed_params, %a): + %reduce_range : bool = prim::Constant[value=1]() + %r = quantized::linear_dynamic(%a, %packed_params, %reduce_range) + return (%r) )"; + + return { + {"quantized::linear_dynamic", + std::move(linear_dynamic), + std::move(quantized_linear_dynamic)}, + }; +} + +static std::vector +dynamic_quant_fusion_pattern_and_replacements() { + std::string linear_dynamic = R"( +graph(%packed_params, %a, %reduce_range, %a_dtype): + %a_scale : float, %a_zero_point : int = aten::_choose_qparams_per_tensor(%a, %reduce_range) + %a_quant = aten::quantize_per_tensor(%a, %a_scale, %a_zero_point, %a_dtype) + %a_dequant = aten::dequantize(%a_quant) + %w_quant : Tensor, %b : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant) + %r = aten::linear(%a_dequant, %w_dequant, %b) + return (%r) )"; + + std::string quantized_linear_dynamic = R"( +graph(%packed_params, %a, %reduce_range, %a_dtype): + %r = quantized::linear_dynamic(%a, %packed_params, %reduce_range) + return (%r) )"; + + std::string linear_dynamic_fp16 = R"( +graph(%packed_params, %a): + %w_unpacked : Tensor, %b : Tensor? = quantized::linear_unpack_fp16(%packed_params) + %r = aten::linear(%a, %w_unpacked, %b) + return (%r) )"; + + std::string quantized_linear_dynamic_fp16 = R"( +graph(%packed_params, %a): + %r = quantized::linear_dynamic_fp16(%a, %packed_params) + return (%r) )"; + + return { + {"quantized::linear_dynamic", + std::move(linear_dynamic), + std::move(quantized_linear_dynamic)}, + {"quantized::linear_dynamic_fp16", + std::move(linear_dynamic_fp16), + std::move(quantized_linear_dynamic_fp16)}, + }; +} + +static std::vector linear_prepack_unpack_patterns() { + std::string linear_with_quant = R"( +graph(%a_dequant, %w_quant, %b): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::linear(%a_dequant, %w_dequant, %b) + return (%r) )"; + + std::string linear_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b): + %packed_params = quantized::linear_prepack(%w_quant, %b) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::linear_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::linear(%a_dequant, %w_dequant, %b_unpacked) + return (%r) )"; + std::string linear_fp16_with_cast = R"( +graph(%w, %a_dq, %b): + %fp16_tensor = aten::_saturate_weight_to_fp16(%w) + %r = aten::linear(%a_dq, %fp16_tensor, %b) + return (%r) )"; + std::string linear_fp16_with_prepack = R"( +graph(%w, %a_dq, %b): + %packed_params = quantized::linear_prepack_fp16(%w, %b) + %w_unpacked : Tensor, %b_unpacked : Tensor? = quantized::linear_unpack_fp16(%packed_params) + %r = aten::linear(%a_dq, %w_unpacked, %b_unpacked) + return (%r) )"; + + return { + {"linear_prepack_unpack", + std::move(linear_with_quant), + std::move(linear_with_quant_prepack)}, + {"linear_fp16_prepack_unpack", + std::move(linear_fp16_with_cast), + std::move(linear_fp16_with_prepack)}, + }; +} + +static std::vector conv_prepack_unpack_patterns() { + std::string conv1d_with_quant = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv1d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv1d_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %packed_params : __torch__.torch.classes.quantized.Conv2dPackedParamsBase = quantized::conv1d_prepack(%w_quant, %b, %stride, %padding, %dilation, %groups) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::conv1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::conv1d(%a_dequant, %w_dequant, %b_unpacked, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv2d_with_quant = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv2d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv2d_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %packed_params : __torch__.torch.classes.quantized.Conv2dPackedParamsBase = quantized::conv2d_prepack(%w_quant, %b, %stride, %padding, %dilation, %groups) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::conv2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::conv2d(%a_dequant, %w_dequant, %b_unpacked, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv3d_with_quant = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv3d(%a_dequant, %w_dequant, %b, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv3d_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %dilation, %groups): + %packed_params : __torch__.torch.classes.quantized.Conv3dPackedParamsBase = quantized::conv3d_prepack(%w_quant, %b, %stride, %padding, %dilation, %groups) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::conv3d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::conv3d(%a_dequant, %w_dequant, %b_unpacked, %stride, %padding, %dilation, %groups) + return (%r) )"; + + std::string conv_transpose1d_with_quant = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %output_padding, %groups, %dilation): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv_transpose1d(%a_dequant, %w_dequant, %b, %stride, %padding, %output_padding, %groups, %dilation) + return (%r) )"; + + std::string conv_transpose1d_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %output_padding, %groups, %dilation): + %packed_params : __torch__.torch.classes.quantized.Conv2dPackedParamsBase = quantized::conv_transpose1d_prepack(%w_quant, %b, %stride, %padding, %output_padding, %dilation, %groups) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::conv_transpose1d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::conv_transpose1d(%a_dequant, %w_dequant, %b_unpacked, %stride, %padding, %output_padding, %groups, %dilation) + return (%r) )"; + + std::string conv_transpose2d_with_quant = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %output_padding, %groups, %dilation): + %w_dequant = aten::dequantize(%w_quant) + %r = aten::conv_transpose2d(%a_dequant, %w_dequant, %b, %stride, %padding, %output_padding, %groups, %dilation) + return (%r) )"; + + std::string conv_transpose2d_with_quant_prepack = R"( +graph(%a_dequant, %w_quant, %b, %stride, %padding, %output_padding, %groups, %dilation): + %packed_params : __torch__.torch.classes.quantized.Conv2dPackedParamsBase = quantized::conv_transpose2d_prepack(%w_quant, %b, %stride, %padding, %output_padding, %dilation, %groups) + %w_quant_unpacked : Tensor, %b_unpacked : Tensor? = quantized::conv_transpose2d_unpack(%packed_params) + %w_dequant = aten::dequantize(%w_quant_unpacked) + %r = aten::conv_transpose2d(%a_dequant, %w_dequant, %b_unpacked, %stride, %padding, %output_padding, %groups, %dilation) + return (%r) )"; + + return { + {"conv1d_prepack_unpack", + std::move(conv1d_with_quant), + std::move(conv1d_with_quant_prepack)}, + {"conv2d_prepack_unpack", + std::move(conv2d_with_quant), + std::move(conv2d_with_quant_prepack)}, + {"conv3d_prepack_unpack", + std::move(conv3d_with_quant), + std::move(conv3d_with_quant_prepack)}, + {"conv_transpose1d_prepack_unpack", + std::move(conv_transpose1d_with_quant), + std::move(conv_transpose1d_with_quant_prepack)}, + {"conv_transpose2d_prepack_unpack", + std::move(conv_transpose2d_with_quant), + std::move(conv_transpose2d_with_quant_prepack)}}; +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_type.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_type.h new file mode 100644 index 0000000000000000000000000000000000000000..1b91854a5e5ca7826c01fd6d054c1a006d04ac07 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/quantization_type.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace torch::jit { + +// Quantization type (dynamic quantization, static quantization). +// Should match the Python enum in quantize_jit.py +enum QuantType : std::uint8_t { DYNAMIC = 0, STATIC }; + +std::ostream& operator<<(std::ostream& os, QuantType t); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/register_packed_params.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/register_packed_params.h new file mode 100644 index 0000000000000000000000000000000000000000..dcee7144f66f7f41e53c0975f25d9dcf0757edae --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/quantization/register_packed_params.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +using PrePackParamFilterFn = std::function; + +TORCH_API std::unordered_set RegisterPrePackParams( + Module& m, + const std::string& method_name, + const PrePackParamFilterFn& is_packed_param, + const std::string& attr_prefix); + +TORCH_API std::string joinPaths(const std::vector& paths); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/refine_tuple_types.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/refine_tuple_types.h new file mode 100644 index 0000000000000000000000000000000000000000..49b8750b72ce8591deca5617654c91ab61d30cb3 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/refine_tuple_types.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace torch::jit { + +// updates the types of tuples according to the type of their current inputs. +TORCH_API void RefineTupleTypes(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_dropout.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_dropout.h new file mode 100644 index 0000000000000000000000000000000000000000..b1ddb931cf51e1c6acf85e9f7d90c84e68bf5270 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_dropout.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API void removeDropout(std::shared_ptr& graph); + +TORCH_API void removeDropout(script::Module& module); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_exceptions.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..7b6d37fbbd9f1cd35e750c7fe92bcf3788a3cbd2 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_exceptions.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace torch::jit { + +// Considering prim::RaiseException nodes unreachable, simplify prim::If nodes +// when one of the branches contains prim::RaiseException. +// +// This pass is illegal in general case as the modified graph might not throw +// an exception that the original graph would throw. The purpose of the pass is +// to cleanup the graph in a "risky" way by removing pathways leading to +// RaiseExceptions nodes. In some sense, this pass could be considered as a +// "Release" mode, while the original graph was in a "Debug" mode. +// The pass should only be used when such transformation is guaranteed to be +// safe by some other mechanisms. For instance, when we know exact shapes of +// tensors flowing through the graph and tensors with such shapes never cause +// exceptions. +TORCH_API void EliminateExceptions(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_expands.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_expands.h new file mode 100644 index 0000000000000000000000000000000000000000..483649d0e918c00cbd47b262110e3cbe24e42a11 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_expands.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void RemoveExpands(const std::shared_ptr& graph); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_inplace_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_inplace_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..344f0d50148e5e54a69a121c0e0870b90fffe788 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_inplace_ops.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +namespace torch::jit { +// see .cpp for docs +TORCH_API void RemoveInplaceOps(const std::shared_ptr& graph); + +TORCH_API void ImplicitCastForBinaryInplaceOps(Block* block); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_mutation.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_mutation.h new file mode 100644 index 0000000000000000000000000000000000000000..4f13698c8810608e31124679a6a07fae440e55c8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_mutation.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace torch::jit { + +struct TORCH_API MutationRemover { + MutationRemover( + std::shared_ptr graph, + std::optional> mutation_filter = std::nullopt) + : mutation_filter_(std::move(mutation_filter)), + aliasDb_(nullptr), + graph_(std::move(graph)) {} + + // return true if graph is modified + bool removeListMutation(); + + // return true if graph is modified + bool removeTensorMutation(); + + bool isSpecialMappedOp(Node* n) { + return n->matches("aten::zero_(Tensor(a!) self) -> Tensor(a!)") || + n->matches( + "aten::fill_.Scalar(Tensor(a!) self, Scalar value) -> Tensor(a!)") || + n->matches( + "aten::normal_(Tensor(a!) self, float mean=0, float std=1, *, Generator? generator=None) -> Tensor(a!)"); + } + + bool inplaceOpVariant(Node* n); + + static bool hasSideEffectOrAlias(Value* v, AliasDb* aliasDb); + + private: + Node* createSpecialMappedOp(Node* n); + bool listMutationFollowingListConstruct(Node* n); + bool tryMakeCreationAndMutationAtomic( + Value* mutated_value, + Node* mutating_op); + bool tryMakeUnaliasedIfOutputAndMutationAtomic( + Value* mutated_value, + Node* mutating_op); + // return true if graph is modified + bool RemoveListMutation(Block* block); + // return true if graph is modified + bool RemoveTensorMutation(Block* block); + + AliasDb* getOrCreateAliasDb() { + if (!aliasDb_) { + aliasDb_ = std::make_unique(graph_); + } + return aliasDb_.get(); + } + + std::optional> mutation_filter_; + std::unique_ptr aliasDb_ = nullptr; + std::shared_ptr graph_; +}; + +// Removes list mutation with functional equivalents +// return true if graph is modified +TORCH_API bool RemoveListMutation(const std::shared_ptr& graph); + +// Replaces in-place aten ops with their functional equivalents +// when it can be proven that this does not change graph semantics +// if `mutation_filter` is present, the pass will only attempt to +// remove mutation on nodes which return true for the filter +// return true if graph is modified +TORCH_API bool RemoveTensorMutation( + const std::shared_ptr& graph, + std::optional> mutation_filter = std::nullopt); + +// Replaces in-place aten activation ops with their functional equivalence +TORCH_API bool InplaceToFunctionalActivation( + const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_redundant_profiles.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_redundant_profiles.h new file mode 100644 index 0000000000000000000000000000000000000000..0360bdf2092e571113847827592cbd6edba1fb1d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/remove_redundant_profiles.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API void RemoveRedundantProfiles(std::shared_ptr& graph); +TORCH_API void RemoveRedundantProfiles(Block* block, AliasDb& db); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/replacement_of_old_operators.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/replacement_of_old_operators.h new file mode 100644 index 0000000000000000000000000000000000000000..ec702fe6416edf20bfb8119cd8a1256f8aff32d5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/replacement_of_old_operators.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace torch::jit { + +// Find the valid upgrader graph for the upgrader and cache the result +// for later lookups. Will error out if there is no valid upgrader graph +// provided for the upgrader name. +std::shared_ptr getUpgraderGraph(const std::string& upgrader_name); + +TORCH_API void ReplaceOldOperatorsWithUpgraders(std::shared_ptr graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/requires_grad_analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/requires_grad_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..bd60e4e249dbf5d550e4156760aa80b0830112c0 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/requires_grad_analysis.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include + +namespace torch::jit { + +struct Graph; +struct ArgumentSpec; + +TORCH_API void PropagateRequiresGrad(std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/restore_mutation.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/restore_mutation.h new file mode 100644 index 0000000000000000000000000000000000000000..949e60f21e23956162543688f1200e0379459e7d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/restore_mutation.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +// A map which stores if an activation operator can perform type promotion +const std::unordered_map activation_type_promotion_mapping = { + {aten::sigmoid, true}, + {aten::tanh, true}, + {aten::celu, false}, + {aten::elu, false}, + {aten::gelu, false}, + {aten::glu, false}, + {aten::hardshrink, false}, + {aten::hardsigmoid, false}, + {aten::hardswish, false}, + {aten::hardtanh, false}, + {aten::leaky_relu, false}, + {aten::prelu, false}, + {aten::relu6, false}, + {aten::relu, false}, + {aten::rrelu, false}, + {aten::selu, false}, + {aten::silu, false}}; + +class FunctionalToInplaceRewriter { + public: + FunctionalToInplaceRewriter(std::shared_ptr graph); + + bool FunctionalToInplace(Block* block); + + private: + AliasDb* getOrCreateAliasDb() { + if (!aliasDb_) { + aliasDb_ = std::make_unique(graph_); + } + return aliasDb_.get(); + } + + bool CanBeInplace(Node* node); + + std::unique_ptr aliasDb_ = nullptr; + std::shared_ptr graph_; +}; + +// A common application scenario is to apply InplaceToFunctionalActivation +// before some JIT optimization passes, so that those passes are less +// constrained by in-place ops. After those passes are done, we can call +// FunctionalToInplaceActivation to recover in-place activation ops, +// so that we won't lose the performance benefit coming from memory reduction. + +// Replaces functional aten activation ops with their in-place equivalents +TORCH_API bool FunctionalToInplaceActivation( + const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/shape_analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/shape_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..d784f7fc23237064a8387ba993b312a36e83375c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/shape_analysis.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +struct Graph; + +struct propagation_error : std::exception {}; + +class PropertyPropBase { + // Used for both Shape Propagation and Dtype/Device Propagation + public: + explicit PropertyPropBase(std::shared_ptr graph) + : graph_(std::move(graph)) {} + virtual ~PropertyPropBase() = default; + + void propagateBlock(Block* block, bool insert_expands = true); + // insert_expands is used for shape inference + + void processIf(Node* node); + void processLoop(Node* node); + + protected: + virtual void propagateNode(Node* node, bool insert_expands = true) = 0; + void setUnshapedType(Value* o); + void setUnshapedType(Node* node); + std::shared_ptr graph_; +}; + +TORCH_API void EraseShapeInformation(const std::shared_ptr& graph); +TORCH_API void PropagateInputShapes(const std::shared_ptr& graph); + +TORCH_API bool mergeTypes( + ArrayRef lhs, + ArrayRef rhs, + ArrayRef outputs); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/specialize_autogradzero.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/specialize_autogradzero.h new file mode 100644 index 0000000000000000000000000000000000000000..d8fbd44c2037af1910a7fb82099fd3a14654141a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/specialize_autogradzero.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace torch::jit { + +// propagate autograd zero information through a gradient graph and +// remove grad_of blocks if present. +// Note: this is a very limited pass. It only propagates autograd zeros for +// operations generated by the symbolic autodiff code and cleans up +// AutogradAdds when possible. Outputs of other nodes are conservatively +// marked Unknown and not optimized. +TORCH_API void specializeAutogradZero(std::shared_ptr g); + +struct ProfilingRecord; + +TORCH_API void InsertProfileNodesForSpecializeAutogradZero(ProfilingRecord* pr); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/subgraph_rewrite.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/subgraph_rewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..4e4f4a04b68e386033d303f367e1f0514c3f4cea --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/subgraph_rewrite.h @@ -0,0 +1,115 @@ +/** This file defines API for pattern-based subgraph rewrites. + * + * The API can be used for finding concrete patterns in the model and replacing + * the corresponding subgraphs with another subgraph. A special case of such + * rewrites is fusion, where the new subgraph consists of just a single node. + * + * There is a default set of the most common patterns that everyone could use. + * Alternatively, an arbitrary pattern can be registered. + */ +#pragma once + +#include +#include + +#include +#include +#include + +namespace torch::jit { + +// Forward declarations. +struct RewritePatternDescr; +struct Match; + +using MatchFilter = std::function< + bool(const Match&, const std::unordered_map&)>; + +/** Run pattern-based subgraph rewrites on all methods in the module. + * + * This pass will go through all methods in the module and try to replace all + * recognized patterns (see SubgraphRewriter::RegisterDefaultPatterns for the + * list of these patterns). + */ +TORCH_API Module PatternBasedRewrite(const Module& module); + +/** A class implementing API for pattern-based subgraph rewrites. + * + * To perform pattern-based subgraph rewrites on a module using this API, one + * needs to create an object of such class, register rewrite patterns and run + * the transformation pass (`runOnModule`). + * + * To use standard patterns, one could use `RegisterDefaultPatterns`. + * + * To enable rewrites of custom patterns, the custom patterns must be registered + * with `RegisterRewritePattern`. + */ +class TORCH_API SubgraphRewriter { + public: + // Run pattern-based subgraph rewrite pass on the module. + Module runOnModule(const Module& module); + + // Run pattern-based subgraph rewrite pass on the graph (used in testing). + // `filter` is a function that does extra filtering on the match. If it + // returns false for a given Match, we'll skip the Match. The filter + // function's arguments consist of a Match and a value map from parsing the + // pattern graph. Both the Match and the value map are necessary because we + // need to 1) do extra filtering on the matched result as well as 2) refer to + // the values in the matched result through the values in the pattern graph. + void runOnGraph( + std::shared_ptr& graph, + const std::vector& filters); + + void runOnGraph( + std::shared_ptr& graph, + const MatchFilter& filter = + [](const Match&, const std::unordered_map&) { + return true; + }) { + runOnGraph(graph, std::vector({filter})); + } + + // Register standard rewrite patterns. + void RegisterDefaultPatterns(); + + /** Register a custom rewrite pattern. + * + * The method takes two parameters specifying the pattern: + * \p PATTERN - IR string representing the pattern subgraph. + * \p REPLACEMENT - IR string representing the replacement subgraph. + * \p value name map - vector of pairs mapping values in the replacement graph + * to the values in the pattern graph. Used for preserving source range info + * across graph rewrite. + * + * See examples of pattern registering in `RegisterDefaultPatterns`. + */ + void RegisterRewritePattern( + const std::string& pattern, + const std::string& replacement, + const std::vector>& value_name_pair = + {}); + + private: + std::vector patterns_; + std::unordered_set nodes_to_delete_; + + void rewriteSinglePatternOnGraph( + std::shared_ptr& graph, + const RewritePatternDescr& pattern, + const std::vector& filters); + + bool overlapsWithPreviousMatches(const Match* match); +}; + +/** Rewrite pattern descriptor. + * + * This structure is used in the implementation of `SubgraphRewriter` and + * is not supposed to be used externally. + */ +struct RewritePatternDescr { + std::string pattern; + std::string replacement; + std::unordered_map value_name_map; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..f6e37f410f9833bac2f8d86e46ead3c0d5b3bca7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_analysis.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +// CAUTION NOT TO BE USED, STILL A WIP, NOT STABLE + +TORCH_API void PropagateShapesOnGraph(std::shared_ptr& graph); + +// CAUTION NOT TO BE USED, STILL A WIP, NOT STABLE +// From [beg, end) attempt to propagate shapes and +// build up a graph that will compute all remaining symbolic +// shapes in [beg, end) that can be executed before beg + +struct ShapeComputeGraphMapping { + ShapeComputeGraphMapping( + std::shared_ptr partial_eval_shape_graph, + std::unordered_map + enclosing_graph_value_to_shape_graph_input, + std::unordered_map graph_output_to_symbolic_shape_dim) + : partial_eval_shape_graph(std::move(partial_eval_shape_graph)), + enclosing_graph_value_to_shape_graph_input_( + std::move(enclosing_graph_value_to_shape_graph_input)), + graph_output_to_symbolic_shape_dim_( + std::move(graph_output_to_symbolic_shape_dim)) {} + + std::shared_ptr partial_eval_shape_graph; + std::unordered_map + enclosing_graph_value_to_shape_graph_input_; + std::unordered_map graph_output_to_symbolic_shape_dim_; +}; + +TORCH_API std::optional +PropagateShapesAndBuildLargeShapeComputeGraph( + std::shared_ptr& graph, + Node* beg, + Node* end); + +// don't insert complete tensor shapes in shape compute graphs and instead +// rely on our partial evaluation pipeline to propagate information. +// this is a good proxy for our ability to propagate non-complete shape +// information. +TORCH_API bool setSymbolicShapeAnalysisTestMode(bool value); +TORCH_API bool symbolicShapeAnalysisTestModeEnabled(); + +using SSAInput = std::variant; +TORCH_API std::optional> +calculateSymbolicShapesOnOp( + const FunctionSchema* schema, + const std::vector& inputs); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_cache.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..d9c7f66ee662583d28e85bfb61278a7011cc3a45 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_cache.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +struct TORCH_API CanonicalizedSymbolicShape { + // TODO: Consider in the future if it is reasonable to + // merge code with SymbolicShape or VaryingShape while keeping + // the two not implicitly convertable (and cause bugs). + CanonicalizedSymbolicShape( + const c10::SymbolicShape& orig_shape, + std::unordered_map& ss_map) { + init(orig_shape, ss_map); + } + + CanonicalizedSymbolicShape(c10::SymbolicShape& orig_shape) { + std::unordered_map new_ssmap; + init(orig_shape, new_ssmap); + } + + size_t hash() const; + + c10::SymbolicShape toSymbolicShape( + std::unordered_map& inverse_ss_map) const; + + TORCH_API friend bool operator==( + const CanonicalizedSymbolicShape& a, + const CanonicalizedSymbolicShape& b); + + private: + std::optional> values_; + + void init( + const c10::SymbolicShape& orig_shape, + std::unordered_map& ss_map); +}; + +// SHAPE CACHE API +TORCH_API std::optional> +get_cached_shape_function( + const FunctionSchema* schema, + const std::vector& arg_vec); + +TORCH_API void cache_shape_function( + const FunctionSchema* schema, + const std::vector& arg_vec, + const std::vector& ret_vec); + +// For use in test code +TORCH_API void clear_shape_cache(); +TORCH_API size_t get_shape_cache_size(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_runtime_fusion.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_runtime_fusion.h new file mode 100644 index 0000000000000000000000000000000000000000..b10632a01bea9e7b8ddaa4e0409c86ad8bc83b60 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/symbolic_shape_runtime_fusion.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +// Takes in a TensorExprGraph of static shapes and generalizes the input shapes +// to symbolic dimensions. Dimensions of value 1 will be preserved, otherwise +// dimensions with the same value will be bucketed to the same symbolic shape. +// E.g. Tensor(5, 3), Tensor(3, 1) -> Tensor(SS(-1), SS(-2)), Tensor(SS(-2), 1) +// From there, runs symbolic shape inference on the graph, and creates a +// versioning if in the graph with prim::TensorExprDynamicGuard checking if +// the inputs at runtime match the Generalized Symbolic Shapes that are inputs +// to the TE Kernel. The computate to calculate all symbolic dimensions is +// inlined in to the if block with the TE Kernel. All Sym Dim Value* are +// appended to the end of the TE Kernel Graph/Node inputs, and the Node is +// augmented with a integer list attr `symbolic_shape_inputs` that gives the +// mapping from Value * -> Symbolic Shape int64_t value. For more lengthy IR +// examples and walkthrough look at ShapeAnalysisTest.DynamicShapesFusion in +// `test_shape_analysis` Returns True on Success, False on Failure, can fail if +// shape propagation fails to propagate # of dims or if complete shapes on +// inputs not set + +TORCH_API bool GenerateGuard( + Node* tensorexpr_graph_node, + bool add_composed_op = false); + +TORCH_API void runTensorExprDynamicGroup(const Code& code, Stack& stack); + +enum class StrideInput { + // Tensors natively store whether they are contiguous or not as a property + // this makes it faster to query `is_contiguous` or + // `is_contiguous(memory_format=channels_last)` + // than looping through the sizes/strides yourself + // For tensors with these properties, we only store one value: + TENSOR_CONT, + TENSOR_CONT_CHANNELS_LAST, + // now, we describe other cases, where there is one stride enum + // per dimension + S_ONE, // STRIDE_ONE: packed + S_CONT, // STRIDE_CONTIGUOUS: stride[i + 1] * sizes[i + 1] + S_TRAN_CONT, // STRIDE_TRANSPOSED_CONTIGUOUS: stride[i-1] * sizes[i-1] + S_AS_ARG, // STRIDE_AS_ARG: stride passed in as runtime value +}; + +TORCH_API std::string toString(StrideInput si); +TORCH_API StrideInput strideInputFromString(const std::string& si); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/tensorexpr_fuser.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/tensorexpr_fuser.h new file mode 100644 index 0000000000000000000000000000000000000000..dc2b9c11b4ac5c0e72341c21588500db3fd5c309 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/tensorexpr_fuser.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +// Run TensorExpressions-based fuser. +// If add_composed_op is true, creates a single operation that +// performs both the runtime check that types align +// and then the dispatch to the kernel/unoptimized graph +TORCH_API void FuseTensorExprs( + std::shared_ptr& graph, + size_t min_group_size = 2, + bool add_composed_op = false, + bool fuse_to_dynamic_shapes = false); + +TORCH_API void setTensorExprFuserEnabled(bool val); +TORCH_API bool tensorExprFuserEnabled(); +TORCH_API void setTensorExprDynamicShapeFusionEnabled(bool val); +TORCH_API bool tensorExprDynamicShapeFusionEnabled(); +TORCH_API bool setTexprReductionsEnabled(bool value); +TORCH_API bool texprReductionsEnabled(); + +TORCH_API void RemoveProfileNodesAndSpecializeTypes( + std::shared_ptr& graph); +TORCH_API bool hasTensorTypeSpecialization(Value* v); +TORCH_API void RemoveTensorTypeSpecializations(std::shared_ptr& graph); +TORCH_API void removeTensorTypeSpecializations(Block* block); + +using tensor_type_converter_t = + c10::function_ref; + +// inserts a TypeCheck pattern +// +// around the guarded node that has a Subgraph attribute, this inserts a pattern +// +// if TypeCheck(...): +// guarded_node +// else: +// FallbackGraph(...) +// +// The TypeCheck includes the types of all Tensor inputs to the guarded_node, +// as processed by the type_converter, a lambda +// TensorTypePtr(const TensorTypePtr& t). This allows to erase irrelevant +// aspects of the type. +// +// The Fallback graph will have the same subgraph as the guarded node (with the +// expectation that the guarded_node's subgraph will then be optimized. +TORCH_API void insertTypeGuard( + Node* guarded_node, + tensor_type_converter_t type_converter, + c10::Symbol kind); + +TORCH_API bool usedOnlyInSize(Value* v); +TORCH_API Value* broadcastSizes(at::ArrayRef sizes, AliasDb* db); + +namespace tensorexpr { +TORCH_API bool isSupported(Node* node); + +/// Get the modifiable custom operator set object. +/// +/// For static shapes, if a custom operator has been added to the custom +/// operator set, it will be pulled into the NNC fusion group. But it doesn't +/// work with dynamic shapes unless explicitly register the shape function via +/// `torch::jit::RegisterShapeComputeGraphForSchema` for the custom operator. +/// +/// @return Reference of the custome operator set +/// +TORCH_API OperatorSet& getCustomOperatorSet(); +} // namespace tensorexpr +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/update_differentiable_graph_requires_grad.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/update_differentiable_graph_requires_grad.h new file mode 100644 index 0000000000000000000000000000000000000000..92ce0678d1736952909596a12c04caa2a574a7e3 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/update_differentiable_graph_requires_grad.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace torch::jit { + +// Because differentiable graphs detach the gradients of input Tensors, +// creating and inlining differentiable graphs changes the requires_grad +// property of tensors in the graph. This pass updates prim::profiles +// requires_grad to keep profiled properties up to date, it does not update +// grad properties of other nodes like graph inputs bc the only downstream +// user of the grad property is the profiling executor, which just uses +// the types of prim::profiles +TORCH_API void UpdateDifferentiableGraphRequiresGrad( + std::shared_ptr& diff_forward_graph, + std::optional new_requires_grad); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/check_alias_annotation.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/check_alias_annotation.h new file mode 100644 index 0000000000000000000000000000000000000000..e227c3bb456027f1593dd24ff27d7064d1900b8c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/check_alias_annotation.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace torch::jit { + +// Verify that alias annotations are correct. See impl for definition of +// "correct". +// +// This function expects a graph with a single op with `unqualifiedOpName`, plus +// the inputs that you would otherwise have passed to the graph executor. +TORCH_API void checkAliasAnnotation( + const std::shared_ptr& graph, + std::vector pythonInputs, + const std::string& unqualifiedOpName); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/memory_dag.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/memory_dag.h new file mode 100644 index 0000000000000000000000000000000000000000..dc6d5b24a09fef760d0b75cc429edde266a7ff61 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/memory_dag.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Uses a compressed index representation for faster comparisons +typedef c10::SparseBitVector<256> MemoryLocations; +namespace torch::jit { + +struct Value; + +using AliasTypeSet = std::vector; + +// `Element` represents a vertex in the points-to graph. It represents +// anything that could have an aliasing relationship--mostly IR +// `Value`s, but also wildcards or the type inside a container (e.g. `T` +// in `List[T]`) +struct Element { + Element(const Value* value_, unsigned index_); + // wildcard constructor + explicit Element(unsigned index_); + + // Index into the owning DAG's bit vector that represents this element. + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + unsigned index; + + // All elements that this element *may* point to. It's possible to have + // multiple elements that you might point to due to control flow/complex ops + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + MemoryLocations pointsTo; + // Backreference for points-to. + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + MemoryLocations pointedFrom; + + // Elements can contain other elements (e.g. List[Tensor]) + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + MemoryLocations containedElements; + + // The values that this element corresponds to. May be empty if this element + // doesn't represent a first-class value. + // This is for debug information only. + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + std::unordered_set values; + + private: + // Make `from` point at `to`. + void makePointerTo(Element* from, Element* to); + + friend class MemoryDAG; + // We memoize the results of `getMemoryLocations` to speed up queries. + // A nullopt means that this cache is not yet populated. Since `MemoryDAG` is + // immutable, this cache should never need to be invalidated. + mutable std::optional cachedMemoryLocations_; + + mutable std::optional cachedAllContainedMemoryLocations_; +}; + +// class MemoryDAG +// +// This class tracks the "A points to B" graph for all values. It is used by +// AliasDb to provide a higher-level API. +// +// We maintain a DAG where: +// - Vertices (called "Elements") represent Values and +// other aliasing entities (e.g. the stuff inside a list) +// - Edges represent a "points-to" relationship. +// +// Leaves in this DAG are entities that don't point to anything, and thus +// correspond to unique "memory locations". +// +// So, by traversing the "points-to" graph to the leaves, you can determine +// which memory locations an element may point to. +class TORCH_API MemoryDAG { + public: + explicit MemoryDAG(std::vector> indexToElementMap) + : indexToElementMap_(std::move(indexToElementMap)) {} + // explicitly delete copy constructor because otherwise windows build is + // confused for an exported class see + // https://stackoverflow.com/a/51033485/105137 + MemoryDAG(const MemoryDAG&) = delete; + MemoryDAG& operator=(const MemoryDAG&) = delete; + + // Return the unique memory locations that `Element` might represent. + const MemoryLocations& getMemoryLocations(const Element* e) const; + + // Do `a` and `b` potentially share a memory location? + bool mayAlias(const Element* a, const Element* b) const; + + // Does `a` hold reference to any memory that is stored in `b`, or vice versa? + bool mayContainAlias(const Element* a, const Element* b) const; + + bool mayContainAlias(const Element* a, const at::ArrayRef b) const; + + bool mayContainAlias( + const at::ArrayRef a, + const at::ArrayRef b) const; + + // Converts from the compressed index representation + const Element* fromIndex(unsigned x) const; + Element* fromIndex(unsigned x); + void collectAllContainedMemoryLocations( + const Element* elem, + MemoryLocations& cont) const; + + /** + * The following methods are special cases where we need to mutate the + * internals of MemoryDAG for efficiency reasons. Don't call them unless you + * know what you're doing! In particular, don't add new mutating methods + * without ensuring that you are maintaining cache consistency for memory + * locations. + */ + + // Adding wildcards can trigger extremely expensive cache invalidations. This + // method adds them in a more efficient cache-aware way. + void setWildcards( + const std::unordered_set& wildcards, + const ska::flat_hash_map& elementMap, + const std::function& getWildcardElement); + Element* unsafeMakeFreshValue(const Value* v); + + private: + const MemoryLocations& getAllContainedMemoryLocations( + const Element* elem) const; + void collectAllContainedMemoryLocationsImpl( + const Element* elem, + MemoryLocations& cont) const; + std::vector> indexToElementMap_; +}; + +/** + * Helper to build up the points-to graph. + * + * We separate the "building" into a different class because it allows us to + * cache internally to MemoryDAG without worrying about how the DAG structure + * is mutated. + */ +class TORCH_API MemoryDAGBuilder { + public: + MemoryDAGBuilder() = default; + MemoryDAGBuilder(const MemoryDAGBuilder&) = delete; + MemoryDAGBuilder& operator=(const MemoryDAGBuilder&) = delete; + + // Make `from` point at `to`. + void makePointerTo(Element* from, Element* to); + + void addToContainedElements(Element* contained, Element* container); + + std::unique_ptr createMemoryDAG() && { + return std::make_unique(std::move(indexToElementMap_)); + } + + // Make a fresh Element (i.e. an Element that doesn't point to anything) and + // return it. + Element* makeFreshValue(const Value* v); + + friend MemoryDAG; + + private: + // `MemoryDAGBuilder` builds up `indexToElementMap_`, then uses + // the map to construct the `MemoryDAG` + std::vector> indexToElementMap_; +}; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/op_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/op_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..85d9ac8c7d2872e9d373c723cc4f79a9f542b6aa --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/op_registry.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { +// Moved from shape_analysis.cpp + +// Requirements: +// dims : preserved from the first argument +// scalar type : preserved from the first argument (doesn't have to +// match other arguments) +// device : always matching and preserved +// tensor inputs : * +// tensor outputs : 1 +// NB: those ops (with slight adjustments) are good candidates for restarts. +// Knowing the type and device of weights or biases is usually enough to +// infer the output type. +std::shared_ptr nn_ops_first_input_preserving(); + +// Requirements: +// dims : Changed from first argument +// scalar type : preserved from the first argument +// device : always matching and preserved +// tensor inputs : 1 +// tensor outputs : 1 +std::shared_ptr ops_one_tensor_in_shape_transform(); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/optimization_utils.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/optimization_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..720523ede4ccfccb1d91dd52fc68c8041be89aa5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/optimization_utils.h @@ -0,0 +1,12 @@ + +#pragma once + +#include + +namespace torch::jit { + +// Checks if the parameters, not including the +// first param are all constants. +bool nonConstantParameters(Node* n); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/subgraph_utils.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/subgraph_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..fc5ba3e415ee956a6058bff3c44e187f7d4cac0c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/utils/subgraph_utils.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +// Utilities for dealing with nodes that contain subgraphs. +// +// They handle the complexity of editing inputs/outputs as you merge nodes in +// and out of subgraphs. +namespace torch::jit::SubgraphUtils { + +// Create a new subgraph node that contains only `n`. The new subgraph will have +// `subgraphKind` as its type. +// +// `n` is destroyed. +// +// Returns the new subgraph node. +TORCH_API Node* createSingletonSubgraph(Node* n, Symbol subgraphKind); + +// Creates a new subgraph that only contains `n`, amd updates the new outputs +// of the subgraph to have the aliasing properties of the original `n` outputs +TORCH_API Node* createSingletonSubgraphAndUpdateAliasing( + Node* to_merge, + Symbol subgraphKind, + AliasDb& db); + +// Merge a node into a subgraph node. If `toMerge` is also a subgraph, the +// subgraphs are merged. +// If `destroyNode` is true `toMerge` is destroyed. +// An optional argument 'vmap' could be used to retrieve value mappings. +// Values will be mapped to their new subgraph values +TORCH_API void mergeNodeIntoSubgraph( + Node* toMerge, + Node* subgraphNode, + bool destroyNode = true); + +// Merges a node into a subgraph node, and updates the new outputs of the +// subgraph to have the aliasing properties of the corresponding `to_merge` +// outputs +TORCH_API void mergeNodeIntoSubgraphAndUpdateAliasing( + Node* to_merge, + Node* subgraphNode, + AliasDb& db); + +TORCH_API std::vector unmergeAliasedOutputs( + Node* subgraphNode, + AliasDb& db); + +// Move nodes from a subgraph node to the outer graph. +// `subgraphNode` is destroyed. +TORCH_API void unmergeSubgraph(Node* subgraphNode); + +// Move `node_to_unmerge` and its descendants after `subgraphNode` +// promotes any dependencies of `node_to_unmerge` to subgraphNode outputs +TORCH_API void unmergeNode(Node* node_to_unmerge, Node* subgraphNode); + +TORCH_API bool unmergeOutputsAlisingInputs(Node* subgraphNode); + +TORCH_API bool unmergeAliasedOutputs(Node* subgraphNode); + +// Convenience function +std::shared_ptr getSubgraph(Node* n); + +TORCH_API std::string generateNameForGraph( + const std::shared_ptr& graph, + size_t maxlen = 40, + const std::string& prefix = "fused"); + +} // namespace torch::jit::SubgraphUtils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/value_refinement_utils.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/value_refinement_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..387a0af360f322644756f7b6364dc3494c76d271 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/value_refinement_utils.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +// Refine from Value of type List -> len of list +// If a refinement mapping of List Value * -> len is present in a block +// the list is guaranteed to be that length +// TODO: vector may be faster +using ListRefinement = std::unordered_map; + +TORCH_API ListRefinement +intersectRefinements(const ListRefinement& ref1, const ListRefinement& ref2); + +TORCH_API ListRefinement +unionRefinements(const ListRefinement& ref1, const ListRefinement& ref2); + +// Represents the refinement information that can be carried on a boolean +struct BooleanRefinementMapping { + BooleanRefinementMapping( + ListRefinement true_refine, + ListRefinement false_refine) + : true_refine_(std::move(true_refine)), + false_refine_(std::move(false_refine)) {} + BooleanRefinementMapping() = default; // empty + + static BooleanRefinementMapping FalseRefinements( + ListRefinement false_refine) { + return BooleanRefinementMapping({}, std::move(false_refine)); + } + + static BooleanRefinementMapping TrueRefinements(ListRefinement true_refine) { + return BooleanRefinementMapping(std::move(true_refine), {}); + } + + BooleanRefinementMapping intersectBooleanRefinementMapping( + BooleanRefinementMapping& other) { + return BooleanRefinementMapping( + intersectRefinements(true_refine_, other.true_refine()), + intersectRefinements(false_refine_, other.false_refine())); + } + + ListRefinement& true_refine() { + return true_refine_; + } + + ListRefinement& false_refine() { + return false_refine_; + } + + private: + ListRefinement true_refine_; + ListRefinement false_refine_; +}; + +TORCH_API void joinIfRefinements( + Node* if_node, + std::unordered_set& throwing_blocks, + ListRefinement& curr_block_refinements, + ListRefinement& true_block_refinements, + ListRefinement& false_block_refinements, + std::unordered_map& info); + +// handles adding blocks to throwing blocks and propagating refinements via +// boolean comparisons +TORCH_API bool handleCommonRefinentOperators( + Node* n, + std::unordered_set& throwing_blocks, + std::unordered_map& info); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/variadic_ops.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/variadic_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..afc033a57263bf6715a4a59a9192d739b3fb91cb --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/variadic_ops.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace torch::jit { + +// Try to replace an op that takes a list input with another op that takes a +// variadic number of arguments. +TORCH_API bool UseVariadicOp( + const std::shared_ptr& graph, + NodeKind op, + NodeKind variadic_op); + +TORCH_API bool RemoveListMutationAndUseVariadicOp( + const std::shared_ptr& graph, + NodeKind op, + NodeKind variadic_op); + +// Convenient functions for replacing aten::stack/aten::cat with their +// variadic versions. +TORCH_API bool UseVariadicCat(const std::shared_ptr& graph); +TORCH_API bool RemoveListMutationAndUseVariadicCat( + const std::shared_ptr& graph); + +TORCH_API bool UseVariadicStack(const std::shared_ptr& graph); +TORCH_API bool RemoveListMutationAndUseVariadicStack( + const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/vulkan_rewrite.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/vulkan_rewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..cc4e52515039c6235499717047419c1ec6ded144 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/vulkan_rewrite.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { +TORCH_API void vulkanInsertPrePackedOps(std::shared_ptr& graph); +TORCH_API void vulkanInsertPrePackedOps(script::Module& module); +TORCH_API void vulkanFusePrePackedConvWithClamp(script::Module& module); +TORCH_API void vulkanFoldPrePackingOps(script::Module& module); +TORCH_API script::Module vulkanOptimizeForMobile( + const script::Module& module, + const std::set& optimization_blocklist, + const std::vector& preserved_methods); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/xnnpack_rewrite.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/xnnpack_rewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..74ffdbaa2af79982bdb5276fdfcffce1b4a3d630 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/passes/xnnpack_rewrite.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +TORCH_API void transformConv1dToConv2d(std::shared_ptr& graph); +TORCH_API void transformConv1dToConv2d(script::Module& module); +TORCH_API void insertPrePackedOps(std::shared_ptr& graph); +TORCH_API void insertPrePackedOps(script::Module& module); +TORCH_API void fusePrePackedLinearConvWithClamp(script::Module& module); +TORCH_API void FoldPrePackingOps(script::Module& module); +TORCH_API script::Module optimizeForMobile( + const script::Module& module, + const std::set& optimization_blocklist = {}, + const std::vector& preserved_methods = {}); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/init.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/init.h new file mode 100644 index 0000000000000000000000000000000000000000..2a66cc3228470c56fa09a8ac086bfe5c3c676d09 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/init.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +void initJITBindings(PyObject* module); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/module_python.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/module_python.h new file mode 100644 index 0000000000000000000000000000000000000000..ec247e5e3a2687f1a5fb2f9db05d8423d4a80a8c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/module_python.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace py = pybind11; + +namespace torch::jit { + +inline std::optional as_module(py::handle obj) { +#if IS_PYBIND_2_13_PLUS + PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store + storage; + auto& ScriptModule = + storage + .call_once_and_store_result([]() -> py::object { + return py::module_::import("torch.jit").attr("ScriptModule"); + }) + .get_stored(); +#else + static py::handle ScriptModule = + py::module::import("torch.jit").attr("ScriptModule"); +#endif + if (py::isinstance(obj, ScriptModule)) { + return py::cast(obj.attr("_c")); + } + return std::nullopt; +} + +inline std::optional as_object(py::handle obj) { +#if IS_PYBIND_2_13_PLUS + PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store< + std::tuple> + storage; + auto& [ScriptObject, RecursiveScriptClass] = + storage + .call_once_and_store_result( + []() -> std::tuple { + return { + py::module_::import("torch").attr("ScriptObject"), + py::module_::import("torch.jit") + .attr("RecursiveScriptClass")}; + }) + .get_stored(); +#else + static py::handle ScriptObject = + py::module::import("torch").attr("ScriptObject"); + + static py::handle RecursiveScriptClass = + py::module::import("torch.jit").attr("RecursiveScriptClass"); +#endif + + if (py::isinstance(obj, ScriptObject)) { + return py::cast(obj); + } + if (py::isinstance(obj, RecursiveScriptClass)) { + return py::cast(obj.attr("_c")); + } + return std::nullopt; +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind.h new file mode 100644 index 0000000000000000000000000000000000000000..5bab3878f3b46e7d4e189daffb71ebfe5b5a5a74 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind.h @@ -0,0 +1,213 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace py = pybind11; + +namespace torch::jit { + +// This is a variant of shared_ptr that "sees through" a wrapper. +// We use it to convert Value, Node, Block and node to "wrapped" Python +// values. When we destruct the C++ object, the wrapper's pointer will +// be set to 0 and any future dereferencing will throw. We need this +// because the Python objects may hang around after the C++ object +// has already been destroyed. +// This also needs the magic type_caster below, which is from the +// workaround offered in https://github.com/pybind/pybind11/issues/2751 +template +class unwrapping_shared_ptr { + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v, + "unwrapping type only defined for Graph object types"); + + private: + std::shared_ptr> impl; + + public: + unwrapping_shared_ptr() : impl({}) {} + explicit unwrapping_shared_ptr(T* p) : impl(p->wrap()) { + impl->clear_cb = &clear_registered_instances; + } + T* get() const { + if (!impl->elem) { + throw std::logic_error("has been invalidated"); + } + return impl->elem; + } + // we need to disable the overloaded & for PyBind11 < 2.3 due. + // see https://github.com/pybind/pybind11/pull/1435 +#if (PYBIND11_VERSION_MAJOR > 2) || \ + ((PYBIND11_VERSION_MAJOR == 2) && (PYBIND11_VERSION_MINOR >= 3)) + T** operator&() { + if (!impl->elem) { + throw std::logic_error("has been invalidated"); + } + return &(impl->elem); + } +#endif +}; + +} // namespace torch::jit + +PYBIND11_DECLARE_HOLDER_TYPE(T, torch::jit::unwrapping_shared_ptr, true) + +namespace pybind11::detail { + +#define CREATE_UNWRAPPING_CASTER(Class) \ + template <> \ + struct type_caster : public type_caster_base { \ + public: \ + using type = Class; \ + using holder_type = torch::jit::unwrapping_shared_ptr; \ + \ + bool load(handle src, bool convert) { \ + return load_impl>(src, convert); \ + } \ + \ + explicit operator type*() { \ + return static_cast(value); \ + } \ + explicit operator type&() { \ + return *static_cast(value); \ + } \ + \ + protected: \ + friend class type_caster_generic; \ + \ + bool load_value(const value_and_holder& v_h) { \ + if (v_h.holder_constructed()) { \ + value = v_h.template holder().get(); \ + return true; \ + } else { \ + throw cast_error( \ + "Unable to cast from non-held to held instance (#Class& to Holder<#Class>)"); \ + } \ + } \ + } + +CREATE_UNWRAPPING_CASTER(torch::jit::Node); +CREATE_UNWRAPPING_CASTER(torch::jit::Value); +CREATE_UNWRAPPING_CASTER(torch::jit::Block); + +#undef CREATE_UNWRAPPING_CASTER + +template <> +struct type_caster { + public: + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + PYBIND11_TYPE_CASTER(torch::jit::IValue, _("IValue")); + + bool load(handle src, bool) { + try { + value = torch::jit::toTypeInferredIValue(src); + return true; + } catch (std::exception& e) { + return false; + } + } + + static handle cast( + torch::jit::IValue src, + return_value_policy /* policy */, + handle /* parent */) { + return torch::jit::toPyObject(std::move(src)).release(); + } +}; + +template <> +struct type_caster { + public: + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + PYBIND11_TYPE_CASTER(torch::jit::Symbol, _("Symbol")); + + bool load(handle src, bool) { + // TODO: Is there a way to py::cast that doesn't raise an exception on + // failure? Can we catch pybind11::cast_error here instead? + std::string src_str; + try { + src_str = py::cast(src); + } catch (std::exception& e) { + return false; + } + value = torch::jit::Symbol::fromQualString(src_str); + return true; + } + + static handle cast( + torch::jit::Symbol src, + return_value_policy /* policy */, + handle /* parent */) { + return py::cast(std::string(src.toQualString()), return_value_policy::copy) + .release(); + } +}; + +template <> +struct type_caster { + public: + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + PYBIND11_TYPE_CASTER(torch::jit::AttributeKind, _("AttributeKind")); + + bool load(handle src, bool) { + return false; + } + + static handle cast( + torch::jit::AttributeKind src, + return_value_policy /* policy */, + handle /* parent */) { + return py::cast( + std::string(torch::jit::toString(src)), + return_value_policy::copy) + .release(); + } +}; + +// See https://github.com/pybind/pybind11/issues/637 +using ListCasterBase = pybind11::detail:: + list_caster, torch::jit::Node*>; +template <> +struct type_caster> : ListCasterBase { + static handle cast( + const std::vector& src, + return_value_policy, + handle parent) { + return ListCasterBase::cast(src, return_value_policy::reference, parent); + } + static handle cast( + const std::vector* src, + return_value_policy pol, + handle parent) { + return cast(*src, pol, parent); + } +}; + +} // namespace pybind11::detail + +namespace torch::jit { + +static inline py::tuple tuple_tail(const py::tuple& tup) { + py::tuple r(tup.size() - 1); + for (const auto i : c10::irange(1, tup.size())) { + r[i - 1] = tup[i]; + } + return r; +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind_utils.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..754497abf130bc1c6b5984f38a4ad469fa619b0b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/pybind_utils.h @@ -0,0 +1,1292 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_DISTRIBUTED +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// The visibility attribute is to avoid a warning about storing a field in the +// struct that has a different visibility (from pybind) than the struct. +#ifdef _WIN32 +#define VISIBILITY_HIDDEN +#else +#define VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#endif + +namespace torch::jit { + +using ResolutionCallback = std::function; + +void clear_registered_instances(void* ptr); + +TORCH_PYTHON_API IValue toIValue( + py::handle obj, + const TypePtr& type, + std::optional N = std::nullopt); + +TORCH_PYTHON_API py::object toPyObject(IValue ivalue); + +// Hack to overload the behavior of toIValue to accept Python +// numbers in places where a Tensor is expected +// See also torch::should_allow_numbers_as_tensors +class ToIValueAllowNumbersAsTensors { + bool old_; + + public: + ToIValueAllowNumbersAsTensors(bool enable); + ~ToIValueAllowNumbersAsTensors(); +}; + +// Wrap Python function to guard deref +// NB: Need VISIBILITY_HIDDEN for silencing compiler error, +// 'torch::jit::PythonFunctionGuard' declared with greater visibility than the +// type of its field 'torch::jit::PythonFunctionGuard::func_' +struct VISIBILITY_HIDDEN PythonFunctionGuard { + explicit PythonFunctionGuard(py::function func) : func_(std::move(func)) {} + + ~PythonFunctionGuard() { + pybind11::gil_scoped_acquire ag; + func_.dec_ref(); + // explicitly setting PyObject* to nullptr to prevent py::object's dtor to + // decref on the PyObject again. + // See Note [Destructing py::object] in python_ivalue.h + func_.ptr() = nullptr; + } + + py::function func_; +}; + +// The PythonFutureWrapper for ivalue::Future +// +// NB: VISIBILITY_HIDDEN is for silencing compiling error, +// "error: 'torch::jit::PythonFutureWrapper' declared with greater visibility +// than the type of its field 'torch::jit::PythonFutureWrapper::unwrap_func' +// [-Werror=attributes]" +// +// NB: inherit from enable_shared_from_this because then(py::function) needs to +// get a shared_ptr from this pointer. +struct VISIBILITY_HIDDEN PythonFutureWrapper + : std::enable_shared_from_this { + using UnwrapFunc = std::function; + + explicit PythonFutureWrapper( + c10::intrusive_ptr fut, + std::optional unwrap_func = std::nullopt) + : fut(std::move(fut)), unwrap_func(std::move(unwrap_func)) {} + + explicit PythonFutureWrapper(const PythonFutureWrapper&) = delete; + PythonFutureWrapper& operator=(const PythonFutureWrapper&) = delete; + + bool done() { + return fut->completed(); + } + + py::object value() { + // acquiring GIL as toPyObject creates new py::object + // without grabbing the GIL. + py::gil_scoped_acquire acquire; + py::object py_obj = toPyObject(fut->value()); + // unwrap_func is a general compositional function that takes in a + // py::object and executes some python function. It is currently mostly used + // to throw python exceptions. + if (unwrap_func) { + (*unwrap_func)(py_obj); + } + return py_obj; + } + + py::object wait() { + fut->wait(); + if (jit::tracer::isTracing()) { + auto graph = jit::tracer::getTracingState()->graph; + + Value* fut_val = jit::tracer::getValueTrace(fut); + auto output = graph->insert(aten::wait, {fut_val}); + jit::tracer::setValueTrace(fut->value(), output); + } + return value(); + } + + // The py::function cb arg must take a std::shared_ptr + // (i.e., torch._C.Future) as the only argument. If the type mismatches, an + // error will be thrown when waiting for the value of this returned Future. + std::shared_ptr then(py::function cb) { + // We need this an additional layer of wrapper here to guard the + // destruction of the py::function object. Because, the + // Future owns a reference to the py::function in its callback + // vector, but Future does not acquire GIL on destruction. + auto pf = std::make_shared(std::move(cb)); + + return std::make_shared(fut->then( + // Capture a copy of the ivalue::Future instead of the `this` pointer + // because the PythonFutureWrapper object could have been deleted + // when the callbacks are fired. For example, RPC only captures the + // ivalue::Future instead of PythonFutureWrapper in JitFuture's + // callback functions. Hence, if user code does not hold a reference to + // this PythonFutureWrapper object, there is no guarantee that the + // PythonFutureWrapper is still valid when running the callback. + [pyFut(this->getPtr()), + pf(std::move(pf))](c10::ivalue::Future& /* unused */) -> IValue { + try { + pybind11::gil_scoped_acquire ag; + return toIValue(pf->func_(pyFut), PyObjectType::get()); + } catch (py::error_already_set& e) { + auto err = std::runtime_error(c10::str( + "Got the following error when running the callback: ", + e.what())); + { + pybind11::gil_scoped_acquire ag; + // Release ownership on py::objects and also restore Python + // Error Indicator. + e.restore(); + // Clear the Python Error Indicator as we has recorded the + // exception in the response message. + PyErr_Clear(); + } + + throw std::runtime_error(err); + } + }, + PyObjectType::get())); + } + + void add_done_callback(py::function cb) { + auto pf = std::make_shared(std::move(cb)); + // NOLINTNEXTLINE(modernize-avoid-bind) + fut->addCallback(std::bind( + [pyFut(this->getPtr())]( + const std::shared_ptr& pf) { + try { + pybind11::gil_scoped_acquire ag; + pf->func_(pyFut); + } catch (py::error_already_set& e) { + { + pybind11::gil_scoped_acquire ag; + // Release ownership on py::objects and also restore Python + // Error Indicator. + e.restore(); + // Clear the Python Error Indicator as we has recorded the + // exception in the response message. + PyErr_Clear(); + } + // Log and ignore exceptions raised through the callback + LOG(ERROR) << "Got the following error when running the callback: " + << e.what(); + + } catch (const std::exception& e) { + // Log and ignore exceptions raised through the callback + LOG(ERROR) << "Got the following error when running the callback: " + << e.what(); + } + }, + std::move(pf))); + } + + void markCompleted(const py::object& pyValue) { + DCHECK(PyGILState_Check()); + IValue value = toIValue(pyValue, PyObjectType::get()); + + py::gil_scoped_release release; + fut->markCompleted(std::move(value)); + } + + c10::intrusive_ptr fut; + // unwrap_func works like a callback for the value returned by + // PythonFutureWrapper::wait(). + std::optional unwrap_func; + + private: + std::shared_ptr getPtr() { + return shared_from_this(); + } +}; + +// The PythonAwaitWrapper for ivalue::Await +// +// Expresses delayed function execution with Lazy semantic. +// i.e. Await[W] in eager mode can be used as W. +// When the attribute of W type is requested, Await[W] will return the +// attribute of W, transparently calling wait() beforehand. +// No Lazy semantic for script, explicit wait(Await[W]) -> W must be called to +// convert to type W. +// +// The Await object takes shared ownership of specified function and the +// arguments. After first call for wait() it owns the result. Deliberately no +// type inference for eager mode. +struct VISIBILITY_HIDDEN PythonAwaitWrapper + : std::enable_shared_from_this { + explicit PythonAwaitWrapper(c10::intrusive_ptr aw) + : aw_(std::move(aw)) {} + explicit PythonAwaitWrapper(py::handle input) { + args_ = py::tuple(1u); + args_[0] = input; + auto type = PyObjectType::get(); + aw_ = c10::make_intrusive(type); + aw_->markCompleted(toIValue(input, type)); + } + + explicit PythonAwaitWrapper(py::function pf, py::tuple args) + : args_(std::move(args)) { + pyfg_ = std::make_shared(std::move(pf)); + + std::function f = [fg(pyfg_), &args(args_)]() { + pybind11::gil_scoped_acquire ag; + return toIValue(fg->func_(*args), PyObjectType::get()); + }; + aw_ = c10::make_intrusive( + PyObjectType::get(), std::move(f)); + } + + explicit PythonAwaitWrapper(const PythonAwaitWrapper&) = delete; + PythonAwaitWrapper& operator=(const PythonAwaitWrapper&) = delete; + + py::object wait() { + py::gil_scoped_acquire acquire; + return toPyObject(aw_->wait()); + } + + // Nowait semantic means trivial case when Await is constructed from the + // result + bool is_nowait() { + return pyfg_ == nullptr; + } + + const py::function fn() { + TORCH_CHECK( + pyfg_, "Await constructed as awaitable_nowait does not have fn"); + return pyfg_->func_; + } + + const py::tuple args() { + return args_; + } + + TypePtr type() { + return aw_->type(); + } + + c10::intrusive_ptr aw_; + std::shared_ptr pyfg_; + py::tuple args_; + + private: + std::shared_ptr getPtr() { + return shared_from_this(); + } +}; + +// error reporting: when reporting user-caused errors, these functions should +// not use AT_ERROR macros, since these macros add stack trace information +// that is confusing to display to the end user since it always reports +// locations in libtorch code rather than user code. + +inline std::shared_ptr get_python_cu() { + return py::module::import("torch.jit._state") + .attr("_python_cu") + .cast>(); +} + +struct TypedIValue : public std::pair { + using pair::pair; + + IValue& ivalue() { + return this->first; + } + TypePtr& type() { + return this->second; + } +}; + +inline TypedIValue toDictKeyIValue(py::handle key) { + if (py::isinstance(key)) { + return TypedIValue( + ConstantString::create(py::cast(key)), StringType::get()); + } else if (py::isinstance(key)) { + return TypedIValue(py::cast(key), IntType::get()); + } else if (py::isinstance(key)) { + return TypedIValue(py::cast(key), FloatType::get()); + } else { + TORCH_CHECK( + false, "Dictionary inputs may only have string, int, or float keys"); + } +} + +inline std::optional unifyOrInitializeType( + const TypePtr& accum, + const TypePtr& unify) { + if (!accum) { + return unify; + } + return unifyTypes(accum, unify); +} + +using InferredType = c10::InferredType; + +InferredType tryToInferContainerType(py::handle input, bool primitiveTypeOnly); + +// Try to infer the type of a Python object +// The type cannot be inferred if: +// input is an empty container (list, dict) +// input is an list with element types that cannot be unified +// input is an dict with key or value types that cannot be unified +inline InferredType tryToInferType(py::handle input) { + // Try tensor types + if (THPVariable_Check(input.ptr())) { + return InferredType(TensorType::get()); + } + + if (input.is_none()) { + return InferredType(NoneType::get()); + } + + if (py::isinstance(input)) { + auto fn = py::cast(input).function_; + return InferredType(FunctionType::create(fn)); + } + + // Try basic types first + if (py::isinstance(input)) { + return InferredType(BoolType::get()); + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if (py::isinstance(input)) { + return InferredType(IntType::get()); + } else if (py::isinstance(input)) { + return InferredType(FloatType::get()); + } else if (PyComplex_CheckExact(input.ptr())) { + return InferredType(ComplexType::get()); + } else if (py::isinstance(input)) { + // NOTE: We may need a ByteType in the future + return InferredType(StringType::get()); + } else if (py::isinstance(input)) { + return InferredType(StringType::get()); + } else if (THPLayout_Check(input.ptr())) { + return InferredType(IntType::get()); + } else if (THPDevice_Check(input.ptr())) { + return InferredType(DeviceObjType::get()); + } else if (THPGenerator_Check(input.ptr())) { + return InferredType(GeneratorType::get()); + } else if (THPStream_Check(input.ptr())) { + return InferredType(StreamObjType::get()); + } else if (THPDtype_Check(input.ptr())) { + return InferredType(IntType::get()); + } else if (THPQScheme_Check(input.ptr())) { + return InferredType(IntType::get()); + } else if (THPLayout_Check(input.ptr())) { + return InferredType(IntType::get()); + } + + auto enum_type = py::module::import("enum").attr("Enum"); + py::bool_ isEnumValue = py::isinstance(input, enum_type); + if (py::cast(isEnumValue)) { + auto enum_class = input.attr("__class__"); + auto enum_type = py::cast( + py::module::import("torch.jit.annotations") + .attr("try_ann_to_type")(enum_class, SourceRange())); + return InferredType(std::move(enum_type)); + } + + py::bool_ isClass = + py::module::import("inspect").attr("isclass")(input.get_type()); + if (py::cast(isClass)) { + // Assume that the class is compiled already or will compile. Invalidate + // this later if needed. + bool class_compiled = true; + + // Check if the type is already compiled. + py::object existing_ty = py::module::import("torch.jit._state") + .attr("_get_script_class")(input.get_type()); + + if (existing_ty.is_none()) { + // If not, try to compile it. + py::bool_ can_compile = py::module::import("torch._jit_internal") + .attr("can_compile_class")(input.get_type()); + + if (py::cast(can_compile)) { + // Try to compile the class. This is wrapped in a try-catch because + // compilation of class types can raise an Exception and in that case, + // we want to defer to other attempts at type inference below rather + // than fail compilation altogether. + try { + py::module::import("torch.jit._script") + .attr("_recursive_compile_class")( + input.get_type(), SourceRange()); + } catch (...) { + // Invalidate the assumption that the class compiled so that we don't + // look up and return its JIT type as the type for the input. + class_compiled = false; + } + } + } + + // If the class compiled successfully, look up the existing JIT type by + // qualified name and return it. + if (class_compiled) { + auto script_class = py::module::import("torch.jit._state") + .attr("_get_script_class")(input.get_type()); + + if (!script_class.is_none()) { + auto class_type = py::cast(script_class); + + if (class_type && !class_type->is_module()) { + return InferredType(std::move(class_type)); + } + } + } + } + + if (py::isinstance(input)) { + auto object = py::cast(input); + return InferredType(object.type()); +#ifdef USE_RPC + } else if (py::isinstance(input)) { + auto rref_ivalue = input.cast().toIValue(); + return InferredType(rref_ivalue.type()); +#endif + } + + auto await_type = py::module::import("torch._awaits").attr("_Await"); + py::bool_ is_await = py::isinstance(input, await_type); + if (py::cast(is_await)) { + auto awptr = input.cast>(); + return InferredType(AwaitType::create(awptr->aw_->elementType())); + } + + if (as_module(py::cast(input))) { + return InferredType("Cannot infer type of ScriptModule"); + } + + auto module_type = py::module::import("torch.nn").attr("Module"); + py::bool_ is_module = py::isinstance(input, module_type); + if (py::cast(is_module)) { + return InferredType("Cannot infer concrete type of torch.nn.Module"); + } + + // Try container types + return tryToInferContainerType(input, false); +} + +// This function is similar to tryToInferType, but it only tries to infer +// primitive types (int, float, bool, complex) or nested container of primitive +// types. +inline InferredType tryToInferPrimitiveType(py::handle input) { + if (input.is_none()) { + return InferredType(NoneType::get()); + } + + // Only primitive data type + if (py::isinstance(input)) { + return InferredType(BoolType::get()); + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if (py::isinstance(input)) { + return InferredType(IntType::get()); + } else if (py::isinstance(input)) { + return InferredType(FloatType::get()); + } else if (PyComplex_CheckExact(input.ptr())) { + return InferredType(ComplexType::get()); + } + + // Try container types + return tryToInferContainerType(input, true); +} + +inline InferredType tryToInferContainerType( + py::handle input, + bool primitiveTypeOnly = false) { + if (six::isTuple(input)) { + py::tuple tuple = py::cast(input); + std::vector element_types; + element_types.reserve(tuple.size()); + + for (py::handle elem : tuple) { + auto type_match = primitiveTypeOnly ? tryToInferPrimitiveType(elem) + : tryToInferType(elem); + if (type_match.success()) { + element_types.push_back(type_match.type()); + } else { + // Forward error message along + return type_match.reason(); + } + } + return InferredType(TupleType::create(std::move(element_types))); + } else if (PyDict_Check(input.ptr())) { + // Check to make sure we can generate useful input/output types + auto dict = py::cast(input); + size_t len = py::len(dict); + if (!len) { + return InferredType("Dictionary inputs must have entries"); + } + + TypePtr key_type = nullptr; + TypePtr value_type = nullptr; + + for (auto entry : dict) { + // Try to infer the key type and unify it with the existing one + auto entry_key_type_match = primitiveTypeOnly + ? tryToInferPrimitiveType(entry.first) + : tryToInferType(entry.first); + if (!entry_key_type_match.success()) { + return entry_key_type_match.reason(); + } + auto unified_key = + unifyOrInitializeType(key_type, entry_key_type_match.type()); + if (!unified_key) { + return InferredType(c10::str( + "Dictionary inputs to traced functions must have consistent type. Found ", + key_type->repr_str(), + " and ", + (entry_key_type_match.type())->repr_str())); + } + + // Try to infer the value type and unify it with the existing one + auto entry_value_type_match = primitiveTypeOnly + ? tryToInferPrimitiveType(entry.second) + : tryToInferType(entry.second); + if (!entry_value_type_match.success()) { + return entry_value_type_match.reason(); + } + auto unified_value = + unifyOrInitializeType(value_type, entry_value_type_match.type()); + if (!unified_value) { + return InferredType(c10::str( + "Dictionary inputs to traced functions must have consistent type. Found ", + value_type->repr_str(), + " and ", + (entry_value_type_match.type())->repr_str())); + } + + key_type = *unified_key; + value_type = *unified_value; + } + return InferredType( + DictType::create(std::move(key_type), std::move(value_type))); + } else if (PyList_Check(input.ptr())) { + auto list = py::cast(input); + size_t len = py::len(list); + if (!len) { + return InferredType("List trace inputs must have elements"); + } + + TypePtr element_type = nullptr; + for (auto elem : list) { + auto element_type_match = primitiveTypeOnly + ? tryToInferPrimitiveType(elem) + : tryToInferType(elem); + if (!element_type_match.success()) { + return InferredType(c10::str( + "Could not infer type of list element: ", + element_type_match.reason())); + } + auto unified_type = + unifyOrInitializeType(element_type, element_type_match.type()); + if (!unified_type) { + return InferredType(c10::str( + "List inputs to traced functions must have consistent element type. Found ", + element_type->repr_str(), + " and ", + (element_type_match.type())->repr_str())); + } + element_type = *unified_type; + } + return InferredType(ListType::create(element_type)); + } else { + if (primitiveTypeOnly) { + return InferredType(c10::str( + "Only tuple, list, or dict (possibly nested) of primitive types (bool, float, int, complex)", + "are supported ", + "as inputs or outputs of traced functions", + ", but instead got value of type ", + py::str(input.get_type().attr("__name__")), + ".")); + } else { + // TODO: this message is not correct anymore, since this InferredType is + // used from a bunch of circumstances unrelated to tracing. We can re-use + // this instead of the attribute_failure stuff in concreteType + return InferredType(c10::str( + "Only tensors and (possibly nested) tuples of tensors, lists, or dicts ", + "are supported ", + "as inputs or outputs of traced functions", + ", but instead got value of type ", + py::str(input.get_type().attr("__name__")), + ".")); + } + } +} + +inline bool isTraceableType(const TypePtr& type) { + if (type->isSubtypeOf(*TensorType::get())) { + return true; + } + + if (auto list_type = type->cast()) { + return isTraceableType(list_type->getElementType()); + } + + if (auto tuple_type = type->cast()) { + return std::all_of( + tuple_type->elements().begin(), + tuple_type->elements().end(), + [](const TypePtr& element_type) { + return isTraceableType(element_type); + }); + } + + if (auto dict_type = type->cast()) { + return isTraceableType(dict_type->getValueType()); + } + + return false; +} + +inline IValue toTypeInferredIValue(py::handle input) { + auto match = tryToInferType(input); + if (!match.success()) { + auto object = py::cast(input); + if (auto mod = as_module(object)) { + // if obj is already a ScriptModule, just return its ivalue + auto ptr = mod.value()._ivalue(); + // explict copy semantics for strong ownership of the resource. + return c10::intrusive_ptr::reclaim_copy( + ptr.release()); + } + + // Check if the obj is a ScriptObject. + if (auto script_obj = as_object(object)) { + auto ptr = script_obj.value()._ivalue(); + return c10::intrusive_ptr::reclaim_copy( + ptr.release()); + } + TORCH_CHECK( + false, + "Tracer cannot infer type of ", + py::str(input), + "\n:", + match.reason()); + } + return toIValue(input, match.type()); +} + +inline Stack toTraceableStack(const py::tuple& inputs) { + auto info = toTypeInferredIValue(inputs); + TORCH_CHECK( + isTraceableType(info.type()), + "Type '", + info.type()->repr_str(), + "' cannot be traced. Only Tensors and (possibly nested) Lists, Dicts, and" + " Tuples of Tensors can be traced"); + return info.toTupleRef().elements().vec(); +} + +// Serialize the python dictionary into a traceable stack. +inline Stack toTraceableStack(const py::dict& inputs) { + Stack res; + for (auto it = inputs.begin(); it != inputs.end(); it++) { + if (THPVariable_Check(it->second.ptr())) { + res.push_back(toIValue(it->second, tryToInferType(it->second).type())); + } + } + return res; +} + +inline IValue createGenericList(py::handle obj, const TypePtr& elem_type) { + auto elems = c10::impl::GenericList(elem_type); + for (auto elem : obj) { + elems.push_back(toIValue(elem, elem_type)); + } + return IValue(elems); +} + +inline IValue createGenericDict( + const py::dict& obj, + const TypePtr& key_type, + const TypePtr& value_type) { + c10::impl::GenericDict elems(key_type, value_type); + elems.reserve(py::len(obj)); + for (auto& entry : obj) { + elems.insert( + toIValue(entry.first, key_type), toIValue(entry.second, value_type)); + } + return IValue(elems); +} + +template +inline void guardAgainstNamedTensor(const T& var) { + TORCH_CHECK( + !var.has_names(), + "NYI: Named tensors are currently unsupported in TorchScript. As a " + "workaround please drop names via `tensor = tensor.rename(None)`."); +} + +// Extract custom class registered with torchbind +template +c10::intrusive_ptr toCustomClass(py::handle obj) { + static_assert( + std::is_base_of_v, "T is not a CustomClass"); + const auto& type = c10::getCustomClassType>(); + c10::IValue ivalue = toIValue(obj, type); + return std::move(ivalue).toCustomClass(); +} + +// Small wrapper around getting the type name string from Python to make +// types easier to interpret, e.g. give the structural type for a NamedTuple +inline std::string friendlyTypeName(py::handle obj) { + if (py::isinstance(obj) && py::hasattr(obj, "_fields")) { + auto field_names = + py::cast>(py::getattr(obj, "_fields")); + std::stringstream ss; + ss << py::str(obj.get_type().attr("__name__")); + ss << " (aka NamedTuple("; + bool first = true; + for (auto& field_name : field_names) { + if (!first) { + ss << ", "; + } + ss << field_name; + first = false; + } + ss << "))"; + return ss.str(); + } else { + return py::str(obj.get_type().attr("__name__")); + } +} + +// Thrown when trying to create a schema for a list of python +// arguments that cannot be converted. +// Can be caught by the caller to attempt to use other schema +// when there is an overloaded operator. +struct schema_match_error : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +inline IValue argumentToIValue( + const FunctionSchema& schema, + size_t argumentPosition, + py::handle object) { + const auto& argument = schema.arguments().at(argumentPosition); + try { + return toIValue(object, argument.real_type(), argument.N()); + } catch (const py::cast_error& error) { + throw schema_match_error(c10::str( + schema.formatTypeMismatchMsg( + argument, + friendlyTypeName(object), + argumentPosition, + py::repr(object)), + "\nCast error details: ", + error.what())); + } catch (const py::error_already_set& error) { + throw schema_match_error(c10::str( + schema.formatTypeMismatchMsg( + argument, + friendlyTypeName(object), + argumentPosition, + py::repr(object)), + "\n Python error details: ", + error.what())); + } +} + +inline IValue returnToIValue(const TypePtr& type, py::handle object) { + try { + return toIValue(object, type); + } catch (const py::cast_error& error) { + throw std::runtime_error(c10::str( + " expected value of type ", + type->str(), + " for return value but instead got value of type ", + py::str(object.get_type().attr("__name__")), + ".", + "\nValue: ", + py::repr(object), + "\nCast error details: ", + error.what())); + } +} + +inline py::object getScriptedClassOrError(const c10::NamedTypePtr& classType) { + auto py_class = + py::module::import("torch.jit._state") + .attr("_get_python_class")(classType->name()->qualifiedName()); + if (py_class.is_none()) { + std::stringstream err; + err << "Unknown reference to ScriptClass "; + err << classType->name()->qualifiedName(); + err << ". (Did you forget to import it?)"; + throw std::runtime_error(err.str()); + } + return py_class; +} + +struct VISIBILITY_HIDDEN tuple_slice { + /*implicit*/ tuple_slice(py::tuple tup_) + : tup(std::move(tup_)), b(0), e(tup.size()) {} + tuple_slice(py::tuple tup_, int64_t b_) + : tup(std::move(tup_)), b(b_), e(tup.size()) {} + tuple_slice(py::tuple tup_, int64_t b_, int64_t e_) + : tup(std::move(tup_)), b(b_), e(e_) {} + py::detail::tuple_iterator begin() const { + return {tup, static_cast(b)}; + } + py::detail::tuple_iterator end() const { + return {tup, static_cast(e)}; + } + size_t size() const { + return e - b; + } + py::detail::tuple_accessor operator[](size_t index) const { + return {tup, static_cast(b + index)}; + } + + private: + py::tuple tup; + int64_t b; + int64_t e; +}; + +inline bool validateFakeScriptObjectSchema( + const c10::FunctionSchema& schema, + size_t argumentPosition, + py::handle object) { + auto argument = schema.arguments().at(argumentPosition); + auto class_type = argument.real_type()->expect(); + auto fake_class_registry = + py::module::import("torch._library.fake_class_registry"); + auto fake_class = fake_class_registry.attr("find_fake_class")( + class_type->name().value().qualifiedName()); + if (!py::isinstance(object.attr("wrapped_obj"), fake_class)) { + throw schema_match_error(c10::str( + schema.formatTypeMismatchMsg( + argument, + friendlyTypeName(object), + argumentPosition, + py::repr(object.attr("wrapped_obj"))), + "\nCast error details: ", + argument.name(), + " is expected to be a FakeScriptObject of ", + class_type->name().value().qualifiedName())); + } + return true; +} + +inline bool matchSchemaAllowFakeScriptObject( + const FunctionSchema& schema, + const tuple_slice& args, + const py::kwargs& kwargs) { + size_t all_arguments = args.size() + kwargs.size(); + if (all_arguments > schema.arguments().size()) { + throw schema_match_error(c10::str( + schema.name(), + "() expected at most ", + schema.arguments().size(), + " argument(s) but received ", + all_arguments, + " argument(s). Declaration: ", + schema)); + } + + int64_t arg_idx = 0; + auto fake_class_registry = + py::module::import("torch._library.fake_class_registry"); + + // First push all positional args. + for (const auto& arg : args) { + // ...but refuse to do it if the schema says that this was supposed + // to be keyword only + if (schema.arguments()[arg_idx].kwarg_only()) { + throw schema_match_error(c10::str( + schema.name(), + "() takes ", + arg_idx, + " positional argument(s) but ", + args.size(), + " was/were given. Declaration: ", + schema)); + } + // Use the type information from the schema to convert the PyObject. + const auto& argument = schema.arguments().at(arg_idx); + if (argument.real_type()->kind() == TypeKind::ClassType && + py::isinstance(arg, fake_class_registry.attr("FakeScriptObject"))) { + validateFakeScriptObjectSchema(schema, arg_idx, arg); + } else { + argumentToIValue(schema, arg_idx, arg); + } + + arg_idx++; + } + + // Now for every remaining non-positional argument in the schema, look for it + // in the kwargs dict and push it if found, or use its default value if it + // has one. + size_t consumed_kwargs = 0; + for (size_t i = arg_idx; i < schema.arguments().size(); ++i) { + const auto& arg = schema.arguments()[i]; + if (kwargs.contains(arg.name().c_str())) { + auto cur_kwarg = kwargs[arg.name().c_str()]; + if (arg.real_type()->kind() == TypeKind::ClassType && + py::isinstance( + cur_kwarg, fake_class_registry.attr("FakeScriptObject"))) { + validateFakeScriptObjectSchema(schema, i, cur_kwarg); + } else { + argumentToIValue(schema, i, cur_kwarg); + } + consumed_kwargs += 1; + } else if (arg.default_value()) { + continue; + } else { + throw schema_match_error(c10::str( + schema.name(), + "() is missing value for argument '", + arg.name(), + "'. Declaration: ", + schema)); + } + } + + if (consumed_kwargs != kwargs.size()) { + std::vector names; + for (const auto& kwarg : kwargs) { + names.emplace_back(py::cast(kwarg.first)); + } + throw schema_match_error(schema.findErrorInKwargs(names)); + } + + return true; +} + +inline Stack createStackForSchema( + const FunctionSchema& schema, + const tuple_slice& args, + const py::kwargs& kwargs, + std::optional self) { + size_t all_arguments = (self ? 1 : 0) + args.size() + kwargs.size(); + if (all_arguments > schema.arguments().size()) { + throw schema_match_error(c10::str( + schema.name(), + "() expected at most ", + schema.arguments().size(), + " argument(s) but received ", + all_arguments, + " argument(s). Declaration: ", + schema)); + } + Stack stack; + stack.reserve(schema.arguments().size()); + + int64_t arg_idx = 0; + if (self) { + push(stack, std::move(*self)); + arg_idx++; + } + // First push all positional args. + for (const auto& arg : args) { + // ...but refuse to do it if the schema says that this was supposed + // to be keyword only + if (schema.arguments()[arg_idx].kwarg_only()) { + throw schema_match_error(c10::str( + schema.name(), + "() takes ", + arg_idx, + " positional argument(s) but ", + self ? 1 + args.size() : args.size(), + " was/were given. Declaration: ", + schema)); + } + // Use the type information from the schema to convert the PyObject. + push(stack, argumentToIValue(schema, stack.size(), arg)); + arg_idx++; + } + + // Now for every remaining non-positional argument in the schema, look for it + // in the kwargs dict and push it if found, or use its default value if it + // has one. + size_t consumed_kwargs = 0; + for (size_t i = stack.size(); i < schema.arguments().size(); ++i) { + const auto& arg = schema.arguments()[i]; + if (kwargs.contains(arg.name().c_str())) { + push(stack, argumentToIValue(schema, i, kwargs[arg.name().c_str()])); + consumed_kwargs += 1; + } else if (arg.default_value()) { + push(stack, *arg.default_value()); + } else { + throw schema_match_error(c10::str( + schema.name(), + "() is missing value for argument '", + arg.name(), + "'. Declaration: ", + schema)); + } + } + + if (consumed_kwargs != kwargs.size()) { + std::vector names; + for (const auto& kwarg : kwargs) { + names.emplace_back(py::cast(kwarg.first)); + } + throw schema_match_error(schema.findErrorInKwargs(names)); + } + + return stack; +} + +inline py::object createPyObjectForStack(Stack&& stack) { + if (stack.empty()) { + return py::none(); + } + + // Return a simple value and not a single-element tuple if there is only one + // return value. + if (stack.size() == 1) { + return toPyObject(std::move(stack[0])); + } + + // If there is more than one return value, pop them into a py::tuple. + py::tuple return_values(stack.size()); + for (const auto ret : c10::irange(return_values.size())) { + return_values[ret] = toPyObject(std::move(stack[ret])); + } + +#if defined(__clang__) + return std::move(return_values); +#else + return return_values; +#endif +} + +// TODO: Remove once we clean up the GraphExecutor usage. +inline Stack evilDeprecatedBadCreateStackDoNotUse( + const py::tuple& tuple, + at::ArrayRef inputs, + size_t reserve_extra_space = 0) { + if (tuple.size() != inputs.size()) { + TORCH_CHECK( + false, + "expected " + std::to_string(inputs.size()) + " inputs, but got " + + std::to_string(tuple.size())); + } + Stack result; + result.reserve(tuple.size() + reserve_extra_space); + for (const auto i : c10::irange(inputs.size())) { + result.push_back(toIValue(std::move(tuple[i]), inputs[i]->type())); + } + return result; +} + +// Run `callee`, potentially inserting a CallFunction/CallMethod node into the +// tracing graph. +inline py::object runAndInsertCall( + Function& callee, + const tuple_slice& args, + const py::kwargs& kwargs, + std::optional self, + // Lambda that tells this function how to insert `callee` into the graph if + // we're tracing. + const std::function& + callInserter) { + auto stack = + createStackForSchema(callee.getSchema(), args, kwargs, std::move(self)); + const auto& tracing_state = tracer::getTracingState(); + if (!tracing_state) { + pybind11::gil_scoped_release no_gil_guard; + // If we're not tracing, just run the callee as normal. + callee.run(stack); + } else { + // If we are tracing, insert the appropriate CallFunction or CallMethod node + // and then run the callee with tracing disabled. + + // Get the graph `Value`s that represent the input IValues + auto inputs = last(stack, callee.num_inputs()); + auto input_values = + fmap(inputs, [](const IValue& v) { return tracer::getValueTrace(v); }); + TORCH_INTERNAL_ASSERT(callee.getSchema().returns().size() == 1) + auto return_type = callee.getSchema().returns().at(0).type(); + auto graph = tracing_state->graph; + std::vector named_values; + named_values.reserve(input_values.size()); + for (Value* v : input_values) { + named_values.emplace_back(v); + } + + // Add a call node. + MatchedSchema match = matchSchema( + callee.getSchema(), + tracer::getPythonInterpreterSourceRange(), + *graph, + named_values, + {}); + auto output_value = callInserter(*graph, match); + + // Actually run the callee. Pause the tracer so that we don't double-add the + // callee nodes. + { + pybind11::gil_scoped_release no_gil_guard; + ResourceGuard guard(tracer::pauseTracing()); + callee.run(stack); + } + + // Associate the output IValues with the output `Value`s in the graph + tracer::setValueTrace(stack.back(), output_value); + } + + TORCH_CHECK( + !stack.empty(), + "Expected values in the stack after execution but found none"); + return toPyObject(std::move(stack.back())); +} + +inline std::optional maybeTorchFunctionDispatch( + const py::object& callee, + const tuple_slice& args_no_self, + const py::kwargs& kwargs, + const c10::QualifiedName& qualname) { + std::vector args_vec; + for (const auto& arg : args_no_self) { + args_vec.push_back(arg); + } + py::tuple args = py::cast(args_vec); + + // Handle __torch_function__ dispatch + std::vector overloaded_args; + size_t total_arg_num = args.size() + kwargs.size(); + for (const auto& arg : args) { + is_tensor_and_append_overloaded(arg.ptr(), &overloaded_args); + is_tensor_list_and_append_overloaded( + arg.ptr(), + &overloaded_args, + static_cast(total_arg_num), + false /* throw_error */); + } + // NB: for kwargs, we cannot guarantee the order of appending + // is the same as the argument order in operator's schema. + // This is suboptimal, but should be fine. Later when we have + // better schema matching and argument parsing, we could + // match the operator in `operations` first, then the order will + // be guaranteed. + for (auto item : kwargs) { + is_tensor_and_append_overloaded(item.second.ptr(), &overloaded_args); + is_tensor_list_and_append_overloaded( + item.second.ptr(), + &overloaded_args, + total_arg_num, + false /* throw_error */); + } + if (!overloaded_args.empty()) { + return pybind11::reinterpret_steal( + handle_torch_function_no_python_arg_parser( + /*overloaded_args=*/overloaded_args, + /*args=*/args.ptr(), + /*kwargs=*/kwargs.ptr(), + /*func_name=*/qualname.name().c_str(), + /*torch_api_function=*/callee.ptr(), + /*module_name=*/qualname.prefix().c_str())); + } + + return std::nullopt; +} + +inline py::object invokeScriptFunctionFromPython( + Function& callee, + const tuple_slice& args, + const py::kwargs& kwargs) { + // TODO: we could add __torch_function__ dispatch here but I don't know + // the implications of doing so + + return runAndInsertCall( + callee, + args, + kwargs, + /*self=*/std::nullopt, + [&](Graph& graph, const MatchedSchema& match) { + return graph.insertFunctionCall(&callee, match); + }); +} + +inline py::object invokeScriptMethodFromPython( + Method& callee, + const tuple_slice& args, + const py::kwargs& kwargs) { + auto self = callee.owner()._ivalue(); + + if (auto torch_fn_result = maybeTorchFunctionDispatch( + py::cast(callee), args, kwargs, callee.name())) { + return *torch_fn_result; + } + + return runAndInsertCall( + callee.function(), + args, + kwargs, + self, + [&](Graph& graph, const MatchedSchema& match) { + return graph.insertMethodCall(callee.name(), match); + }); +} + +TORCH_PYTHON_API std::pair, Stack> getOpWithStack( + const std::vector>& operations, + const py::args& args, + const py::kwargs& kwargs); + +TORCH_PYTHON_API py::object invokeOperatorFromPython( + const std::vector>& operations, + const py::args& args, + const py::kwargs& kwargs, + std::optional dk = std::nullopt); + +TORCH_PYTHON_API std::optional _maybe_handle_torch_function( + const std::string& ns, + const std::string& method_name, + const std::string& overload_name, + bool is_overload, + const py::args& args, + const py::kwargs& kwargs); + +TORCH_PYTHON_API bool checkSchemaAllowFakeScriptObject( + const FunctionSchema& schema, + const py::args& args, + const py::kwargs& kwargs); + +TORCH_PYTHON_API py::object _get_operation_for_overload_or_packet( + const std::vector>& operations, + Symbol symbol, + const py::args& args, + const py::kwargs& kwargs, + bool is_overload, + std::optional dk = std::nullopt); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_arg_flatten.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_arg_flatten.h new file mode 100644 index 0000000000000000000000000000000000000000..232f5b6ea08129b9ec29c4940a85d682c301d2c0 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_arg_flatten.h @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace torch::jit::python { + +struct IODescriptor { + struct VariableMetadata { + VariableMetadata(const autograd::Variable& var) + : sizes(var.sizes().vec()), + type(var.scalar_type()), + device(var.device()), + requires_grad(var.requires_grad()) {} + + bool operator==(const VariableMetadata& o) const { + return std::tie(device, requires_grad, type, sizes) == + std::tie(o.device, o.requires_grad, o.type, o.sizes); + } + + static size_t hash(const VariableMetadata& m) { + return c10::get_hash(m.sizes, m.device, m.requires_grad, m.type); + } + + std::vector sizes; + at::ScalarType type; + at::Device device; + bool requires_grad; + }; + + bool operator==(const IODescriptor& o) const { + return std::tie(structure, metadata, grad_enabled) == + std::tie(o.structure, o.metadata, o.grad_enabled); + } + + static size_t hash(const IODescriptor& o) { + return c10::get_hash(o.structure, o.metadata, o.grad_enabled); + } + + void extend(const autograd::variable_list& list) { + metadata.reserve(metadata.size() + list.size()); + for (auto& var : list) + metadata.emplace_back(var); + } + + // Description of argument structure. Variables are replaced with + // different characters, depending on their flags, beginnings and + // ends of tuples and lists are denoted by a pair of parenthesis + // of their corresponding kind. They should always be paired. + // Example desc: (vv[v(v)v]) + // NOTE: if extend() was ever called then metadata.size() can be + // different than the number of 'v's in structure. + std::string structure; + std::vector strings; + std::vector metadata; + bool grad_enabled = false; +}; + +static inline std::ostream& operator<<( + std::ostream& out, + const IODescriptor::VariableMetadata& meta) { + at::Device meta_device = meta.device; + auto& t = at::getDeprecatedTypeProperties( + meta_device.is_cpu() ? at::Backend::CPU : at::Backend::CUDA, meta.type); + out << t << "(requires_grad=" << meta.requires_grad; + if (meta_device.is_cuda()) { + out << ", device=" << meta_device.index(); + } + out << ") {"; + for (const auto i : c10::irange(meta.sizes.size())) { + if (i > 0) + out << ", "; + out << meta.sizes[i]; + } + out << "}"; + return out; +} + +static inline std::ostream& operator<<( + std::ostream& out, + const IODescriptor& desc) { + out << desc.structure << "\n"; + out << " with grad_enabled=" << desc.grad_enabled << "\n"; + for (const auto i : c10::irange(desc.metadata.size())) { + out << " with v" << i << " having type " << desc.metadata[i] << "\n"; + } + return out; +} + +struct ParsedArgs { + // Flat vector of Variables found in arguments + autograd::variable_list vars; + // Metadata describing nesting of objects received from Python and + // metadata of vars and whether grad is enabled. + IODescriptor desc; + + void extend(const autograd::variable_list& list) { + if (list.empty()) + return; + vars.reserve(vars.size() + list.size()); + for (auto& var : list) + vars.emplace_back(var); + desc.extend(list); + } +}; + +ParsedArgs flatten(py::handle obj); +PyObject* unflatten( + at::ArrayRef vars, + const IODescriptor& structure); + +} // namespace torch::jit::python diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_custom_class.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_custom_class.h new file mode 100644 index 0000000000000000000000000000000000000000..3c2c58efbde0c904b82b17623ff087db8bf824d8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_custom_class.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +void initPythonCustomClassBindings(PyObject* module); + +struct ScriptClass { + ScriptClass(c10::StrongTypePtr class_type) + : class_type_(std::move(class_type)) {} + + py::object __call__(const py::args& args, const py::kwargs& kwargs); + + c10::StrongTypePtr class_type_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_dict.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_dict.h new file mode 100644 index 0000000000000000000000000000000000000000..5e8fdbfe9a0a5fcb6a63d45bd85a9b74ebaa766d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_dict.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include + +namespace torch::jit { + +void initScriptDictBindings(PyObject* module); + +/// An iterator over the keys of ScriptDict. This is used to support +/// .keys() and iteration. +class ScriptDictKeyIterator final { + public: + ScriptDictKeyIterator( + c10::impl::GenericDict::iterator iter, + c10::impl::GenericDict::iterator end) + : iter_(std::move(iter)), end_(std::move(end)) {} + at::IValue next(); + + private: + c10::impl::GenericDict::iterator iter_; + c10::impl::GenericDict::iterator end_; +}; + +/// An iterator over the key-value pairs of ScriptDict. This is used to support +/// .items(). +class ScriptDictIterator final { + public: + ScriptDictIterator( + c10::impl::GenericDict::iterator iter, + c10::impl::GenericDict::iterator end) + : iter_(std::move(iter)), end_(std::move(end)) {} + at::IValue next(); + + private: + c10::impl::GenericDict::iterator iter_; + c10::impl::GenericDict::iterator end_; +}; + +/// A wrapper around c10::Dict that can be exposed in Python via pybind +/// with an API identical to the Python dictionary class. This allows +/// dictionaries to have reference semantics across the Python/TorchScript +/// boundary. +class ScriptDict final { + public: + // Constructor. + ScriptDict(const at::IValue& data) + : dict_(at::AnyType::get(), at::AnyType::get()) { + TORCH_INTERNAL_ASSERT(data.isGenericDict()); + dict_ = data.toGenericDict(); + } + + // Get the type of the dictionary. + at::DictTypePtr type() const { + return at::DictType::create(dict_.keyType(), dict_.valueType()); + } + + // Return a string representation that can be used + // to reconstruct the instance. + std::string repr() const { + std::ostringstream s; + s << '{'; + bool f = false; + for (auto const& kv : dict_) { + if (f) { + s << ", "; + } + s << kv.key() << ": " << kv.value(); + f = true; + } + s << '}'; + return s.str(); + } + + // Return an iterator over the keys of the dictionary. + ScriptDictKeyIterator iter() const { + auto begin = dict_.begin(); + auto end = dict_.end(); + return ScriptDictKeyIterator(begin, end); + } + + // Return an iterator over the key-value pairs of the dictionary. + ScriptDictIterator items() const { + auto begin = dict_.begin(); + auto end = dict_.end(); + return ScriptDictIterator(begin, end); + } + + // Interpret the dictionary as a boolean; empty means false, non-empty means + // true. + bool toBool() const { + return !(dict_.empty()); + } + + // Get the value for the given key. Throws std::out_of_range if the key does + // not exist. + at::IValue getItem(const at::IValue& key) { + return dict_.at(key); + } + + // Set the value for the given key. + void setItem(const at::IValue& key, const at::IValue& value) { + dict_.insert_or_assign(key, value); + } + + // Check whether the dictionary contains the given key. + bool contains(const at::IValue& key) { + return dict_.contains(key); + } + + // Delete the given key from the dictionary. + bool delItem(const at::IValue& key) { + return dict_.erase(key); + } + + // Get the size of the dictionary. + int64_t len() const { + return dict_.size(); + } + + // A c10::Dict instance that holds the actual data. + c10::impl::GenericDict dict_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ir.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ir.h new file mode 100644 index 0000000000000000000000000000000000000000..26adf8c0e49419b578c233d369f6c79ef43f6796 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ir.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +void initPythonIRBindings(PyObject* module); + +// execute a Python function, used for Ops we can't optimize but that we want to +// optimize around +struct ConcretePythonOp : public PythonOp { + static Symbol Kind; + + ConcretePythonOp(Graph* graph) : PythonOp(graph, ::c10::prim::PythonOp) {} + ConcretePythonOp* init( + THPObjectPtr&& pyobj, + const std::string& cconv, + pyobj_list&& scalar_args) { + this->pyobj = std::move(pyobj); + this->scalar_args = std::move(scalar_args); + this->cconv = cconv; + return this; + } + // The Python object which contains the implementation of this function. + // This is either a class (non-legacy) or an object (legacy). See + // TraceInterpreterState for execution semantics. + THPObjectPtr pyobj; + // The calling convention for the Python function. + // 'c' -- constant argument + // 'd' -- dynamic argument + std::string cconv; + // Scalar arguments to the Python function. Not necessarily passed to + // the function in this order; see cconv for the correct order. + std::vector scalar_args; + + std::string name() const override; + void cloneFrom(Node* other_) override; + Node* allocNewInstance(Graph* g) override { + return new ConcretePythonOp(g); + } + // recover the autograd.Function instance, if this PythonOp's function + // was originally SomeFunction.apply + // used in ONNX for discovering symbolics + std::optional autogradFunction() const override; + void writeScalars(std::ostream& out) const override; + void lint_python() const override; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ivalue.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ivalue.h new file mode 100644 index 0000000000000000000000000000000000000000..1d44282d59d678ff3f9cd57aa16ee747cb4bf549 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_ivalue.h @@ -0,0 +1,111 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace py = pybind11; + +namespace c10::ivalue { + +// concrete ivalue Holder that hold a py::object +struct C10_EXPORT ConcretePyObjectHolder final : PyObjectHolder { + public: + static c10::intrusive_ptr create(py::object py_obj) { + return c10::make_intrusive(std::move(py_obj)); + } + + static c10::intrusive_ptr create(const py::handle& handle) { + py::gil_scoped_acquire ag; + return c10::make_intrusive( + handle.cast()); + } + + PyObject* getPyObject() override { + return py_obj_.ptr(); + } + + InferredType tryToInferType() override { + pybind11::gil_scoped_acquire ag; + return torch::jit::tryToInferType(py_obj_); + } + + IValue toIValue(const TypePtr& type, std::optional N = std::nullopt) + override { + pybind11::gil_scoped_acquire ag; + return torch::jit::toIValue(py_obj_, type, N); + } + + std::string toStr() override { + pybind11::gil_scoped_acquire ag; + return py::str(py_obj_); + } + + std::vector extractTensors() override { + // We could implement this entirely in C++ via pybind11 but it turns out to + // be substantially slower. Namely, the total time taken by markCompleted on + // a CUDAFuture is 21.5us with this implementation, but goes up to 58.7us + // when using C++. The reason is unclear. + try { + pybind11::gil_scoped_acquire ag; + +#if IS_PYBIND_2_13_PLUS + PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store + storage; + auto& extractorFn = + storage + .call_once_and_store_result([]() -> py::object { + return py::module_::import("torch._jit_internal") + .attr("_extract_tensors"); + }) + .get_stored(); +#else + static py::object& extractorFn = *new py::object( + py::module::import("torch._jit_internal").attr("_extract_tensors")); +#endif + + return extractorFn(py_obj_).cast>(); + } catch (py::error_already_set& e) { + auto err = std::runtime_error( + c10::str("Cannot extract tensors from value: ", e.what())); + { + pybind11::gil_scoped_acquire ag; + e.restore(); + PyErr_Clear(); + } + throw std::runtime_error(err); + } + } + + // Note [Destructing py::object] + // ~~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // (1) Why py_obj_ = py::none(); does not work. Because we also need to + // acquire GIL when destructing py::object of None that de-references None. + // https://docs.python.org/3/c-api/none.html#c.Py_RETURN_NONE + // + // https://stackoverflow.com/questions/15287590/why-should-py-increfpy-none-be-required-before-returning-py-none-in-c + // + // (2) Why we need to call dec_ref() explicitly. Because py::object of + // nullptr, on destruction, effectively does nothing because of it calls + // Py_XDECREF(NULL) underlying. + // https://docs.python.org/3/c-api/refcounting.html#c.Py_XDECREF + ~ConcretePyObjectHolder() override { + pybind11::gil_scoped_acquire ag; + py_obj_.dec_ref(); + // explicitly setting PyObject* to nullptr to prevent py::object's dtor to + // decref on the PyObject again. + py_obj_.ptr() = nullptr; + } + + // explicit construction to avoid errornous implicit conversion and + // copy-initialization + explicit ConcretePyObjectHolder(py::object py_obj) + : py_obj_(std::move(py_obj)) {} + + private: + py::object py_obj_; +}; + +} // namespace c10::ivalue diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_list.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_list.h new file mode 100644 index 0000000000000000000000000000000000000000..83955a9f3d5ae3a56e7e17c5e46f6c25f8d9b979 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_list.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +void initScriptListBindings(PyObject* module); + +/// An iterator over the elements of ScriptList. This is used to support +/// __iter__(), . +class ScriptListIterator final { + public: + ScriptListIterator( + c10::impl::GenericList::iterator iter, + c10::impl::GenericList::iterator end) + : iter_(iter), end_(end) {} + at::IValue next(); + bool done() const; + + private: + c10::impl::GenericList::iterator iter_; + c10::impl::GenericList::iterator end_; +}; + +/// A wrapper around c10::List that can be exposed in Python via pybind +/// with an API identical to the Python list class. This allows +/// lists to have reference semantics across the Python/TorchScript +/// boundary. +class ScriptList final { + public: + // TODO: Do these make sense? + using size_type = size_t; + using diff_type = ptrdiff_t; + using ssize_t = Py_ssize_t; + + // Constructor for empty lists created during slicing, extending, etc. + ScriptList(const at::TypePtr& type) : list_(at::AnyType::get()) { + auto list_type = type->expect(); + list_ = c10::impl::GenericList(list_type); + } + + // Constructor for instances based on existing lists (e.g. a + // Python instance or a list nested inside another). + ScriptList(const at::IValue& data) : list_(at::AnyType::get()) { + TORCH_INTERNAL_ASSERT(data.isList()); + list_ = data.toList(); + } + + at::ListTypePtr type() const { + return at::ListType::create(list_.elementType()); + } + + // Return a string representation that can be used + // to reconstruct the instance. + std::string repr() const { + std::ostringstream s; + s << '['; + bool f = false; + for (auto const& elem : list_) { + if (f) { + s << ", "; + } + s << at::IValue(elem); + f = true; + } + s << ']'; + return s.str(); + } + + // Return an iterator over the elements of the list. + ScriptListIterator iter() const { + auto begin = list_.begin(); + auto end = list_.end(); + return ScriptListIterator(begin, end); + } + + // Interpret the list as a boolean; empty means false, non-empty means + // true. + bool toBool() const { + return !(list_.empty()); + } + + // Get the value for the given index. + at::IValue getItem(diff_type idx) { + idx = wrap_index(idx); + return list_.get(idx); + } + + // Set the value corresponding to the given index. + void setItem(diff_type idx, const at::IValue& value) { + idx = wrap_index(idx); + return list_.set(idx, value); + } + + // Check whether the list contains the given value. + bool contains(const at::IValue& value) { + for (const auto& elem : list_) { + if (elem == value) { + return true; + } + } + + return false; + } + + // Delete the item at the given index from the list. + void delItem(diff_type idx) { + idx = wrap_index(idx); + auto iter = list_.begin() + idx; + list_.erase(iter); + } + + // Get the size of the list. + ssize_t len() const { + return list_.size(); + } + + // Count the number of times a value appears in the list. + ssize_t count(const at::IValue& value) const { + ssize_t total = 0; + + for (const auto& elem : list_) { + if (elem == value) { + ++total; + } + } + + return total; + } + + // Remove the first occurrence of a value from the list. + void remove(const at::IValue& value) { + auto list = list_; + + int64_t idx = -1, i = 0; + + for (const auto& elem : list) { + if (elem == value) { + idx = i; + break; + } + + ++i; + } + + if (idx == -1) { + throw py::value_error(); + } + + list.erase(list.begin() + idx); + } + + // Append a value to the end of the list. + void append(const at::IValue& value) { + list_.emplace_back(value); + } + + // Clear the contents of the list. + void clear() { + list_.clear(); + } + + // Append the contents of an iterable to the list. + void extend(const at::IValue& iterable) { + list_.append(iterable.toList()); + } + + // Remove and return the element at the specified index from the list. If no + // index is passed, the last element is removed and returned. + at::IValue pop(std::optional idx = std::nullopt) { + at::IValue ret; + + if (idx) { + idx = wrap_index(*idx); + ret = list_.get(*idx); + list_.erase(list_.begin() + *idx); + } else { + ret = list_.get(list_.size() - 1); + list_.pop_back(); + } + + return ret; + } + + // Insert a value before the given index. + void insert(const at::IValue& value, diff_type idx) { + // wrap_index cannot be used; idx == len() is allowed + if (idx < 0) { + idx += len(); + } + + if (idx < 0 || idx > len()) { + throw std::out_of_range("list index out of range"); + } + + list_.insert(list_.begin() + idx, value); + } + + // A c10::List instance that holds the actual data. + c10::impl::GenericList list_; + + private: + // Wrap an index so that it can safely be used to access + // the list. For list of size sz, this function can successfully + // wrap indices in the range [-sz, sz-1] + diff_type wrap_index(diff_type idx) { + auto sz = len(); + if (idx < 0) { + idx += sz; + } + + if (idx < 0 || idx >= sz) { + throw std::out_of_range("list index out of range"); + } + + return idx; + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_sugared_value.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_sugared_value.h new file mode 100644 index 0000000000000000000000000000000000000000..15cc2445fd56b076c87469ceaaded924c35fc865 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_sugared_value.h @@ -0,0 +1,378 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +std::string typeString(py::handle h); + +inline std::shared_ptr toSimple(Value* v) { + return std::make_shared(v); +} + +// NB: This should be the single entry-point for instantiating a SugaredValue +// from a Python object. If you are adding support for converting a new Python +// type, *add it in this function's implementation*. +std::shared_ptr toSugaredValue( + py::object obj, + GraphFunction& m, + const SourceRange& loc, + bool is_constant = false); + +std::optional as_function(const py::object& obj); + +struct VISIBILITY_HIDDEN PythonValue : public SugaredValue { + PythonValue( + py::object the_self, + std::optional rcb = std::nullopt, + Value* module_self = nullptr) + : self(std::move(the_self)), + rcb(std::move(rcb)), + moduleSelf_(module_self) {} + + FunctionSchema getSchema( + const size_t n_args, + const size_t n_binders, + const SourceRange& loc); + + // call it like a function, e.g. `outputs = this(inputs)` + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& m, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override; + + std::string kind() const override; + + std::vector> asTuple( + const SourceRange& loc, + GraphFunction& m, + const std::optional& size_hint = {}) override; + + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + Value* asValue(const SourceRange& loc, GraphFunction& m) override { + throw( + ErrorReport(loc) + << kind() << " cannot be used as a value. " + << "Perhaps it is a closed over global variable? If so, please " + << "consider passing it in as an argument or use a local varible " + << "instead."); + } + + protected: + py::object getattr(const SourceRange& loc, const std::string& name); + + void checkForAddToConstantsError(std::stringstream& ss); + + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + py::object self; + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + std::optional rcb; + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + Value* moduleSelf_ = nullptr; +}; + +struct VISIBILITY_HIDDEN PythonModuleValue : public PythonValue { + explicit PythonModuleValue(py::object mod) : PythonValue(std::move(mod)) {} + + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; +}; + +// Used for desugaring uses of the torch.cuda module. All the CUDA APIs with +// torch.cuda.* are resolved using CUDAPythonModuleValue. +struct VISIBILITY_HIDDEN CUDAPythonModuleValue : public PythonValue { + explicit CUDAPythonModuleValue(py::object mod) + : PythonValue(std::move(mod)) {} + + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; +}; + +// Represents all the parameters of a module as a List[Tensor] +struct VISIBILITY_HIDDEN ConstantParameterList : public SugaredValue { + ConstantParameterList(Value* the_list) : the_list_(the_list) {} + std::string kind() const override { + return "constant parameter list"; + } + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& caller, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override { + return toSimple(the_list_); + } + + private: + Value* the_list_; +}; + +struct VISIBILITY_HIDDEN ModuleDictMethod : public SugaredValue { + explicit ModuleDictMethod(SugaredValuePtr iterable, std::string name) + : iterable_(std::move(iterable)), name_(std::move(name)) {} + + std::string kind() const override { + return name_; + } + + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& f, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override { + if (!args.empty() || !kwargs.empty()) { + throw( + ErrorReport(loc) << name_ << " method does not accept any arguments"); + } + return iterable_; + } + + SugaredValuePtr iterable_; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + const std::string name_; +}; + +struct SugaredDict; + +// defines how modules/methods behave inside the script subset. +// for now this does not have any interaction with python. +// in the future, we will add the ability to resolve `self.foo` to python +// {functions, modules, constants} so this SugaredValue is defined here +// anticipating we will eventually need to replace Module with a py::object +// holding the actual nn.Module class. + +struct VISIBILITY_HIDDEN ModuleValue : public SugaredValue { + ModuleValue(Value* self, std::shared_ptr concreteType) + : self_(self), concreteType_(std::move(concreteType)) {} + + std::string kind() const override { + return "module"; + } + + Value* asValue(const SourceRange& loc, GraphFunction& m) override; + + SugaredValuePtr asTupleValue(const SourceRange& loc, GraphFunction& m) + override; + + // select an attribute on it, e.g. `this.field` + std::shared_ptr tryGetAttr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field); + + // select an attribute on it, e.g. `this.field` + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + // select an attribute on it, e.g. `this.field` + bool hasAttr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + // call module.forward with pre_hooks and hooks + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& caller, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override; + + std::shared_ptr getSugaredDict( + const SourceRange& loc, + GraphFunction& m); + + std::shared_ptr getSugaredNamedBufferDict( + const SourceRange& loc, + GraphFunction& m); + + std::shared_ptr getSugaredNamedParameterList( + const SourceRange& loc, + GraphFunction& m); + + std::shared_ptr getSugaredNamedParameterDict( + const SourceRange& loc, + GraphFunction& m); + + void setAttr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field, + Value* newValue) override; + + SugaredValuePtr iter(const SourceRange& loc, GraphFunction& m) override; + + std::shared_ptr getitem( + const SourceRange& loc, + GraphFunction& m, + Value* idx, + TypePtr type_hint) override; + + private: + // Check that the type of all submodules is a subtype of ty. If the function + // returns false, more information about why it returns false (e.g. which + // submodule's type is not a subtype of ty) is printed it why_not if it is not + // null. + bool areAllSubmodulesSubtypeOf( + const TypePtr& ty, + std::ostream* why_not = nullptr) const; + + Value* self_; + std::shared_ptr concreteType_; +}; + +bool isNamedTupleClass(const py::object& obj); +TypePtr registerNamedTuple( + const py::object& obj, + const SourceRange& loc, + const ResolutionCallback& rcb); + +void recurseThroughNestedModules( + const SourceRange& loc, + GraphFunction& m, + std::vector& keys, + std::vector& values, + std::shared_ptr& self, + const std::string& prefix, + const std::string& field); + +// Used to support named_modules() +struct VISIBILITY_HIDDEN SugaredDict : public SugaredValue { + explicit SugaredDict( + std::shared_ptr self, + std::shared_ptr keys, + std::shared_ptr modules) + : self_(std::move(self)), + keys_(std::move(keys)), + modules_(std::move(modules)) {} + + std::string kind() const override { + return "ModuleDict"; + } + + std::shared_ptr getKeys() { + return keys_; + } + + std::shared_ptr getModules() { + return modules_; + } + + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + SugaredValuePtr iter(const SourceRange& loc, GraphFunction& m) override { + return keys_; + } + + std::shared_ptr self_; + std::shared_ptr keys_; + std::shared_ptr modules_; +}; + +struct VISIBILITY_HIDDEN BooleanDispatchValue : public SugaredValue { + BooleanDispatchValue(py::dict dispatched_fn) + : dispatched_fn_(std::move(dispatched_fn)) {} + + std::string kind() const override { + return "boolean dispatch"; + } + + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& caller, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override; + + private: + py::dict dispatched_fn_; +}; + +struct VISIBILITY_HIDDEN PythonClassValue : public ClassValue { + PythonClassValue(ClassTypePtr type, py::object py_type) + : ClassValue(std::move(type)), py_type_(std::move(py_type)) {} + + std::string kind() const override { + return "Python type"; + } + + std::shared_ptr attr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + bool hasAttr( + const SourceRange& loc, + GraphFunction& m, + const std::string& field) override; + + private: + py::object py_type_; +}; + +struct VISIBILITY_HIDDEN PythonExceptionValue : public ExceptionValue { + explicit PythonExceptionValue(const py::object& exception_class) + : ExceptionValue( + py::str(py::getattr(exception_class, "__name__", py::str("")))), + exception_class_qualified_name_( + py::str(py::module::import("torch._jit_internal") + .attr("_qualified_name")( + exception_class, + /*mangle_name=*/false))) {} + + std::string kind() const override { + return "Python exception"; + } + + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& caller, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override; + + private: + std::string exception_class_qualified_name_; +}; + +// Python Slice class. +struct VISIBILITY_HIDDEN PythonSliceClass : public SugaredValue { + explicit PythonSliceClass() = default; + + std::string kind() const override { + return "Python slice class"; + } + + std::shared_ptr call( + const SourceRange& loc, + GraphFunction& caller, + at::ArrayRef args, + at::ArrayRef kwargs, + size_t n_binders) override; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tracer.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tracer.h new file mode 100644 index 0000000000000000000000000000000000000000..55c36f7a88ab05d58ccedaf26f8cd2958647f765 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tracer.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace torch::jit { + +struct Module; + +namespace tracer { +void initPythonTracerBindings(PyObject* module); + +SourceRange getPythonInterpreterSourceRange(); + +Node* preRecordPythonTrace( + THPObjectPtr pyobj, + const std::string& arg_types, + at::ArrayRef inputs, + std::vector scalar_args); + +std::pair, Stack> createGraphByTracingWithDict( + const py::function& func, + const py::dict& inputs_dict, + const Stack& inputs, + const py::function& var_name_lookup_fn, + bool strict, + bool force_outplace, + Module* self = nullptr, + const std::vector& argument_names = {}); + +std::pair, Stack> createGraphByTracing( + const py::function& func, + Stack inputs, + const py::function& var_name_lookup_fn, + bool strict, + bool force_outplace, + Module* self = nullptr, + const std::vector& argument_names = {}); +} // namespace tracer +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tree_views.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tree_views.h new file mode 100644 index 0000000000000000000000000000000000000000..796bf125defd824520dda38496aa09d71480252a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/python_tree_views.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +void initTreeViewBindings(PyObject* module); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/script_init.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/script_init.h new file mode 100644 index 0000000000000000000000000000000000000000..65c8ad3be6850e6629c52238b9d64a20062c5c0a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/script_init.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace torch::jit { +void initJitScriptBindings(PyObject* module); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/update_graph_executor_opt.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/update_graph_executor_opt.h new file mode 100644 index 0000000000000000000000000000000000000000..81cfd658f6ede87517a2edad395984293885ab76 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/update_graph_executor_opt.h @@ -0,0 +1,6 @@ +#pragma once +#include +namespace torch::jit { +TORCH_API void setGraphExecutorOptimize(bool o); +TORCH_API bool getGraphExecutorOptimize(); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/utf8_decoding_ignore.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/utf8_decoding_ignore.h new file mode 100644 index 0000000000000000000000000000000000000000..3b50bce86ff5b9cb5984f16b3e2c248cded67569 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/python/utf8_decoding_ignore.h @@ -0,0 +1,6 @@ +#pragma once +#include +namespace torch::jit { +TORCH_API void setUTF8DecodingIgnore(bool o); +TORCH_API bool getUTF8DecodingIgnore(); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/resource_guard.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/resource_guard.h new file mode 100644 index 0000000000000000000000000000000000000000..a78022a0c6fe07645a2855ad17a50f3b0319b766 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/resource_guard.h @@ -0,0 +1,25 @@ +#pragma once +#include + +namespace torch::jit { + +class ResourceGuard { + std::function _destructor; + bool _released{false}; + + public: + ResourceGuard(std::function destructor) + : _destructor(std::move(destructor)) {} + + // NOLINTNEXTLINE(bugprone-exception-escape) + ~ResourceGuard() { + if (!_released) + _destructor(); + } + + void release() { + _released = true; + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/argument_spec.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/argument_spec.h new file mode 100644 index 0000000000000000000000000000000000000000..493a63b9444691a26c4a9fb712b0159772749ba4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/argument_spec.h @@ -0,0 +1,503 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +C10_CLANG_DIAGNOSTIC_PUSH() +#if C10_CLANG_HAS_WARNING("-Wshorten-64-to-32") +C10_CLANG_DIAGNOSTIC_IGNORE("-Wshorten-64-to-32") +#endif + +namespace torch::jit { + +// GraphExecutor creates specializations of Graphs for different +// dimensionalitities and types of inputs. + +struct ArgumentInfo { + friend struct ArgumentSpec; + using plain_data_type = uint64_t; + + bool defined() const { + return defined_; + } + at::Device device() const { + return at::Device(DeviceType(dev_type_), device_); + } + // XXX: It is guaranteed that this will return false when called on non-tensor + // arguments + bool requires_grad() const { + return requires_grad_; + } + int dim() const { + return dim_; + } + at::ScalarType type() const { + return at::ScalarType(type_); + } + TypePtr toType() const { + if (!defined()) + return TensorType::get(); + + return TensorType::create( + type(), device(), std::optional(dim()), requires_grad()); + } + operator TypePtr() const { + return toType(); + } + + private: + unsigned defined_ : 1; + unsigned requires_grad_ : 1; + unsigned : 5; + unsigned dim_ : 8; + unsigned device_ : 8; + unsigned type_ : 8; + unsigned dev_type_ : 16; + unsigned : 16; +}; + +static_assert( + std::is_standard_layout_v, + "ArgumentInfo is to be a POD struct"); +static_assert( + sizeof(ArgumentInfo) == sizeof(ArgumentInfo::plain_data_type), + "ArgumentInfo is expected to be a 32-bit struct"); + +struct ArgumentSpec { + ArgumentSpec(size_t num_flat_tensor_inputs, size_t num_flat_optional_inputs) + : hash_code(c10::hash_combine( + num_flat_tensor_inputs, + num_flat_optional_inputs)) { + tensor_args.reserve(num_flat_tensor_inputs); + optional_presence.reserve(num_flat_optional_inputs); + } + + void addOptional(const IValue& input) { + bool is_present = !input.isNone(); + optional_presence.push_back(is_present); + hash_code = c10::hash_combine(hash_code, is_present); + } + + void addTensor(const IValue& input, bool with_grad) { + AT_ASSERT(input.isTensor(), "Expected Tensor but found ", input.tagKind()); + tensor_args.emplace_back(); + auto& arg = tensor_args.back(); + // Initialize all fields to 0. This is convenient, because e.g. + // requires_grad() can be checked even on tensors AND will make + // padding bits all 0s. + std::memset(&arg, 0, sizeof(ArgumentInfo)); + + // [argspec refcounting] reinterpret the IValue to avoid having to refcount + // the Tensor microbenchmarks + // https://github.com/zdevito/pytorch/commit/21e7200a0a0fc456bea2f10e95b1781f83933d10 + // show overhead in extra refcounting along this path + const at::Tensor* t = reinterpret_cast(&input); + arg.defined_ = t->defined(); + if (arg.defined_) { + arg.requires_grad_ = with_grad && autograd::Variable(*t).requires_grad(); + arg.dim_ = t->dim(); + at::Device device = t->device(); + arg.dev_type_ = + // NOLINTNEXTLINE(bugprone-signed-char-misuse) + static_cast>(device.type()); + // NOLINTNEXTLINE(bugprone-signed-char-misuse) + arg.device_ = device.index(); + arg.type_ = static_cast(t->scalar_type()); + } + combineHash(arg); + } + + void combineHash(const ArgumentInfo& arg) { + ArgumentInfo::plain_data_type arg_data = 0; + std::memcpy(&arg_data, &arg, sizeof(ArgumentInfo)); + hash_code = c10::hash_combine(hash_code, arg_data); + } + + // equality is fast: check ninputs, and then check the raw array data, + // there are no size/stride indirections + // hopefully std::vector has fast equality + bool operator==(const ArgumentSpec& spec) const { + if (optional_presence != spec.optional_presence) { + return false; + } + if (tensor_args.size() != spec.tensor_args.size()) + return false; + // NB: we need to break out early when there are no elements, because + // passing a nullptr to memcmp is UB. + if (tensor_args.empty()) + return true; + return std::memcmp( + tensor_args.data(), + spec.tensor_args.data(), + tensor_args.size() * sizeof(ArgumentInfo)) == 0; + } + bool operator!=(const ArgumentSpec& spec) const { + return !(*this == spec); + } + size_t numTensors() const { + return tensor_args.size(); + } + const ArgumentInfo& tensorAt(size_t i) const { + return tensor_args[i]; + } + size_t numOptionals() const { + return optional_presence.size(); + } + bool isPresent(size_t i) const { + return optional_presence[i]; + } + size_t hashCode() const { + return hash_code; + } + + private: + size_t hash_code; // precomputed on construction + std::vector tensor_args; + std::vector optional_presence; +}; + +namespace { +static constexpr size_t ARG_SPEC_DEPTH_LIMIT = 128; +} + +// ArgumentSpecCreator takes an initial graph and comes up with a set +// of simple instructions to compute the ArgumentSpec given a set of +// input tensors. +struct TORCH_API ArgumentSpecCreator { + // instructs acts on a stack of a list of input IValues + // at the beginning the stack contains a single list of the inputs to the + // function the ENTER_ instructs descend into subobjects and push new lists + // onto the stack + enum Inst : char { + ENTER_TUPLE, // consume a tuple ivalue from the top-most list, and push the + // list of its elements onto the stack as a new list + ENTER_OBJECT, // same as ENTER_TUPLE, but the input is a class + LEAVE, // pop the top-most list from the stack + SKIP, // consume an element from the top-most list, and discard + SPECIALIZE_OPTIONAL_TENSOR, // consume a optional tensor for the top-most + // list, and add it to the ArgSpec key being + // created + SPECIALIZE_TENSOR, // consume a tensor for the top-most + // list, and add it to the ArgSpec key being created + SPECIALIZE_OPTIONAL, + // consume a nontensor optional from the top-most list, + // and add it to the ArgSpec key being created + }; + ArgumentSpecCreator(Graph& graph); + ArgumentSpec create(bool with_grad, const Stack& stack) const; + void specializeTypes(Graph& g, const ArgumentSpec& spec) const; + void dump() const; + using WrittenSlots = std::unordered_set; + + private: + void scan( + const TypePtr& typ, + size_t depth, + const WrittenSlots& written_slots); + size_t num_inputs_; + size_t num_tensors_ = 0; + size_t num_optionals_ = 0; + std::vector instructions_; +}; + +// CompleteArgumentSpec represents one particular specialization. +// It is designed so that it can be created, hashed, and compared quickly +// since it is used along the hot-path of the JIT to check if the code +// we have created is valid for the given inputs. + +// COmpleteArgumentInfoPOD is only used internally in CompleteArgumentSpec +// API users should use ArgumentInfo +struct CompleteArgumentInfoPOD { + // total size is 64-bit + unsigned is_tensor : 8; // all other fields are invalid if this is false + unsigned type : 8; // scalar type + unsigned defined : 1; + unsigned requires_grad : 1; + signed device : 14; + unsigned dev_type : 16; + unsigned + total_dims : 16; // all TensorInfoPODs are in CompleteArgumentSpec's + // tensor_info() array. total_dims is the total number of + // dimensions seen so far in all previous members of + // tensor_info(), including this tensor 2*total_dims + // becomes the offset into the sizes_strides list for the + // _next_ tensor in the tensor_info array for tensor 0, + // the offset is always 0 +}; + +static_assert( + sizeof(CompleteArgumentInfoPOD) == sizeof(int64_t), + "CompleteArgumentInfoPOD must be 64-bit struct for CompleteArgumentSpec encoding to work"); + +struct CompleteArgumentInfo; + +struct CompleteArgumentSpec { + CompleteArgumentSpec(bool with_grad, at::ArrayRef inputs) + : hash_code(0), ninputs(inputs.size()) { + int32_t all_dims = 0; + const auto num_inputs = inputs.size(); + for (const auto i : c10::irange(num_inputs)) { + if (!inputs[i].isTensor()) + continue; + auto& tensor = inputs[i].toTensor(); + all_dims += tensor.defined() ? tensor.ndimension() : 0; + } + // allocate enough room for all TensorPODs and dimensions + data.resize(ninputs + all_dims * 2); + + // and reinterpret our data array as these structs + auto* pods = reinterpret_cast(data.data()); + int64_t* next_dim = sizes_strides(); + int32_t total_dims = 0; + for (const auto i : c10::irange(num_inputs)) { + auto& pod = pods[i]; + pod.is_tensor = static_cast(inputs[i].isTensor()); + if (pod.is_tensor) { + at::Tensor t = inputs[i].toTensor(); + pod.defined = t.defined(); + if (pod.defined) { + pod.type = static_cast(t.scalar_type()); + at::Device device = t.device(); + // NOLINTNEXTLINE(bugprone-signed-char-misuse) + pod.dev_type = + static_cast>(device.type()); + // NOLINTNEXTLINE(bugprone-signed-char-misuse) + pod.device = device.index(); + pod.requires_grad = with_grad && t.requires_grad(); + total_dims += t.ndimension(); + auto sizes = t.sizes(); + std::copy(sizes.begin(), sizes.end(), next_dim); + next_dim += sizes.size(); + auto strides = t.strides(); + std::copy(strides.begin(), strides.end(), next_dim); + next_dim += strides.size(); + } + } + // each POD has a running tally of all dimensions including its own + TORCH_CHECK( + total_dims < std::numeric_limits::max(), + "The number of dims cannot be packed into CompleteArgumentSpec:", + total_dims); + pod.total_dims = total_dims; + } + // we precompute the hash_code to minimize the time inside of hash + // table operations where we may need to hold a compiler cache lock. + hash_code = c10::hash_combine(0, ninputs); + for (auto d : data) { + hash_code = c10::hash_combine(hash_code, d); + } + } + + // equality is fast: check ninputs, and then check the raw array data, + // there are no size/stride indirections + bool operator==(const CompleteArgumentSpec& spec) const { + return ninputs == spec.ninputs && data == spec.data; + } + bool operator!=(const CompleteArgumentSpec& spec) const { + return !(*this == spec); + } + friend struct CompleteArgumentInfo; + CompleteArgumentInfo at(size_t i) const; + size_t size() const { + return ninputs; + } + size_t hashCode() const { + return hash_code; + } + + private: + ArrayRef tensor_info() const { + return ArrayRef( + reinterpret_cast(data.data()), ninputs); + } + // the start of the sizes_strides information, which comes after the + // CompleteArgumentInfoPOD list. + const int64_t* sizes_strides() const { + return data.data() + ninputs; + } + int64_t* sizes_strides() { + return data.data() + ninputs; + } + size_t hash_code; // precomputed on construction + size_t ninputs; + // layout is ninputs of TensorPOD (each 64-bit) followed by their size and + // stride info for 3 tensors: + // [t0POD][t1POD][t2POD]... + // [t0 sizes][t0 strides][t1 sizes][t1 strides][t2 sizes][t2 strides] + std::vector data; +}; + +// public view of compressed CompleteArgumentInfo +struct CompleteArgumentInfo { + CompleteArgumentInfo(const CompleteArgumentSpec& spec, const int i) + : spec(spec), i(i) {} + bool isTensor() const { + return pod(i).is_tensor; + } + at::ScalarType type() const { + return at::ScalarType(pod(i).type); + } + bool defined() const { + return pod(i).defined; + } + bool requires_grad() const { + return pod(i).requires_grad; + } + at::Device device() const { + return at::Device( + DeviceType(pod(i).dev_type), + static_cast(pod(i).device)); + } + int ndimension() const { + // See [valid range], it is always valid to ask for offset for (i + 1) + return (sizes_strides_offset(i + 1) - sizes_strides_offset(i)) / 2; + } + at::IntArrayRef sizes() const { + return at::IntArrayRef( + spec.sizes_strides() + sizes_strides_offset(i), ndimension()); + } + at::IntArrayRef strides() const { + int ndim = ndimension(); + return at::IntArrayRef( + spec.sizes_strides() + sizes_strides_offset(i) + ndim, ndim); + } + operator TypePtr() const { + if (!defined()) + return TensorType::get(); + return TensorType::create( + type(), + device(), + c10::VaryingShape{sizes()}, + c10::VaryingShape{strides()}, + requires_grad()); + } + + private: + // offsetinto sizes_strides() array where the sizes start for tensor j + // [valid range] valid range is [0, ninputs] + // (i.e. you can ask for the offset at ninputs, which would be the offset of + // the next tensor if it existed) + int sizes_strides_offset(int j) const { + if (j == 0) + return 0; + return 2 * pod(j - 1).total_dims; + } + const CompleteArgumentInfoPOD& pod(int j) const { + return spec.tensor_info().at(j); + } + const CompleteArgumentSpec& spec; + const int i; +}; + +inline std::ostream& operator<<(std::ostream& out, const ArgumentInfo& info) { + if (!info.defined()) { + return out << ""; + } + out << "Tensor(device=" << info.device() << ", type=" << toString(info.type()) + << ", requires_grad=" << info.requires_grad() << ", dims=" << info.dim() + << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const ArgumentSpec& spec) { + out << "{"; + for (const auto i : c10::irange(spec.numTensors())) { + if (i > 0) + out << ", "; + out << spec.tensorAt(i); + } + out << "; "; + for (const auto i : c10::irange(spec.numOptionals())) { + if (i > 0) + out << ", "; + out << spec.isPresent(i); + } + out << "}"; + return out; +} + +inline std::ostream& operator<<( + std::ostream& out, + const CompleteArgumentInfo& info) { + if (!info.defined()) { + return out << ""; + } + out << "Tensor(device=" << info.device() << ", type=" << toString(info.type()) + << ", requires_grad=" << info.requires_grad() + << ", sizes=" << info.sizes() << ", strides=" << info.strides() << ")"; + return out; +} + +inline std::ostream& operator<<( + std::ostream& out, + const CompleteArgumentSpec& spec) { + out << "{"; + for (const auto i : c10::irange(spec.size())) { + if (i > 0) + out << ", "; + out << spec.at(i); + } + out << "}"; + return out; +} + +inline CompleteArgumentInfo CompleteArgumentSpec::at(size_t i) const { + return CompleteArgumentInfo(*this, i); +} + +inline std::optional convertOptional( + std::optional const& from) { + return (from) ? std::optional(static_cast(*from)) + : std::optional{}; +} + +} // namespace torch::jit + +namespace std { + +template +struct hash> { + size_t operator()(const c10::VaryingShape& vs) const { + return c10::get_hash( + vs.size(), + vs.size() ? vs.sizes().value() : std::vector>()); + } +}; + +template <> +struct hash { + size_t operator()(const c10::TensorType& ptt) const { + return c10::get_hash< + std::optional, + c10::VaryingShape, + c10::VaryingShape, + std::optional>( + torch::jit::convertOptional(ptt.scalarType()), + ptt.sizes(), + ptt.strides(), + ptt.requiresGrad()); + } +}; + +template <> +struct hash { + size_t operator()(const torch::jit::ArgumentSpec& spec) const { + return spec.hashCode(); + } +}; +template <> +struct hash { + size_t operator()(const torch::jit::CompleteArgumentSpec& spec) const { + return spec.hashCode(); + } +}; +} // namespace std + +C10_CLANG_DIAGNOSTIC_POP() diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/autodiff.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/autodiff.h new file mode 100644 index 0000000000000000000000000000000000000000..32a8166caf0e5936f3eea292aa7a895ad6ddbc58 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/autodiff.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include + +#include +#include + +namespace torch::jit { + +using value_list = std::vector; +// clang-format off +// Example showcasing how Gradient is constructed: +// +// Let's assume we have a function f, `m` and `n` do not require grad +// (`n` can depend only on `m`): +// y, n = f(x, m) +// +// Now, let's assume that the reverse of f (called f') needs to use values of `x`, `t` and `y`. +// `t` is an intermediate value produced in the body of f, and let's assume that it requires +// grad too. +// +// In this case differentiate(f) will return this: +// y, n, t = f(x, m) // `t` is appended to the output list +// dx = f'(dy, dt, x, t, y) // No `dm` or `dn` because they do not require gradient +// // All needed values from f are prepended to the input list +// +// f_real_outputs = 2 // Only first two outputs were present in f originally +// df_input_vjps = {0, 2} // i.e. connect grad_fn of y and t variables produced by f, +// y t // with y's output_nr = 0 and t's output_nr = 1 +// df_input_captures = {I0, O2, O0} // Order matches the prefix of inputs to df +// x t y +// df_output_vjps = {0} // i.e. connect next_edge[0] of grad_fn to x's (grad_fn, output_nr). +// +// Terminology: vjp = vector-jacobian product +// clang-format on + +struct Gradient { + explicit operator bool() const { + return df != nullptr; + } + std::shared_ptr f; + std::shared_ptr df; + + // Describes how to construct outputs of f from what its graph will return. + // This is necessary because some trailing outputs are intermediates produced + // only to be saved for df (and should be ignored). + size_t f_real_outputs = 0; // initialized for safety. + + // df inputs are split into two sections: vjps (aka grad_outputs) and + // captures. VJPs are "seeds" for the gradient computation given for each + // input capture of an Output kind. Captures are values the need to be saved + // when f is run. We handle inputs specially, because this allows us to avoid + // adding extra vjps as df inputs. + + std::vector df_input_vjps; // Offsets into f's outputs. + // capture can come from inputs or outputs + std::vector df_input_captured_inputs; // Offsets into f's inputs + std::vector df_input_captured_outputs; // Offsets into f's outputs + + // df will produce vjps for a subset of inputs of f that required grad. + // df_output_vjps[idx] == inp_idx means that idx-th output of df produces a + // vjp for inp_idx-th input of f. + std::vector df_output_vjps; // Offsets into f's inputs. + + // How to use gradient to implement a differentiable autograd function: + // When running f: + // - Unwrap input Variables + // - Run f's graph + // - Create grad_fn + // - Wrap outputs in Variables (assume we have a tensor_outputs array): + // outputs = map(Variable, tensor_output) + // for i, offset in enumerate(df_input_vjps): + // outputs[offset].set_grad_fn(grad_fn, output_nr=i) + // - Use df_output_vjps to connect next_edges of grad_fn: + // for idx in df_output_vjps: + // grad_fn.add_next_edge(inputs[idx].gradient_edge()) + // - Save captures for df (care needs to be taken to use SavedVariables for + // inputs and outputs that we will actually return) + // - Return outputs[:f_real_outputs] + // + // When running df: + // - Concatenate received vjps and captured Variables + // - Interpret df + // - Wrap outputs of df into Variables (that don't require grad) +}; +TORCH_API Gradient differentiate(std::shared_ptr& graph); + +// can we take a derivative of this node symbolically? +TORCH_API bool isDifferentiable(const Node* n); +TORCH_API bool isDifferentiable(Graph& g); +TORCH_API bool isZero(Value* v); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/calculate_necessary_args.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/calculate_necessary_args.h new file mode 100644 index 0000000000000000000000000000000000000000..e1aff151f35e421e1d06be6de259953b83c23ba1 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/calculate_necessary_args.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +// Calculates the number of args that need to be passed in. +// Less args may be needed if defaults are provided. +// Returns: {number args needed, number of out args} +inline std::pair CalculateNecessaryArgs( + const std::vector& schema_args, + at::ArrayRef actual_inputs, + bool allow_trailing_out_args) { + if (schema_args.empty()) { + return std::make_pair(0, 0); + } + + // count number of out arguments + int64_t schema_idx = static_cast(schema_args.size()) - 1; + if (allow_trailing_out_args) { + // skip over out arguments in the end. + while (schema_idx >= 0) { + const auto& current_arg = schema_args.at(schema_idx); + if (!current_arg.is_out()) { + break; + } + schema_idx--; + } + } + + int64_t num_out = static_cast(schema_args.size()) - schema_idx - 1; + + if (schema_args.size() < actual_inputs.size()) { + return std::make_pair(actual_inputs.size(), num_out); + } + + // if it is the default args, we reset the index to the last element + if (!allow_trailing_out_args) { + schema_idx = schema_args.size() - 1; + } + // keeps track of trailing unnecessary args + while (schema_idx >= 0) { + // this means it is not default argument, so it is necessary + if (!schema_args.at(schema_idx).default_value().has_value()) { + return std::make_pair(schema_idx + 1, num_out); + } else { + auto schema_value = + schema_args.at(schema_idx).default_value().value().toIValue(); + // non-const value will become nullptr here, so will be marked necessary + // non-const would include prim::ListConstruct, prim::DictConstruct as + // well. + auto actual_value = toIValue(actual_inputs[schema_idx]); + if (!actual_value.has_value()) { + return std::make_pair(schema_idx + 1, num_out); + } + // if the IR has same value as default value of the schema, + // it is not necessary argument. + if (schema_value != actual_value.value()) { + return std::make_pair(schema_idx + 1, num_out); + } + } + schema_idx--; + } + return std::make_pair(0, num_out); +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/custom_operator.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/custom_operator.h new file mode 100644 index 0000000000000000000000000000000000000000..faa8c90754a0e59a629be161c1c150bacdb7b7e7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/custom_operator.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +/// Registration class for new operators. Effectively calls +/// `torch::jit::registerOperator` for every supplied operator, but allows doing +/// so in the global scope when a `RegisterOperators` object is assigned to a +/// static variable. +/// Note: This is *not* the custom operator API. If you want to register custom +/// operators, take a look at torch::RegisterOperators. +struct TORCH_API RegisterOperators { + RegisterOperators() = default; + + /// Registers a vector of already created `Operator`s. + /// The operator element is now optional to filter null ops. It's backward + /// compatible and works for selective operator registration. + explicit RegisterOperators(std::vector> operators) { + for (std::optional& o : operators) { + if (o) { + registerOperator(std::move(o.value())); + } + } + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..59f5aa796f76cf4080135be5de9c83488035f23d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry.h @@ -0,0 +1,33 @@ +#pragma once +// This file is temporary until native_functions.yaml and derivatives.yaml are +// merged. Ideally this should all go into native_functions.yaml + +#include +#include + +namespace torch::jit { + +TORCH_API std::optional> GetDecomposition( + const FunctionSchema& schema); + +TORCH_API void RegisterDecomposition( + const FunctionSchema& schema, + std::shared_ptr g); + +TORCH_API void RunDecompositions(std::shared_ptr g); + +TORCH_API std::optional GetDecompositionFunction( + const FunctionSchema& schema); + +// For invocation in C++, recommended is to assign to static local variable +TORCH_API Function* GetDecompositionExecutor(const char* schema_literal); + +TORCH_API Function* GetDecompositionExecutor(const FunctionSchema& schema); + +TORCH_API void run_jit_decomposition( + const c10::OperatorHandle& op, + torch::jit::Stack* stack); + +TORCH_API bool has_jit_decomposition(const FunctionSchema& schema); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry_util.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry_util.h new file mode 100644 index 0000000000000000000000000000000000000000..08b5750957b2ae31deacaaae0deae35473c91fce --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/decomposition_registry_util.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API const std::string& GetSerializedDecompositions(); + +TORCH_API const OperatorMap& GetDecompositionMapping(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/exception_message.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/exception_message.h new file mode 100644 index 0000000000000000000000000000000000000000..e3f00272a999f3d9431528db7d8e74ff0cc3d823 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/exception_message.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace torch::jit { + +struct ExceptionMessage { + ExceptionMessage(const std::exception& e) : e_(e) {} + + private: + const std::exception& e_; + friend std::ostream& operator<<( + std::ostream& out, + const ExceptionMessage& msg); +}; + +inline std::ostream& operator<<( + std::ostream& out, + const ExceptionMessage& msg) { + auto c10_error = dynamic_cast(&msg.e_); + if (c10_error) { + out << c10_error->what_without_backtrace(); + } else { + out << msg.e_.what(); + } + return out; +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..8295b9d6c378818db0283e627d703e2c037fcd68 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor.h @@ -0,0 +1,148 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +TORCH_DECLARE_bool(torch_jit_enable_new_executor); + +TORCH_DECLARE_bool(torch_jit_execution_plan_reuse_code_graph); + +namespace torch::jit { +struct GraphExecutorState; +struct Code; + +enum ExecutorExecutionMode { + SIMPLE, + PROFILING, +}; + +struct ExecutionPlan { + ExecutionPlan() = default; + ExecutionPlan(std::shared_ptr graph, std::string function_name) + : code(graph, std::move(function_name)), + graph( + FLAGS_torch_jit_execution_plan_reuse_code_graph + ? code.graph() + : std::move(graph)) {} + + operator bool() const { + return static_cast(graph); + } + + Code code; + std::shared_ptr graph; +}; + +// Notice that those structs don't manage lifetime of their members. +// They are only valid only right after you call getDebugState() and should +// never be used again once another GraphExecutor function is called. + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct GraphExecutorState { + const Graph* graph = nullptr; + ExecutionPlan fallback; // XXX: members of this field are optional + std::unordered_map execution_plans; +}; + +struct TORCH_API EnableProfilingGuard { + EnableProfilingGuard(); + ~EnableProfilingGuard(); + + private: + bool old_executor_mode = false; + bool old_get_optimize = false; +}; + +struct GraphExecutorImplBase; +struct TORCH_API GraphExecutor { + GraphExecutor() = default; + GraphExecutor(const std::shared_ptr& graph, std::string function_name); + + GraphExecutor( + const std::shared_ptr& graph, + std::string function_name, + ExecutorExecutionMode executor_mode); + + void run(Stack& inputs); + c10::intrusive_ptr runAsync( + Stack& stack, + TaskLauncher taskLauncher = at::launch); + + // `remaining_bailout_depth` stands for the maximum number of profiled and + // specialized recompilations allowed for the current `GraphExecutor`. if + // remaining_bailout_depth is equal to 0, `GraphExecutor` won't perform any + // profiling and specialization. This is also equivalent to the + // SIMPLE_EXECUTOR mode. if remaining_bailout_depth is greater than 0, + // `GraphExecutor` will profile and specialize its input graph based on the + // profiled information whenever a bailout check is failed/triggered, a new + // `GraphExecutor` will be created. This new `GraphExecutor`'s + // remaining_bailout_depth will be reduced by 1. + // If no bailout depth is passed, the depth will be initialized from the + // current global fusion strategy settings. + const ExecutionPlan& getPlanFor( + Stack& inputs, + std::optional remaining_bailout_depth = std::nullopt); + GraphExecutorState getDebugState(); + + void debugFlushCompilationCache(); + + bool isOptimized() const; + + private: + std::shared_ptr pImpl; +}; + +TORCH_API Node* replaceBlockWithFallbackGraph( + Block* b, + ArrayRef inputs); + +// These passes need to run before it is valid to pass to the interpreter +// regardless of whether sizes have been specialized or not. +TORCH_API void runRequiredPasses(const std::shared_ptr& g); + +TORCH_API void debugSetFusionGroupInlining(bool state); +TORCH_API bool getFusionGroupInlining(); + +TORCH_API void debugSetAutodiffSubgraphInlining(bool state); +TORCH_API std::shared_ptr lastExecutedOptimizedGraph(); + +TORCH_API std::atomic& getProfilingMode(); +TORCH_API std::atomic& getExecutorMode(); +TORCH_API std::atomic& getNumProfiledRuns(); +TORCH_API size_t getBailoutDepth(); +TORCH_API bool IsNewExecutorEnabled(); + +struct TORCH_API GraphOptimizerEnabledGuard { + GraphOptimizerEnabledGuard(bool state) + : old_state_(getGraphExecutorOptimize()) { + setGraphExecutorOptimize(state); + } + + ~GraphOptimizerEnabledGuard() { + setGraphExecutorOptimize(old_state_); + } + + bool old_state_; +}; + +namespace detail { + +GraphExecutor* getGradExecutor(Operation& op); + +GraphExecutor* getDifferentiableGraphOpExecutor(Operation& op); + +// for debugging information we expose a way to get the last actually +// run graph. Previous approaches allowed querying the GraphExecutor +// for what graph it would run in certain circumstances (graphFor), but +// this is fragile because we sometimes change how these decisions are made. +// This interface still allows our tests to look at optimized graphs, but +// with less plumbing. +} // namespace detail + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor_impl.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..70069ac1907b0f54fa425ceecee20d48801b13ed --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_executor_impl.h @@ -0,0 +1,113 @@ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +void packGradient(const Gradient& gradient, Node* dnode); +bool needsGradient(const std::shared_ptr& graph); +void runOptimization( + std::shared_ptr& graph, + bool unroll_non_constant_loops = true, + bool const_prop_user_classes = true); +void runNondiffOptimization( + std::shared_ptr& graph, + bool strict_fuser_check = false); +void debugSetAutodiffSubgraphInlining(bool state); +bool TORCH_API getAutodiffSubgraphInlining(); + +void debugSetFusionGroupInlining(bool state); +bool getFusionGroupInlining(); + +// Tunable parameters for deciding when to create/keep subgraphs of +// differentiable code +const size_t autodiffSubgraphNodeThreshold = 2; +const size_t autodiffSubgraphInlineThreshold = 5; + +// a Graph can be created via tracing, or via a language-based frontend +// GraphExecutor runs it. It can run the same graph on many different sizes +// and different requires_grad states, and handles specializations for each +// situation. GraphExecutor is completely unaware of tracing or module +// parameters to keep the tracing concerns separated. +struct GraphExecutorImplBase { + static std::shared_ptr prepareGraph( + const std::shared_ptr& graph) { + auto copy = graph->copy(); + EraseShapeInformation(copy); + return copy; + } + + GraphExecutorImplBase( + const std::shared_ptr& graph, + std::string function_name) + : graph(prepareGraph(graph)), + function_name_(std::move(function_name)), + num_inputs(this->graph->inputs().size()), + num_outputs(this->graph->outputs().size()) {} + + // entry point where execution begins + void run(Stack& stack); + c10::intrusive_ptr runAsync( + Stack& stack, + TaskLauncher taskLauncher = at::launch); + + virtual const ExecutionPlan& getPlanFor( + Stack& stack, + std::optional remaining_bailout_depth = std::nullopt) = 0; + virtual GraphExecutorState getDebugState() = 0; + virtual ~GraphExecutorImplBase() = default; + + virtual bool isOptimized() const { + return false; + } + + protected: + friend struct GraphExecutor; + + // The unoptimized starting graph. This field is effectively const, but we + // can't make it so because Graph::copy() is not const (and making it const is + // not that easy at this point). + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + std::shared_ptr graph; + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + std::string function_name_; + + // If false, we'll run the graph as we get it, without any optimizations. + // Useful for debugging. + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + const size_t num_inputs; + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + const size_t num_outputs; + + // GraphExecutors can be accessed from multiple threads, so this thread needs + // to be held every time we access the fallback or plan_cache. + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + std::mutex compile_mutex; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_iterator.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..c008902a2e8f71f1cb9eb2ce58b250971f488b50 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/graph_iterator.h @@ -0,0 +1,147 @@ +#include + +namespace torch::jit { + +// This class facilitates depth-first iteration over all nodes in a graph. +class DepthFirstGraphNodeIterator { + Node* current_; + + public: + // Constructor. + explicit DepthFirstGraphNodeIterator(std::shared_ptr& graph) + : current_(*(graph->block()->nodes().begin())) {} + + // Moves up and to the next node (may move up recursively). + void move_up() { + if (current_ == nullptr) { + return; + } + // Basically we start from the child block (which is current_) + // and we try to find the block that owns it. Now we need to check + // if that block is the graph root block, or if it is an If/Loop/etc + // block. + // + // If it's the graph root block we can stop because there is no "up" + // but if it is a node (e.g. If/Loop/etc) we need to apply logic + // based on where we are coming from to move to the next block. + // This might mean that we need to traverse up again (e.g. if we've + // reached the end of the else clause in an if block we need to go) + // up to the parent block that contains the if. + // + // Similarly if we've reached the end of the parent block containing + // the else clause we might need to go up again so this is a recursive + // function. + // + // BlockNode (if/loop/with) + // | + // [Block1] ... [Block2] + // | + // [ Node1, Node2, Node3, FromNode] + // + auto parent_block = current_->owningBlock(); + TORCH_INTERNAL_ASSERT(parent_block, "Every node must be owned by a block"); + + // Get the node that owns the parent block. This node has to be an if, + // loop, or with. + auto parent_node = parent_block->owningNode(); + if (parent_node == nullptr) { + // If there's no node that owns this current block then we're at the + // top of the graph and since we're trying to move up we have reached + // the end of the traversal. + current_ = nullptr; + return; + } + + // Check the type of node this root is. + if (parent_node->kind() == prim::If) { + // Need to check if we came from the `then` branch or the `else` branch. + auto* then_block = parent_node->blocks().at(0); + auto* else_block = parent_node->blocks().at(1); + + if (parent_block == else_block) { + // If else block then we move to the next node in the parent block. + current_ = parent_node->next(); + if (current_->kind() == prim::Return) { + move_up(); + } + } else { + // If then block then move to the else block if it is not empty. + TORCH_INTERNAL_ASSERT(parent_block == then_block); + bool else_block_empty = + else_block->nodes().begin() == else_block->nodes().end(); + + if (!else_block_empty) { + current_ = *(else_block->nodes().begin()); + } else { + // Since it's empty we move to the next node. + current_ = parent_node->next(); + if (current_->kind() == prim::Return) { + move_up(); + } + } + } + } else if ( + parent_node->kind() == prim::Loop || + parent_node->kind() == prim::With) { + current_ = parent_node->next(); + if (current_->kind() == prim::Return) { + move_up(); + } + } else { + TORCH_INTERNAL_ASSERT( + false, "Only if/loop/with nodes should have child blocks"); + } + } + + // Moves to the next adjacent node or up in to the parent if that is not + // possible. + void move_next() { + if (current_ == nullptr) { + return; + } + + // Increment to the next node in the current block. + current_ = current_->next(); + + // Check if we're at the end of the block. If so we need + // to move upwards (if it makes sense to). + if (current_->kind() == prim::Return) { + move_up(); + } + } + + // Moves to the next node in the graph into children if it can. + void move_into() { + if (current_ == nullptr) { + return; + } + + // Check if we're currently on a node that contains sub-nodes. + if (current_->kind() == prim::If || current_->kind() == prim::Loop || + current_->kind() == prim::With) { + auto* first_block = current_->blocks().at(0); + current_ = first_block->param_node(); + // Move next will move up and out of the current node if the block is + // empty. `move_up` which is called by `move_next` will handle the + // difference between If, Loop, and With blocks appropriately. + move_next(); + } else { + move_next(); + } + } + + // Get the next Node in the graph. \returns nullptr if there are no nodes + // left. + Node* next() { + auto result = current_; + + // Try move into the existing node to set the next node to be returned. + // This will move to the next node if not possible, or move upwards and + // to the next. + move_into(); + + return result; + } +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/instruction.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/instruction.h new file mode 100644 index 0000000000000000000000000000000000000000..73c78adbda03e5bd307979a28abcc648050c8a03 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/instruction.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { +// instruction look like: +// op_code X, N +// meaning of X, N depend on the op: +// O - index into operator table +// R - index into register table +// I - literal integer +// C - index into constant table +// P - jump offset relative to beginning of current instruction +// F - index into function table +// T - index into the type table, used for guard instructions +// S - index into object slots +// C - index into code table + +#define FORALL_OPCODES(_) \ + _(OP, "O") /* invoke operator X */ \ + _(OPN, "OI") /* invoke vararg operator X with N arguments */ \ + _(LOAD, "R") /* push a value from a register X */ \ + _(MOVE, "R") /* push a value from register X, clearing the register */ \ + _(STOREN, "RI") /* store N values to registers [X, X+N) */ \ + _(STORE, "R") /* store 1 value to registers X */ \ + _(DROP, "") /* drop 1 value from the top of the stack */ \ + _(DROPR, "R") /* clear register X */ \ + _(LOADC, "C") /* push the constant X */ \ + _(JF, "P") /* pop the top of the stack, if false, branch to P */ \ + _(JMP, "P") /* unconditional branch to X */ \ + _(LOOP, "PI") /* perform a loop, X is where to branch if cond is false */ \ + _(RET, "") /* exit execution */ \ + _(WAIT, "") /* wait for a future to be complete */ \ + _(CALL, "F") /* call function X */ \ + _(GUARD, "T") /* check a guard against type_table, true if passes */ \ + _(TYPECHECK, "TN") /* check each type of input[i] against type_table[X+N] */ \ + _(FAIL_GUARD, "T") /* fail a guard, patch back to GUARD */ \ + _(PROFILE_OP, "F") /* get a callback from profile_function_table at X */ \ + _(TAIL_CALL, "F") /* replace current frame with function F */ \ + _(INTERFACE_CALL, "CI") /* call method X on the first argument (of N) */ \ + _(GET_ATTR, "S") /* get attribute from slot X in an Object */ \ + _(SET_ATTR, "S") /* set attribute to slot X in an Object */ \ + _(LIST_UNPACK, "I") /* unpack list expecting length I */ \ + _(TUPLE_CONSTRUCT, "I") /* construct a tuple using X inputs */ \ + _(NAMED_TUPLE_CONSTRUCT, \ + "TI") /* construct a tuple of type X, using N inputs */ \ + _(LIST_CONSTRUCT, "TI") /* construct a list of type X, using N inputs */ \ + _(DICT_CONSTRUCT, "TI") /* construct a dict of type X, using N inputs */ \ + _(CREATE_OBJECT, "T") /* create an object of type X */ \ + _(ISINSTANCE, "TI") /* check object is one of types[X:X+N] */ \ + _(TUPLE_SLICE, "II") /* slice tup[X:(X+N)] */ \ + _(TUPLE_INDEX, "") /* get the value from a tuple at that index */ \ + _(RAISE_EXCEPTION, "") /* throws the exception from Python */ \ + _(DICT_INDEX, "") /* gets the value from the dict for given key */ \ + _(UNCHECKED_CAST, "") /* perform an unchecked cast operation */ \ + _(__IS__, "") /* performs `is` operator from Python */ \ + _(UN_INITIALIZED, \ + "") /* sets default values to variables that are uninitialized */ \ + _(__ISNOT__, "") /* performs `is not` operator from Python */ \ + _(FORMAT, "I") /* performs string format function `f strings` or `{}.format` \ + the number of inputs in stored in X */ \ + _(DEVICE, "") /* invokes aten::device for a Tensor */ \ + _(DTYPE, "") /* invokes aten::dtype for a Tensor */ \ + _(DIM, "") /* invokes aten::dim for a Tensor */ \ + _(__NOT__, "") /* performs `not` operator from Python */ \ + _(TO_LIST, "") /* convert the input to a list */ \ + _(NUM_TO_TENSOR, \ + "") /* performs the conversion of a number/scalar to Tensor */ \ + _(IS_CUDA, "") /* invokes aten::is_cuda for a Tensor */ \ + _(FORK, "CN") /* launch a thread to run code entry x with N inputs */ \ + _(WARN, "I") /* emit a warning with line information */ \ + _(ENTER, "EN") /* enter scope of a contextmanager */ \ + _(EXIT, "EX") /* exit the last entered contextmanager */ \ + _(AWAITABLE, "CN") /* initialize await for code entry x with N inputs */ + +enum OpCode : uint8_t { +#define DEFINE_OP(op, _) op, + FORALL_OPCODES(DEFINE_OP) +#undef DEFINE_OP +}; + +struct Instruction { + OpCode op; + uint8_t unused; + uint16_t N; + int32_t X; + // TODO: check for overflow + Instruction(OpCode op, int32_t X, uint16_t N) + : op(op), unused(0), N(N), X(X) {} +}; +std::ostream& operator<<(std::ostream& out, Instruction inst); + +bool isOpSupportedInMobile(OpCode op); +char const* toString(OpCode op); +OpCode parseOpCode(const char* str); +std::ostream& operator<<(std::ostream& out, Instruction inst); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/interpreter.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/interpreter.h new file mode 100644 index 0000000000000000000000000000000000000000..e6a71dc0a0b953dbdcaadcb7ffe2c39ad7ad19cf --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/interpreter.h @@ -0,0 +1,161 @@ +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include + +TORCH_DECLARE_bool(torch_jit_disable_warning_prints); +TORCH_DECLARE_bool(torch_jit_enable_rethrow_caught_exception); + +namespace at { +class Tensor; +TORCH_API void launch(std::function func); +} // namespace at +namespace c10 { +struct IValue; +struct OperatorName; +} // namespace c10 + +namespace torch::jit { + +// The interpreter run Graphs with Tensor inputs and Tensor outputs +// a separate component in the autograd handles unwrapping and wrapping +// variable objects for use in the interpreter. +namespace interpreter { +struct CodeImpl; +} + +struct Node; +struct GraphExecutor; +struct InterpreterStateImpl; +struct Graph; +struct Node; +struct Instruction; +using Stack = std::vector; +using c10::ivalue::Future; +using TaskLauncher = std::function)>; + +bool TORCH_API in_torchscript_runtime(); + +struct TORCH_API Code { + Code() = default; + explicit Code(interpreter::CodeImpl* pImpl); + // remaining_bailout_depth is irrelevant in a `Code` object unless the `Code` + // is directly created by `GraphExecutor` in which case it's likely to contain + // `prim::BailOut`s to control the maximum depth of bailout chains + explicit Code( + const std::shared_ptr& graph, + std::string function_name, + size_t remaining_bailout_depth = 0); + + const std::vector& grad_executors(); + const std::vector& diff_graph_op_executors(); + + explicit operator bool() const { + return pImpl != nullptr; + } + size_t num_inputs() const; + size_t num_outputs() const; + size_t num_bailouts() const; + const std::vector& constant_table() const; + const std::vector& type_table() const; + const std::vector& instructions() const; + const std::unordered_map& op_to_num_specified_args() + const; + const std::vector& instructions_source() const; + void request_bailout(size_t index); + size_t register_size() const; + std::shared_ptr graph() const; + + private: + std::shared_ptr pImpl; + friend struct InterpreterStateImpl; + friend std::ostream& operator<<(std::ostream& out, const Code& code); +}; + +struct TORCH_API MobileCode : Code { + explicit MobileCode( + const std::shared_ptr& graph, + std::string function_name, + bool emit_default_input_instructions = true, + bool support_default_args_before_out = true, + bool emit_promoted_ops = true, + size_t remaining_bailout_depth = 0); +}; + +struct InterpreterState { + TORCH_API InterpreterState( + const Code& code, + TaskLauncher taskLauncher = at::launch); + TORCH_API void run(Stack& stack); + TORCH_API c10::intrusive_ptr runAsync(Stack& stack); + c10::intrusive_ptr getFuture(); + + private: + InterpreterState(c10::intrusive_ptr pImpl); + // Ideally we should use c10::intrusive_ptr for pImpl; + // but intrusive_ptr requires full definition of InterpreterStateImpl, + // which we need to hide in the header. + c10::intrusive_ptr pImpl; + friend struct InterpreterStateImpl; +}; + +// Created by wait() +struct Suspend : public std::exception { + const char* what() const noexcept override { + return "Suspend"; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + explicit Suspend(c10::intrusive_ptr future_) + : future(std::move(future_)) {} + + c10::intrusive_ptr future; +}; + +// InterpreterContinuation propagates dist_autograd_context_id +// through (and only through) the forward pass manually, other +// thread local settings are propagated with ThreadLocalState +struct InterpreterContinuation { + InterpreterContinuation( + InterpreterState state_, + Stack stack_, + int64_t dist_autograd_context_id = 0, + std::optional tls_state = std::nullopt) + : state(std::move(state_)), + stack(std::move(stack_)), + tls_state_(std::move(tls_state)) +#ifdef USE_DISTRIBUTED + , + dist_autograd_context_id_(dist_autograd_context_id) +#endif + { + } + + void operator()(); + + private: + InterpreterState state; + Stack stack; + std::optional tls_state_ = std::nullopt; +#ifdef USE_DISTRIBUTED + int64_t dist_autograd_context_id_; +#endif +}; + +// what is the tensors type, including state from the current execution context +// that modifies how the tensor behaves. For instance if no_grad is enabled +// this will cause the TensorType to have requires_grad=False. +TORCH_API at::TensorTypePtr tensorTypeInCurrentExecutionContext( + const at::Tensor& t); + +// current (TLS) TorchScript interpreter callstack +TORCH_API std::vector currentCallstack(); +TORCH_API std::vector currentModuleHierarchy(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_exception.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_exception.h new file mode 100644 index 0000000000000000000000000000000000000000..cb4f572a8bd3c0b0e076143a36c6e2af7a4885f8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_exception.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include +#include + +namespace torch::jit { + +struct TORCH_API JITException : public std::runtime_error { + explicit JITException( + const std::string& msg, + std::optional python_class_name = std::nullopt, + std::optional original_msg = std::nullopt); + + std::optional getPythonClassName() const { + return python_class_name_; + } + + // the original msg if this is from a python exception. The interpretor has + // changed the original message by adding "The following operation failed in + // the TorchScript interpreter." in front of it in the handleError function. + std::optional getOriginalMsg() const { + return original_msg_; + } + + static const std::string& getCaughtOriginalMsg(); + static const std::string& getCaughtPythonClassName(); + static void setCaughtOriginalMsg(const std::string& msg); + static void setCaughtPythonClassName(const std::string& pythonClassName); + + private: + std::optional python_class_name_; + std::optional original_msg_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_trace.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..9b29501eeb3f91d3373f79bf8cded1605d7db8fc --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/jit_trace.h @@ -0,0 +1,8 @@ +#include +#include + +namespace torch::jit { +TORCH_API std::shared_ptr TraceGraph( + const std::shared_ptr& graph, + Stack& stack); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/logging.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/logging.h new file mode 100644 index 0000000000000000000000000000000000000000..44561a8006cfdfd1751fb44172572429ccd39f77 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/logging.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace torch::jit::logging { + +class LoggerBase { + public: + TORCH_API virtual void addStatValue( + const std::string& stat_name, + int64_t val) = 0; + virtual ~LoggerBase() = default; +}; + +TORCH_API LoggerBase* getLogger(); +TORCH_API LoggerBase* setLogger(LoggerBase* logger); + +// No-op logger. This is the default and is meant to incur almost no runtime +// overhead. + +class NoopLogger : public LoggerBase { + public: + void addStatValue( + const std::string& stat_name [[maybe_unused]], + int64_t val [[maybe_unused]]) override {} + ~NoopLogger() override = default; +}; + +// Trivial locking logger. Pass in an instance of this to setLogger() to use it. +// This keeps track of the sum of all statistics. +// +// NOTE: this is not written in a scalable way and should probably only be used +// in the single-threaded case or for testing. +class TORCH_API LockingLogger : public LoggerBase { + public: + void addStatValue(const std::string& stat_name, int64_t val) override; + virtual int64_t getCounterValue(const std::string& name) const; + enum class AggregationType { SUM = 0, AVG = 1 }; + void setAggregationType(const std::string& stat_name, AggregationType type); + ~LockingLogger() override = default; + + private: + mutable std::mutex m; + struct RawCounter { + RawCounter() : sum(0), count(0) {} + int64_t sum; + size_t count; + }; + std::unordered_map raw_counters; + std::unordered_map agg_types; +}; + +// Make this struct so the timer internals are opaque to the user. +struct JITTimePoint { + std::chrono::time_point point; +}; + +TORCH_API JITTimePoint timePoint(); +TORCH_API void recordDurationSince( + const std::string& name, + const JITTimePoint& tp); + +namespace runtime_counters { +constexpr const char* GRAPH_EXECUTORS_CONSTRUCTED = + "pytorch_runtime.graph_executors_constructed"; +constexpr const char* GRAPH_EXECUTOR_INVOCATIONS = + "pytorch_runtime.graph_executor_invocations"; +constexpr const char* EXECUTION_PLAN_CACHE_HIT = + "pytorch_runtime.execution_plan_cache_hit"; +constexpr const char* EXECUTION_PLAN_CACHE_MISS = + "pytorch_runtime.execution_plan_cache_miss"; + +inline std::vector allRuntimeCounters() { + return { + GRAPH_EXECUTORS_CONSTRUCTED, + GRAPH_EXECUTOR_INVOCATIONS, + EXECUTION_PLAN_CACHE_HIT, + EXECUTION_PLAN_CACHE_MISS}; +} + +} // namespace runtime_counters + +} // namespace torch::jit::logging diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator.h new file mode 100644 index 0000000000000000000000000000000000000000..2e609f18ecc07489863ba59518bb4af558f9c7b7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator.h @@ -0,0 +1,345 @@ +// in memory description of all ATen Ops similar to Caffe2 schema +// once C10 exists this can be removed, or stubbed out, but we need +// it now to implement correct semantic checking for script +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +struct Node; +using ::c10::Argument; +using ::c10::FunctionSchema; +using ::c10::Symbol; + +using OperationCreator = Operation (*)(const Node*); + +namespace { +const std::array kJitOnlyOperatorTags = { + at::Tag::pt2_compliant_tag}; +} + +/* + * Note: JIT relies on Operator instances having static lifetime, because + * it for example stores a non-owning FunctionSchema* pointer in the Node class, + * which points to the function schema stored in the Operator instance. + * Also, jit::Operator is meant to store more operator related information like + * symbolic derivatives, which also requires them to have static lifetime + * so that changes to symbolic derivatives are remembered. + * + * Currently, the JIT operator library contains a jit::Operator instance + * with a wrapper for each c10 operator. The c10 operator library registers + * those wrappers using listeners in register_c10_ops.cpp. + * TODO Instead of doing it this way, we should only have pure-jit ops in + * the jit library but have the JIT operator lookup look into the c10 library + * too. + */ + +// An Operator is a thin wrapper around either a pure JIT operator (e.g. prim +// ops) or a c10 operator, allowing some common operations and abstracting away +// the concrete operator nature. +struct TORCH_API Operator { + private: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + struct C10Operator final { + c10::OperatorHandle handle_; + Operation op_; + }; + struct UnparsedFunctionSchema final { + std::string schema_string_; + mutable std::optional alias_analysis_; + }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + struct JitOnlyOperator final { + // The only valid transition for schema_ is from right->left, i.e. + // when the schema gets parsed. + mutable std::variant schema_; + + std::variant op_; + }; + + public: + Operator(c10::OperatorHandle opHandle, Operation operation) + : op_(C10Operator{std::move(opHandle), std::move(operation)}) {} + + Operator( + std::string schema, + Operation op, + c10::AliasAnalysisKind alias_analysis) + : op_(JitOnlyOperator{ + UnparsedFunctionSchema{std::move(schema), alias_analysis}, + Operation(std::move(op))}) {} + + Operator( + std::string name, + std::string overload_name, + std::vector arguments, + std::vector returns, + Operation op, + c10::AliasAnalysisKind alias_analysis) + : op_(JitOnlyOperator{ + FunctionSchema(varArgSchemaWithName( + std::move(name), + std::move(overload_name), + std::move(arguments), + std::move(returns), + alias_analysis)), + std::move(op)}) {} + + Operator( + std::string schema, + OperationCreator op_creator, + c10::AliasAnalysisKind alias_analysis) + : op_(JitOnlyOperator{ + UnparsedFunctionSchema{std::move(schema), alias_analysis}, + op_creator}) {} + + // Helper constructor to register `op` to run + // run for _every_ IR Node where n.kind() == name, regardless of arguments. + // This is accomplished by marking the schema varargs and having no required + // arguments. + Operator( + Symbol name, + OperationCreator op_creator, + c10::AliasAnalysisKind alias_analysis) + : op_(JitOnlyOperator{ + FunctionSchema(varArgSchemaWithName(name, alias_analysis)), + op_creator}) {} + + Operation getOperation(const Node* node = nullptr) const { + return std::visit( + c10::overloaded( + [](const C10Operator& op) { return op.op_; }, + [node](const JitOnlyOperator& op) { + return std::visit( + c10::overloaded( + [](const Operation& op) { return op; }, + [node](const OperationCreator& op_creator) { + return op_creator(node); + }), + op.op_); + }), + op_); + } + + Operation getOperationForDispatchKey(c10::DispatchKey dk) const { + // TODO: some sort of caching mechanism? + return std::visit( + c10::overloaded( + [dk](const C10Operator& op) { + return Operation([op, dk](Stack& stack) { + op.handle_.callBoxedForDispatchKey(dk, stack); + }); + }, + [](const JitOnlyOperator& op) { + TORCH_CHECK( + false, + "calling a JIT operator for dispatch key is not supported"); + return Operation(nullptr); + }), + op_); + } + + const FunctionSchema& schema() const { + return std::visit( + c10::overloaded( + [](const C10Operator& op) -> const FunctionSchema& { + return op.handle_.schema(); + }, + [](const JitOnlyOperator& op) -> const FunctionSchema& { + // we lazily parse schema initialized from strings so that + // we do less work during static operator registration + if (op.schema_.index() == 1) { + auto& unmaterializedSchema = + std::get(op.schema_); + FunctionSchema schema = + parseSchema(unmaterializedSchema.schema_string_); + if (unmaterializedSchema.alias_analysis_.has_value()) { + // TODO What if it gets set later? + schema.setAliasAnalysis( + *unmaterializedSchema.alias_analysis_); + } + op.schema_ = std::move(schema); + } + return std::get(op.schema_); + }), + op_); + } + + c10::ArrayRef getTags() const { + return std::visit( + c10::overloaded( + [](const C10Operator& op) { return op.handle_.getTags(); }, + [](const JitOnlyOperator& op) { + // JitOnlyOperators don't have an c10::OperatorHandle or a way to + // specify tags. We're grandfathering them all into + // pt2_compliant_tag, but for anything else, please just stop + // using JitOnlyOperator. + return c10::ArrayRef(kJitOnlyOperatorTags); + }), + op_); + } + + bool isC10Op() const { + return op_.index() == 0; + } + + c10::AliasAnalysisKind aliasAnalysisKind() const { + const FunctionSchema& schemaRef = schema(); + c10::AliasAnalysisKind alias_analysis = schemaRef.aliasAnalysis(); + + TORCH_CHECK( + alias_analysis == AliasAnalysisKind::FROM_SCHEMA || + !schemaRef.hasAnyAliasInfo(), + "In operator registration: Tried to register operator ", + schemaRef, + " with aliasing information in the schema but without AliasAnalysisKind::FROM_SCHEMA."); + return alias_analysis; + } + + bool hasOperation() const { + return std::visit( + c10::overloaded( + [](const C10Operator&) { return true; }, + [](const JitOnlyOperator& op) { return op.op_.index() == 0; }), + op_); + } + + private: + static FunctionSchema varArgSchemaWithName( + Symbol name, + AliasAnalysisKind alias_analysis) { + auto result = FunctionSchema( + name, + "", + {}, + {}, + /*is_vararg*/ true, + /*is_varret*/ true); + result.setAliasAnalysis(alias_analysis); + return result; + } + + static FunctionSchema varArgSchemaWithName( + std::string name, + std::string overload_name, + std::vector arguments, + std::vector returns, + AliasAnalysisKind alias_analysis) { + auto result = FunctionSchema( + std::move(name), + std::move(overload_name), + std::move(arguments), + std::move(returns), + /*is_vararg*/ false, + /*is_varret*/ false); + result.setAliasAnalysis(alias_analysis); + return result; + } + + std::variant op_; +}; + +TORCH_API std::string canonicalSchemaString(const FunctionSchema& schema); + +TORCH_API const std::vector> getAllOperators(); +TORCH_API const std::vector>& getAllOperatorsFor( + Symbol name); +// Returns operators in the order which OpOverloadPacket resolves them. +TORCH_API std::vector> getAllSortedOperatorsFor( + Symbol name); + +// given a operator with an overload name, find the specific operator related to +// it, may return nullptr if no operator exists. +TORCH_API std::shared_ptr findOperatorFor( + const c10::OperatorName& full_name); + +TORCH_API std::vector findSimilarOperators(Symbol input_op); + +TORCH_API void registerOperator(Operator&& op); +TORCH_API void deregisterOperator(const FunctionSchema& schema); + +// XXX: this function is meant to be used with string literals only! +TORCH_API std::shared_ptr getOperatorForLiteral( + const char* signature); + +// Ensure the thing that registers c10 ops is defined. +// Otherwise, our registry will not have c10 ops. You can run into this +// scenario if you're querying registered ops during static init. +// +// This fn is defined in register_c10_ops.cpp +TORCH_API void ensure_c10_registerer_defined(); + +// Used to assert that unschematized operators have an analysis method written +TORCH_API bool aliasAnalysisHasSpecialCaseFor(c10::Symbol sym); + +// A factory function to generate an optional operator. It has two +// instantiations depending on the template bool arg value. The arg can be a +// compile-time function for the selective op registration based on schema +// string. +template +std::optional OperatorGenerator( + const char* schema_str, + Func&& op, + AliasAnalysisKind alias_analysis) { + return std::optional(Operator( + std::string(schema_str), std::forward(op), alias_analysis)); +} + +template +std::optional OperatorGenerator( + torch::detail::SelectiveStr schema_str, + Func&& op, + AliasAnalysisKind alias_analysis) { + return OperatorGenerator( + static_cast(schema_str), + std::forward(op), + alias_analysis); +} + +template +std::optional OperatorGenerator( + torch::detail::SelectiveStr schema_str, + Func&& op, + AliasAnalysisKind alias_analysis) { + return std::nullopt; +} + +template +std::optional OperatorGenerator( + const std::string name, + const std::string overload_name, + const std::vector arguments, + const std::vector returns, + Func&& op, + AliasAnalysisKind alias_analysis) { + return std::optional(Operator( + name, + overload_name, + arguments, + returns, + std::forward(op), + alias_analysis)); +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator_options.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator_options.h new file mode 100644 index 0000000000000000000000000000000000000000..50c41fc3ad39d44262b4da8e54fd4b75b00d8f2d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/operator_options.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +using AliasAnalysisKind = c10::AliasAnalysisKind; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/print_handler.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/print_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..36feaffb200b655bd452ff822ae7af5149bc2670 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/print_handler.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +namespace torch::jit { + +using PrintHandler = void (*)(const std::string&); + +TORCH_API PrintHandler getDefaultPrintHandler(); +TORCH_API PrintHandler getPrintHandler(); +TORCH_API void setPrintHandler(PrintHandler ph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_graph_executor_impl.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_graph_executor_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..c64e0b123d6500563ac9fb969a86888c747e6e33 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_graph_executor_impl.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include +#include + +TORCH_DECLARE_bool(torch_jit_static_then_dynamic); + +TORCH_DECLARE_bool(torch_jit_always_dynamic); + +namespace torch::jit { + +TORCH_API void runNooptPassPipeline(std::shared_ptr& graph); + +struct TORCH_API ProfilingGraphExecutorImpl : public GraphExecutorImplBase { + ProfilingGraphExecutorImpl( + const std::shared_ptr& graph, + std::string function_name); + + const ExecutionPlan& getPlanFor( + Stack& stack, + std::optional remaining_bailout_depth) override; + GraphExecutorState getDebugState() override; + ~ProfilingGraphExecutorImpl() override = default; + + void debugFlushCompilationCache(); + + bool isOptimized() const override { + return optimized_plan_.has_value(); + } + + private: + const ExecutionPlan& getOptimizedPlanFor( + Stack& stack, + std::optional remaining_bailout_depth); + void runProfilingInsensitiveOptimizations(std::shared_ptr& graph); + void runProfilingOptimizations( + std::shared_ptr& graph, + size_t remaining_depth); + void replaceFallbackGraphWithFallbackFunction(Block* b); + FusionBehavior getCurrentBehavior(size_t remaining_depth); + size_t getInstantiatedBailoutDepth(); + void runNoGradOptimizations( + std::shared_ptr& graph, + size_t remaining_bailout_depth); + void runFinalOptimizations(std::shared_ptr& graph); + + void clearTheGraphCompilationIntermediateGraphs(); + + std::unique_ptr pr_; + std::optional + profiling_plan_; // plan to run in order to profiling the code + std::optional optimized_plan_; + FusionStrategy fusion_strategy_; + + // this plan is used if getGraphExecutorOptimize is unset + std::optional fallback_plan_; + // fallback functions are inserted for tensorexpr fusion groups + // and by specialize_autogradzero. Whenever, at runtime, input + // tensor don't match profiled properties, fallback functions are called + // They are the deoptimized version of the logic in fusion groups + // and/or autograd. + // The fallback functions are owned by a GraphExecutor instance + // They only exist in the optimized graph which is a private property + // of the GraphExecutor and only shared with InterpreterState + std::vector> fallback_functions_; + std::optional remaining_bailout_depth_; + // The time the optimized_plan_ is created. + int32_t time_optimized_plan_created_ = 0; + // Has the extra memory used by the graph for profiling is released? + bool is_graph_extra_memory_released_ = false; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_record.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_record.h new file mode 100644 index 0000000000000000000000000000000000000000..c45dcde7b0bf0ea2314eb676ea87e958499ff7a2 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/profiling_record.h @@ -0,0 +1,205 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// We would like to assign each position/axis of a tensor an abstract size +// * For each `tensor` we have a profiled `Value` of a `TensorType` describing +// the properties of the `tensor`. +// * `TensorType` has a property called `symbolic_sizes_` to describe observed +// `tensor.sizes()` +// * `symbolic_sizes_` is a vector of abstract sizes (or +// `std::vector`) where +// * `ShapeSymbol`at `symbolic_sizes_[i]` describes the size value +// (`Dimension`) at `tensor.sizes()[i]` +// * We may see the same `Dimension` at different positions `i` in +// `tensor.sizes()` or even in different `tensor` +// * First, we would like associate the same `ShapeSymbol` to the same +// `Dimension` across **one** profiling execution or run of a TorchScript +// function. +// * The same `ShapeSymbol`s in different positions of `symbolic_shapes_` in +// possibly different `TensorType`s (i.e. `TensorType`s for different +// profiled values) form an implicit set. The elements of such a set are +// called *dimension locations*. +// * These sets allow us to track how the shapes of input arguments of some +// operation relate to operation's output shapes as the input and output +// shapes might share the same `ShapeSymbol`s +// * For **every** profiling run, we would like to maintain the invariant that +// *the same `ShapeSymbol` is always associated with the same `Dimension`*. +// * To maintain this invariant we merge the profiling information from all +// profiling runs, +// * For every two runs, we iterate over all `symbic_shapes_` and compare +// their `ShapeSymbol`s in the same position. +// * if we observe that for every dimension location that has +// the`ShapeSymbol S1` in run #1 there is **only one** `ShapeSymbol S2` in +// the same dimension location in run #2, we conclude that the invariant +// holds. +// * However, if we observe some dimension locations in run #2 have +// `ShapeSymbol S2` and the other ones have `ShapeSymbol S3` we would like +// to partition the virtual set of dimension locations associated with +// `ShapeSymbol S1` into two new subsets, so the invariant holds. +// * The partitioning works by assigning a new symbol to the dimension +// locations (associated with `ShapeSymbol S1`) that have `ShapeSymbol S2` +// and another new symbol to the dimension locations that have `ShapeSymbol +// S3`. In other words, +// * Subset #1 will consist of the dimension locations that in run #2 have +// `ShapeSymbol S2` and will have `ShapeSymbol S4` in those dimension +// locations +// * Subset #2 will consist of the dimension locations that in run #2 have +// `ShapeSymbol S4` and will have `ShapeSymbol S5` in those dimension +// locations +// * The effective result of merging the profiling information from two runs +// is new `TensorTypes` whose `symbolic_sizes_` /dimension locations have +// either `ShapeSymbol S4` or `ShapeSymbol S5`. +// * Partitioning can be done even before we have seen all the dimension +// locations associated with `ShapeSymbol S1` +// * We use `getSymbolInSet` of `ShapeSymbolTable` to remember all +// `ShapeSymbols` from run #2 we observed in the dimension locations +// associated with `ShapeSymbol S1` . +// * For every `ShapeSymbol` from run #2 in the dimension location +// associated with `ShapeSymbol S1` `getSymbolInSet` returns a symbol +// that we assign to the dimension location in a new TensorType. +// * It's important to point out that the same `ShapeSymbol S2` from run +// #2 in two dimension locations that have different `ShapeSymbol`s in +// run #1 are different! These dimension locations will belong to +// different subsets and have different `ShapeSymbol`s after merge. +// * On the other hand, for the same `ShapeSymbol S2` in two dimension +// locations that have `ShapeSymbol S1` in run #1`getSymbolInSet` will +// return the same symbol. + +namespace torch::jit { + +using ::c10::TensorTypePtr; +using Dimension = int64_t; + +TORCH_API void RegisterProfilingNode(const std::function&); + +struct ProfilingRecord; + +// `SetPartitioningHelper` is used to maintain the following invariant: +// For **every** profiling run, *the same `ShapeSymbol` is always associated +// with the same `Dimension`*. +// while merging the profiling information from multiple runs. +struct SetPartitioningHelper { + std::map> + sets2subsets_; + + // `partitionSetByDimension` partitions a virtual set + // of dimension locations associated with ShapeSymbol `symbol` into subsets. + // Partitioning is equivalent to giving (or renaming) a particular + // dimension location a new `ShapeSymbol`. + // The same `Dimension` value in different dimension locations + // that used to have `symbol` will receive the same + // new `ShapeSymbol`, effectively forming a new set. + c10::ShapeSymbol partitionSetByDimension( + Dimension new_size, + c10::ShapeSymbol symbol) { + auto& dims2symbols = getSetForSymbol(symbol); + + if (dims2symbols.count(new_size) == 0) { + auto new_sym = c10::ShapeSymbol::newSymbol(); + dims2symbols[new_size] = new_sym; + return new_sym; + } + + return dims2symbols[new_size]; + } + + private: + std::map& getSetForSymbol(c10::ShapeSymbol s) { + auto& set = sets2subsets_[s]; + // N.B. adding a mapping { s.static_size(), s } + // makes sure we preserve the fact that + // some dimension values remain the same + // across all profiled runs + if (s.is_static()) { + set.insert({s.static_size(), s}); + } + return set; + } +}; + +// ShapeSymbolTable is used by Interpreter +// to assign dimension values to ShapeSymbols +// and fail a guard if the same symbol +// is assigned more than one dimension value. +struct ShapeSymbolTable { + // N.B. we treat static symbols as always assigned + // to themselves + bool isBound(c10::ShapeSymbol s) { + if (s.is_static()) { + return true; + } + return data_.count(s) != 0; + } + + // N.B. we treat static symbols as always assigned + // to themselves + Dimension getValue(c10::ShapeSymbol s) { + if (s.is_static()) { + return s.static_size(); + } + return data_[s]; + } + void assign(c10::ShapeSymbol s, Dimension v) { + TORCH_INTERNAL_ASSERT(!s.is_static()); + data_[s] = v; + } + std::map data_; + // Tries to assign dimension values from `new_sizes` to + // `ShapeSymbol`s `sym_shapes`. + // Returns `true` if every dimension value from `new_sizes` + // can be assigned to the corresponding `ShapeSymbol` from + // `sym_shapes` + // A dimension value can be assigned to a `ShapeSymbol` + // * if the symbol isn't assigned yet any dimension value + // * if the symbol is assigned and its value is equal to + // the dimension value from `new_sizes` + bool bindSymbolicShapes( + at::IntArrayRef new_sizes, + const c10::SymbolicShape& sym_shapes); +}; + +struct ProfilingRecord { + // N.B. ProfilingRecord's copy and move c-tor are disabled, so we won't + // end up accidentally copying or moving ProfilingRecords whose addresses + // are captured in callbacks_ + ProfilingRecord(const ProfilingRecord&) = delete; + ProfilingRecord(ProfilingRecord&&) noexcept = delete; + TORCH_API static std::unique_ptr instrumentGraph( + const std::shared_ptr& graph); + TORCH_API static void removeProfilingNodes(Block* b); + TORCH_API static void removeProfileCounter(Block* b); + + std::shared_ptr profiled_graph_; + mutable std::mutex mutex_; + size_t profiling_count_; + + bool ready() const; + + std::shared_ptr graph() const { + return profiled_graph_; + } + + TORCH_API ProfileIValueOp* createProfileIValueNode(Value* in_val); + TORCH_API ProfileIValueOp* createProfileIValueNode(ArrayRef inputs); + + private: + ProfileOp* createProfileNode( + const std::function& fp, + at::ArrayRef inputs); + void instrumentBlock(Block* block); + void insertShapeProfile(Node* n, size_t offset, const TypePtr& input_type); + ProfilingRecord(std::shared_ptr g); +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/register_ops_utils.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/register_ops_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..340b597280a6e412f3230c2c2599d6b13b268fcf --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/register_ops_utils.h @@ -0,0 +1,883 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { +constexpr inline c10::AliasAnalysisKind aliasAnalysisFromSchema() { + return c10::AliasAnalysisKind::FROM_SCHEMA; +} + +constexpr inline c10::AliasAnalysisKind aliasAnalysisConservative() { + return c10::AliasAnalysisKind::CONSERVATIVE; +} + +constexpr inline c10::AliasAnalysisKind aliasAnalysisSpecialCase() { + return c10::AliasAnalysisKind::INTERNAL_SPECIAL_CASE; +} + +template +c10::List make_result_list(const TypePtr& elemType) { + return c10::List(); +} + +template <> +c10::impl::GenericList make_result_list(const TypePtr& elemType); + +// As described in https://docs.python.org/3/library/functions.html#round +// When a number is exactly halfway between two integers, python builtin round +// function will round to even number. We use round(x/2)*2 to handle the +// special halfway case. For positive 'x', round(x/2)*2 = +// round((x_e + x_r)/2)*2 = x_e + round(x_r/2)*2, where x_e is an even integer, +// x_r is either 0.5 of 1.5, round(x_r/2)*2 results a 0 or 2, so the final +// result will always be a even number. Due to symmetricity, it also applies to +// negative cases. +inline double round_to_even(double a) { + return a - std::floor(a) == 0.5 ? (std::round(a * 0.5) * 2.0) : std::round(a); +} + +// using the rules from python_arg_parser FunctionParameter::check +// tensor cannot have grad set, tensor must be 0 dim, +// and if the dest is an int the source must be integral type +void checkImplicitTensorToNum(const at::Tensor& t, bool toInt); + +[[maybe_unused]] static int64_t floordiv(int64_t a, int64_t b) { + if (b == 0) { + throw std::runtime_error("division by 0"); + } + if ((a > 0) == (b > 0)) { + // simple case, both have same sign + return a / b; + } else { + // in python division rounds down, it doesn't not truncate like in c++ + auto r = lldiv(a, b); + return (r.rem) ? r.quot - 1 : r.quot; + } +} +TORCH_API void checkDoubleInRange(double a); +[[maybe_unused]] static int64_t floor(double a) { + checkDoubleInRange(a); + return std::floor(a); +} +[[maybe_unused]] static int64_t ceil(double a) { + checkDoubleInRange(a); + return std::ceil(a); +} + +[[maybe_unused]] static int64_t gcd(int64_t a, int64_t b) { + while (b != 0) { + int64_t r = a % b; + a = b; + b = r; + } + // in python gcd returns non-negative values + return std::abs(a); +} + +int64_t partProduct(int n, int m); + +void loop(int n, int64_t& p, int64_t& r); + +int nminussumofbits(int v); + +int64_t factorial(int n); +static const double degToRad = std::acos(-1.0) / 180.0; +static const double radToDeg = 180.0 / std::acos(-1.0); +double degrees(double x); +double radians(double x); + +// Convert an python index (which may be negative) into an index usable for a +// C++ container + +// Equivalent to list.at(idx) +template +decltype(auto) getItem(const c10::List& list, int64_t idx) { + const int64_t list_size = list.size(); + const int64_t normalized_idx = normalizeIndex(idx, list_size); + if (normalized_idx < 0 || normalized_idx >= list_size) { + throw std::out_of_range("list index out of range"); + } + return list.get(normalized_idx); +} + +template +void setItem(const c10::List& list, int64_t idx, T&& value) { + const int64_t list_size = list.size(); + const int64_t normalized_idx = normalizeIndex(idx, list_size); + if (normalized_idx < 0 || normalized_idx >= list_size) { + throw std::out_of_range("list index out of range"); + } + list.set(normalized_idx, std::forward(value)); +} + +void listAppend(Stack& stack); + +void listReverse(Stack& stack); + +template +void minList(Stack& stack) { + c10::List a = pop(stack).to>(); + c10::List b = pop(stack).to>(); + + size_t min_size = std::min(a.size(), b.size()); + for (const auto i : c10::irange(min_size)) { + if (a[i] == b[i]) { + continue; + } + + push(stack, a[i] < b[i] ? a : b); + return; + } + + push(stack, b.size() < a.size() ? b : a); +} + +template +void maxList(Stack& stack) { + c10::List a = pop(stack).to>(); + c10::List b = pop(stack).to>(); + + size_t min_size = std::min(a.size(), b.size()); + for (const auto i : c10::irange(min_size)) { + if (a[i] == b[i]) { + continue; + } + + push(stack, a[i] > b[i] ? a : b); + return; + } + + push(stack, b.size() > a.size() ? b : a); +} + +void listPopImpl(Stack& stack, const char* empty_message); + +void listPop(Stack& stack); + +void listClear(Stack& stack); + +void listDelete(Stack& stack); + +void listInsert(Stack& stack); + +template +void listRemove(Stack& stack) { + T elem = pop(stack).to(); + c10::List list = pop(stack).to>(); + + auto pos = std::find(list.begin(), list.end(), elem); + + if (pos != list.end()) { + list.erase(pos); + } else { + TORCH_CHECK(false, "list.remove(x): x not in list"); + } +} + +template +void listMin(Stack& stack) { + c10::List list = pop(stack).to>(); + size_t list_size = list.size(); + if (list_size == 0) { + throw std::runtime_error("min() arg is an empty sequence"); + } + + T min_elem = list[0]; + for (const auto i : c10::irange(1, list_size)) { + T elem = list[i]; + min_elem = elem < min_elem ? elem : min_elem; + } + + stack.push_back(min_elem); +} + +template +void listMax(Stack& stack) { + c10::List list = pop(stack).to>(); + size_t list_size = list.size(); + if (list_size == 0) { + throw std::runtime_error("max() arg is an empty sequence"); + } + + T max_elem = list[0]; + for (const auto i : c10::irange(1, list_size)) { + T elem = list[i]; + max_elem = elem > max_elem ? elem : max_elem; + } + + stack.push_back(max_elem); +} + +template <> +void listRemove(Stack& stack); + +template +void listIndex(Stack& stack) { + T elem = pop(stack).to(); + c10::List list = pop(stack).to>(); + + auto pos = std::find(list.begin(), list.end(), elem); + + if (pos != list.end()) { + push(stack, static_cast(std::distance(list.begin(), pos))); + } else { + TORCH_CHECK(false, "'", elem, "' is not in list"); + } +} + +template <> +void listIndex(Stack& stack); + +template +void listCount(Stack& stack) { + T elem = pop(stack).to(); + c10::List list = pop(stack).to>(); + + const int64_t count = std::count(list.begin(), list.end(), elem); + push(stack, count); +} + +template <> +void listCount(Stack& stack); + +void listExtend(Stack& stack); + +void listCopy(Stack& stack); + +void listSelect(Stack& stack); + +void listLen(Stack& stack); + +template +void listEq(Stack& stack) { + c10::List b = pop(stack).to>(); + c10::List a = pop(stack).to>(); + push(stack, a == b); +} + +template +void listNe(Stack& stack) { + c10::List b = pop(stack).to>(); + c10::List a = pop(stack).to>(); + push(stack, a != b); +} + +inline bool tensor_list_equal( + const c10::List& a, + const c10::List& b) { + if (a.size() != b.size()) { + return false; + } + + for (const auto i : c10::irange(a.size())) { + const at::Tensor& a_element = a[i]; + const at::Tensor& b_element = b[i]; + // This preserves Python's semantics, which uses eq() to compare two + // elements, then passes the result to bool(). + // see: https://docs.python.org/3.4/reference/datamodel.html#object.__ge__ + const auto cmp_result = a_element.eq(b_element); + if (!at::native::is_nonzero(cmp_result)) { + return false; + } + } + + return true; +} + +// Specialization for at::Tensor, since it doesn't define operator== +template <> +void listEq(Stack& stack); + +// Specialization for at::Tensor, since it doesn't define operator== +template <> +void listNe(Stack& stack); + +void listList(Stack& stack); + +template +void listContains(Stack& stack) { + auto key = pop(stack).to(); + auto list = pop(stack).to>(); + // NOLINTNEXTLINE(performance-implicit-conversion-in-loop) + for (const T& item : list) { + if (item == key) { + push(stack, true); + return; + } + } + push(stack, false); +} + +void listAdd(Stack& stack); + +void listInplaceAdd(Stack& stack); + +void listMulIntLeftInPlace(Stack& stack); + +void listMulIntLeft(Stack& stack); + +void listMulIntRight(Stack& stack); + +void listSlice(Stack& stack); + +template +void listSort(Stack& stack) { + bool reverse = pop(stack).toBool(); + c10::List list = pop(stack).to>(); + std::sort(list.begin(), list.end(), [reverse](const T& a, const T& b) { + // FBCode errors without this check - "strict weak ordering" + // TODO: remove when possible, since it just slows down + // sorting and doesn't do anything useful + if (a == b) { + return false; + } + return (a < b) != reverse; + }); +} + +// Specialization for at::Tensor +template <> +void listSort(Stack& stack); + +template +void listCopyAndSort(Stack& stack) { + c10::List list = pop(stack).to>(); + auto list_copied = list.copy(); + std::sort(list_copied.begin(), list_copied.end(), [](const T& a, const T& b) { + // "strict weak ordering" issue - see other sort + if (a == b) { + return false; + } + return a < b; + }); + push(stack, list_copied); +} + +// Specialization for at::Tensor +template <> +void listCopyAndSort(Stack& stack); + +void listSetItem(Stack& stack); + +struct OperatorGeneratorArgs { + const char* schema_str; + bool isOperationCreator; + union { + void (*operation)(Stack&); + OperationCreator operationCreator; + }; + AliasAnalysisKind aliasAnalysis; + + explicit constexpr OperatorGeneratorArgs( + torch::detail::SelectiveStr schema_str, + void (*op)(Stack&), + AliasAnalysisKind aa) + : schema_str(schema_str), + isOperationCreator(false), + operation(op), + aliasAnalysis(aa) {} + + explicit constexpr OperatorGeneratorArgs( + torch::detail::SelectiveStr schema_str, + OperationCreator opCreator, + AliasAnalysisKind aa) + : schema_str(schema_str), + isOperationCreator(true), + operationCreator(opCreator), + aliasAnalysis(aa) {} + + template + explicit constexpr OperatorGeneratorArgs( + torch::detail::SelectiveStr, + Args...) + : schema_str(nullptr), + isOperationCreator(false), + operation(nullptr), + aliasAnalysis(AliasAnalysisKind::INTERNAL_SPECIAL_CASE) {} +}; + +#define DEFINE_GENERIC_BINARY_OP( \ + aten_op, op, int_float_result, complex_result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op \ + ".int_int(int a, int b) -> " #int_float_result), \ + [](Stack& stack) { \ + int64_t a, b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op \ + ".float_float(float a, float b) -> " #int_float_result), \ + [](Stack& stack) { \ + double a, b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op \ + ".complex_complex(complex a, complex b) -> " #complex_result), \ + [](Stack& stack) { \ + c10::complex a, b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +// define implementations for primitive number ops +#define DEFINE_GENERIC_OP(aten_op, int_op, float_op, int_result, float_result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".int(int a, int b) -> " #int_result), \ + [](Stack& stack) { \ + int64_t a, b; \ + pop(stack, a, b); \ + push(stack, int_op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".float(float a, float b) -> " #float_result), \ + [](Stack& stack) { \ + double a, b; \ + pop(stack, a, b); \ + push(stack, float_op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_INT_FLOAT_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op \ + ".int_float(int a, float b) -> " #result), \ + [](Stack& stack) { \ + int64_t a; \ + double b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op \ + ".float_int(float a, int b) -> " #result), \ + [](Stack& stack) { \ + double a; \ + int64_t b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_INT_OP(aten_op, op) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".int(int a, int b) -> int"), \ + [](Stack& stack) { \ + int64_t a, b; \ + pop(stack, a, b); \ + push(stack, op); /* NOLINT(hicpp-signed-bitwise) */ \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_STR_CMP_OP(aten_op, op) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".str(str a, str b) -> bool"), \ + [](Stack& stack) { \ + auto b = pop(stack).toStringRef(); \ + auto a = pop(stack).toStringRef(); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +// define a primitive op over Scalar operands. +// it's necessary to register this overload following +// int/float variations to avoid trapping Scalar args +// in unintended implicit conversions +#define DEFINE_SCALAR_BINARY_OP_AVOID_COLLISION_GENERIC( \ + aten_op, int_op, float_op, result, string_val) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op string_val \ + "(Scalar a, Scalar b) -> " #result), \ + [](Stack& stack) { \ + IValue x, y; \ + pop(stack, x, y); \ + if (x.isDouble()) { \ + if (y.isDouble()) { \ + double a = x.toDouble(); \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else { \ + double a = x.toDouble(); \ + int64_t b = y.toInt(); \ + push(stack, float_op); \ + } \ + } else { \ + if (y.isDouble()) { \ + int64_t a = x.toInt(); \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else { \ + int64_t a = x.toInt(); \ + int64_t b = y.toInt(); \ + push(stack, int_op); \ + } \ + } \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_SCALAR_BINARY_OP(aten_op, int_op, float_op, result) \ + DEFINE_SCALAR_BINARY_OP_AVOID_COLLISION_GENERIC( \ + aten_op, int_op, float_op, result, "") + +#define DEFINE_SCALAR_BINARY_OP_AVOID_COLLISION( \ + aten_op, int_op, float_op, result) \ + DEFINE_SCALAR_BINARY_OP_AVOID_COLLISION_GENERIC( \ + aten_op, int_op, float_op, result, ".Scalar_Scalar") + +#define DEFINE_BINARY_OP(aten_op, op) \ + DEFINE_GENERIC_OP(aten_op, op, op, int, float), \ + DEFINE_INT_FLOAT_OP(aten_op, op, float), \ + DEFINE_SCALAR_BINARY_OP(aten_op, op, op, Scalar) + +#define DEFINE_BINARY_FLOAT_OP(aten_op, op) \ + DEFINE_GENERIC_OP(aten_op, op, op, float, float), \ + DEFINE_INT_FLOAT_OP(aten_op, op, float), \ + DEFINE_SCALAR_BINARY_OP(aten_op, op, op, float) + +#define DEFINE_COMPARISON_OP(aten_op, op) \ + DEFINE_GENERIC_OP(aten_op, op, op, bool, bool), \ + DEFINE_INT_FLOAT_OP(aten_op, op, bool), \ + DEFINE_SCALAR_BINARY_OP(aten_op, op, op, bool), \ + DEFINE_STR_CMP_OP(aten_op, op) + +#define DEFINE_UNARY_INT_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".int(int a) -> " #result), \ + [](Stack& stack) { \ + int64_t a; \ + pop(stack, a); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_UNARY_FLOAT_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".float(float a) -> " #result), \ + [](Stack& stack) { \ + double a; \ + pop(stack, a); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_UNARY_OP(aten_op, op, int_result, float_result) \ + DEFINE_UNARY_INT_OP(aten_op, op, int_result), \ + DEFINE_UNARY_FLOAT_OP(aten_op, op, float_result), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".Scalar(Scalar a) -> Scalar"), \ + [](Stack& stack) { \ + IValue x; \ + pop(stack, x); \ + if (x.isDouble()) { \ + double a = x.toDouble(); \ + push(stack, static_cast(op)); \ + } else { \ + int64_t a = x.toInt(); \ + push(stack, static_cast(op)); \ + } \ + }, \ + aliasAnalysisFromSchema()) +#define DEFINE_BOOL_OP(aten_op, op) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".bool(bool a, bool b) -> bool"), \ + [](Stack& stack) { \ + bool a, b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) +#define DEFINE_STRING_OP(op_name, string_op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#op_name ".str(str a, str b) ->" #result), \ + [](Stack& stack) { \ + auto b = pop(stack).toStringRef(); \ + auto a = pop(stack).toStringRef(); \ + push(stack, string_op); \ + }, \ + aliasAnalysisFromSchema()) + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define DEFINE_UNARY_COMPLEX_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".complex(complex a) -> " #result), \ + [](Stack& stack) { \ + c10::complex a; \ + pop(stack, a); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +// Some complex unary ops (like abs, angle) return real valued output, but most +// other unary ops return complex valued output. So, this macro is used in the +// former case where we can explicitly pass complex_result_cast argument, which +// is set to c10::complex in the macro `DEFINE_UNARY_OP_WITH_COMPLEX` +// defined below. +#define DEFINE_UNARY_OP_WITH_COMPLEX_CAST( \ + aten_op, \ + op, \ + int_result, \ + float_result, \ + complex_result, \ + complex_result_cast) \ + DEFINE_UNARY_INT_OP(aten_op, op, int_result), \ + DEFINE_UNARY_FLOAT_OP(aten_op, op, float_result), \ + DEFINE_UNARY_COMPLEX_OP(aten_op, op, complex_result), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".Scalar(Scalar a) -> Scalar"), \ + [](Stack& stack) { \ + IValue x; \ + pop(stack, x); \ + if (x.isDouble()) { \ + double a = x.toDouble(); \ + push(stack, static_cast(op)); \ + } else if (x.isComplexDouble()) { \ + c10::complex a = x.toComplexDouble(); \ + push(stack, static_cast(op)); \ + } else { \ + int64_t a = x.toInt(); \ + push(stack, static_cast(op)); \ + } \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_UNARY_OP_WITH_COMPLEX(aten_op, op, int_result, float_result) \ + DEFINE_UNARY_OP_WITH_COMPLEX_CAST( \ + aten_op, op, int_result, float_result, complex, c10::complex) + +#define DEFINE_GENERIC_OP_WITH_COMPLEX( \ + aten_op, \ + int_op, \ + float_op, \ + complex_op, \ + int_result, \ + float_result, \ + complex_result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op ".int(int a, int b) -> " #int_result), \ + [](Stack& stack) { \ + int64_t a, b; \ + pop(stack, a, b); \ + push(stack, int_op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".complex(complex a, complex b) -> " #complex_result), \ + [](Stack& stack) { \ + c10::complex a, b; \ + pop(stack, a, b); \ + push(stack, complex_op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".float(float a, float b) -> " #float_result), \ + [](Stack& stack) { \ + double a, b; \ + pop(stack, a, b); \ + push(stack, float_op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_INT_COMPLEX_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op \ + ".int_complex(int a, complex b) -> " #result), \ + [](Stack& stack) { \ + int64_t a; \ + c10::complex b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".complex_int(complex a, int b) -> " #result), \ + [](Stack& stack) { \ + c10::complex a; \ + int64_t b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_FLOAT_COMPLEX_OP(aten_op, op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".float_complex(float a, complex b) -> " #result), \ + [](Stack& stack) { \ + double a; \ + c10::complex b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()), \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA( \ + #aten_op ".complex_float(complex a, float b) -> " #result), \ + [](Stack& stack) { \ + c10::complex a; \ + double b; \ + pop(stack, a, b); \ + push(stack, op); \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX_AVOID_COLLISION_GENERIC( \ + aten_op, int_op, float_op, complex_op, result, string_val) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op string_val \ + "(Scalar a, Scalar b) -> " #result), \ + [](Stack& stack) { \ + IValue x, y; \ + pop(stack, x, y); \ + if (x.isComplexDouble()) { \ + c10::complex a = x.toComplexDouble(); \ + if (y.isComplexDouble()) { \ + c10::complex b = y.toComplexDouble(); \ + push(stack, complex_op); \ + } else if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, complex_op); \ + } else { \ + int64_t b = y.toInt(); \ + push(stack, complex_op); \ + } \ + } else if (x.isDouble()) { \ + double a = x.toDouble(); \ + if (y.isComplexDouble()) { \ + c10::complex b = y.toComplexDouble(); \ + push(stack, complex_op); \ + } else if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else { \ + int64_t b = y.toInt(); \ + push(stack, float_op); \ + } \ + } else { \ + int64_t a = x.toInt(); \ + if (y.isComplexDouble()) { \ + c10::complex b = y.toComplexDouble(); \ + push(stack, complex_op); \ + } else if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else { \ + int64_t b = y.toInt(); \ + push(stack, int_op); \ + } \ + } \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX_WITHOUT_INT_COMPLEX_PAIR( \ + aten_op, int_op, float_op, complex_op, result) \ + OperatorGeneratorArgs( \ + TORCH_SELECTIVE_SCHEMA(#aten_op "(Scalar a, Scalar b) -> " #result), \ + [](Stack& stack) { \ + IValue x, y; \ + pop(stack, x, y); \ + if (x.isComplexDouble()) { \ + c10::complex a = x.toComplexDouble(); \ + if (y.isComplexDouble()) { \ + c10::complex b = y.toComplexDouble(); \ + push(stack, complex_op); \ + } else if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, complex_op); \ + } \ + } else if (x.isDouble()) { \ + double a = x.toDouble(); \ + if (y.isComplexDouble()) { \ + c10::complex b = y.toComplexDouble(); \ + push(stack, complex_op); \ + } else if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else { \ + int64_t b = y.toInt(); \ + push(stack, float_op); \ + } \ + } else { \ + int64_t a = x.toInt(); \ + if (y.isDouble()) { \ + double b = y.toDouble(); \ + push(stack, float_op); \ + } else if (y.isInt()) { \ + int64_t b = y.toInt(); \ + push(stack, int_op); \ + } \ + } \ + }, \ + aliasAnalysisFromSchema()) + +#define DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX( \ + aten_op, int_op, float_op, complex_op, result) \ + DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX_AVOID_COLLISION_GENERIC( \ + aten_op, int_op, float_op, complex_op, result, "") + +#define DEFINE_BINARY_OP_WITH_COMPLEX(aten_op, op) \ + DEFINE_GENERIC_OP_WITH_COMPLEX(aten_op, op, op, op, int, float, complex), \ + DEFINE_INT_COMPLEX_OP(aten_op, op, complex), \ + DEFINE_FLOAT_COMPLEX_OP(aten_op, op, complex), \ + DEFINE_INT_FLOAT_OP(aten_op, op, float), \ + DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX(aten_op, op, op, op, Scalar) + +#define DEFINE_COMPARISON_OP_WITH_COMPLEX(aten_op, op) \ + DEFINE_GENERIC_OP_WITH_COMPLEX(aten_op, op, op, op, bool, bool, bool), \ + DEFINE_INT_FLOAT_OP(aten_op, op, bool), \ + DEFINE_FLOAT_COMPLEX_OP(aten_op, op, bool), \ + DEFINE_SCALAR_BINARY_OP_WITH_COMPLEX_WITHOUT_INT_COMPLEX_PAIR( \ + aten_op, op, op, op, bool), \ + DEFINE_STR_CMP_OP(aten_op, op) + +TORCH_API at::Generator make_generator_for_device( + c10::Device device, + std::optional seed = std::nullopt); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/script_profile.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/script_profile.h new file mode 100644 index 0000000000000000000000000000000000000000..8061d6fc85974905ca28bc307953ebe3285039bb --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/script_profile.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace torch::jit { +namespace profiling { + +struct Datapoint { + using Timepoint = std::chrono::time_point; + SourceRange sourceRange; + Timepoint start; + Timepoint end; + + explicit Datapoint(SourceRange sr) + : sourceRange(std::move(sr)), start(std::chrono::steady_clock::now()) {} +}; + +class TORCH_API InstructionSpan { + public: + explicit InstructionSpan(Node&); + ~InstructionSpan(); + InstructionSpan(InstructionSpan&&) = delete; + InstructionSpan& operator=(InstructionSpan&&) = delete; + + private: + std::unique_ptr datapoint_; +}; + +bool TORCH_API isProfilingOngoing(); + +} // namespace profiling + +struct TORCH_API InstructionStats : public CustomClassHolder { + int64_t count{0}; + std::chrono::nanoseconds duration{0}; +}; + +class TORCH_API SourceStats : public CustomClassHolder { + public: + using LineMap = c10::Dict>; + + SourceStats(SourceRef source, const LineMap& lineMap) + : source_(std::move(source)), lineMap_(lineMap) {} + + const SourceRef& getSourceRef() const { + return source_; + } + + const LineMap& getLineMap() const { + return lineMap_; + } + + private: + SourceRef source_; + LineMap lineMap_; +}; + +/** + * ScriptProfile is an underlying C++ implementation for TorchScript profiling. + * The profiling section is specified by calling enable() and disable(): + * + * ... + * scriptProfile.enable(); + * ... + * (scripts) + * ... + * scriptProfile.disable(); + * ... + * + * NOTE: you cannot attach the profiler while the script is running. + * + * To retrieve collected runtime data, users may call dumpStats() and do + * arbitrary filtering on the data they want. Note that dumpStats() should + * not be called inside a profiling section. + * In general, stats are aggregated per source function body, and then by line + * number. + */ +class TORCH_API ScriptProfile : public CustomClassHolder { + // Aggregates datapoints by function source id, then by line number. + using LineMap = std::map; + using SourceMap = std::map>; + + public: + void enable(); + void disable(); + const SourceMap& dumpStats(); + void addDatapoint(std::shared_ptr); + ~ScriptProfile() override; + + private: + bool enabled_{false}; + std::vector> datapoints_; + SourceMap sourceMap_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/serialized_shape_function_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/serialized_shape_function_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..e822f3f93e3d29d533f27e8565d7a0de787f33b5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/serialized_shape_function_registry.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API const std::string& GetSerializedShapeFunctions(); + +TORCH_API const OperatorMap& GetShapeFunctionMappings(); + +TORCH_API const OperatorMap>& +GetBoundedShapeMappings(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/shape_function_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/shape_function_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..533b1f11020763e2d6d1d05734c6a4b09bcc44aa --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/shape_function_registry.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +TORCH_API const std::string& GetSerializedFuncs(); + +TORCH_API const OperatorMap& GetFuncMapping(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/simple_graph_executor_impl.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/simple_graph_executor_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..e1ebed46ede8076f68affc806e96b2f4b502019d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/simple_graph_executor_impl.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +namespace torch::jit { + +struct TORCH_API SimpleGraphExecutorImpl : public GraphExecutorImplBase { + SimpleGraphExecutorImpl( + const std::shared_ptr& graph, + std::string function_name); + + const ExecutionPlan& getPlanFor( + Stack& stack, + std::optional remaining_bailout_depth) override; + GraphExecutorState getDebugState() override; + ~SimpleGraphExecutorImpl() override = default; + + private: + std::optional execution_plan_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/slice_indices_adjust.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/slice_indices_adjust.h new file mode 100644 index 0000000000000000000000000000000000000000..720c8b69e5ecd55cbe9a00d13342fa9f5cbc98db --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/slice_indices_adjust.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace torch::jit { + +// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software +// Foundation; All Rights Reserved +// +// Stolen (with appropriate modifications) by @agolynski +// (https://github.com/pytorch/pytorch/pull/33019) from cpython repo +// Objects/sliceobject.c with comment: this is harder to get right than you +// might think +// +// This adjusts indexes according to python list semantics and returns number +// of elements in the resulting list. +TORCH_API int64_t slice_indices_adjust( + int64_t length, + int64_t* start, + int64_t* stop, + int64_t step); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_script.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_script.h new file mode 100644 index 0000000000000000000000000000000000000000..0715f0deeb1208ce0cdd0606598f5cc8fd3d4bde --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_script.h @@ -0,0 +1,18 @@ +#pragma once +// This file is temporary until native_functions.yaml and derivatives.yaml are +// merged. Ideally this should all go into native_functions.yaml + +#include +#include +#include + +namespace torch::jit { +struct GradientPair { + std::shared_ptr forward; + std::shared_ptr backward; +}; + +TORCH_API std::optional gradientInfoForSchema( + const FunctionSchema& schema); +TORCH_API bool hasGradientInfoForSchema(const FunctionSchema& schema); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..7222fd8bca326930a4d891038183a073b0e0232b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry.h @@ -0,0 +1,69 @@ +#pragma once +// This file is temporary until native_functions.yaml and derivatives.yaml are +// merged. Ideally this should all go into native_functions.yaml + +#include +#include + +namespace torch::jit { + +/* +ADDING A NEW SHAPE GRAPH: +- For one node schema, there is one corresponding registered shape compute +graph. The schema of the graph should be the same except for Tensor arguments. +For every Tensor input in operator schema, there should be a List[int] +corresponding to that Tensor's shape. For example: "aten::linear(Tensor input, +Tensor weight, Tensor? bias=None) -> Tensor" ==> def linear(input: List[int], +weight: List[int], bias: Optional[List[int]]) + +Additionally, arguments which are unused at the end of the schema may be left +off. This allows sharing a single graph for multiple function schemas, such as +unary operators with different trailing arguments that do not affect the output +shape. + +The shape graph should return a new, unaliased List[int] (or tuple of lists for +multiple returns) and should not modify any input lists. This allows the shape +graphs to be composed and executed. + +The shape analysis (particularly for non-complete, or symbolic shapes) works by +partially evaluating the JIT IR. It may be possible for a Graph to be registered +that we cannot currently partially evaluate. If this happens, please file an +issue. There are lints registered to avoid particular known patterns (continue +or break or early return in a loop). Those may be improved in the future, please +file an issue if necessary. + +To debug (and write initially) the recommended flow is to define these functions +in python and iterate there. Functions should be added to +torch/jit/_shape_functions. + +To test operators, the preferred flow is through OpInfos, with +`assert_jit_shape_analysis=True`. If this is not feasible, you can look at tests +in `test_symbolic_shape_analysis.py` such as `test_adaptive_avg_pool2d`. + +Operators which take in a list of tensors, such as concat, are not yet +supported. Concat has been special cased and could be generalized as needed. +Please file an issue. +*/ + +struct BoundedShapeGraphs { + std::shared_ptr lower_bound; + std::shared_ptr upper_bound; +}; + +TORCH_API void RegisterShapeComputeGraphForSchema( + const FunctionSchema& schema, + const std::shared_ptr& g); + +TORCH_API std::optional> shapeComputeGraphForSchema( + const FunctionSchema& schema); + +TORCH_API std::optional boundedGraphsForSchema( + const FunctionSchema& schema); + +TORCH_API std::vector RegisteredShapeComputeSchemas(); + +TORCH_API void LintShapeComputeGraph( + const FunctionSchema* schema, + const std::shared_ptr& graph); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry_util.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry_util.h new file mode 100644 index 0000000000000000000000000000000000000000..e1280504e5c914f51809e300d0d46bc182ae9789 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/symbolic_shape_registry_util.h @@ -0,0 +1,12 @@ +#pragma once +// This file is temporary until native_functions.yaml and derivatives.yaml are +// merged. Ideally this should all go into native_functions.yaml + +#include +#include + +namespace torch::jit { + +TORCH_API const OperatorMap& get_tensorexpr_elementwise_set(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/vararg_functions.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/vararg_functions.h new file mode 100644 index 0000000000000000000000000000000000000000..0be53d4ffeb28b910e1c3f9d3eb1115a7e527784 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/vararg_functions.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace torch::jit { + +void tupleUnpack(Stack& stack); + +void format(Stack& stack, size_t num_inputs); + +void einsum(Stack& stack, size_t num_inputs); + +void percentFormat(Stack& stack, size_t num_inputs); + +void listUnpack(Stack& stack, size_t num_outputs); + +void tupleConstruct(Stack& stack, size_t num_inputs); + +void namedTupleConstruct(Stack& stack, c10::TypePtr type, size_t num_inputs); + +void listConstruct(Stack& stack, const c10::Type& list_type, size_t num_inputs); + +void dictConstruct(Stack& stack, const c10::Type& type, size_t num_inputs); + +// as weak_ref will create a Object with a non-owning CompilationUnit reference, +// for use as a constant in the Graph to avoid a reference cycle +void createObject( + Stack& stack, + const at::ClassTypePtr& type, + bool as_weak_ref = false); + +void isinstance(Stack& stack, at::ArrayRef types); + +void tupleSlice(Stack& stack, size_t begin, size_t end); + +void dequantize(Stack& stack); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/variable_tensor_list.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/variable_tensor_list.h new file mode 100644 index 0000000000000000000000000000000000000000..e8dcd4f2c5b0b95f0f727e97794ddc321fe49fc7 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/runtime/variable_tensor_list.h @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace torch::jit { + +// a wrapper to mark places where we expect all the at::Tensors to be +// variables +struct variable_tensor_list : public std::vector { + variable_tensor_list() = default; + template + variable_tensor_list(InputIt first, InputIt last) + : std::vector(first, last) {} + explicit variable_tensor_list(std::vector&& tensor) + : std::vector(std::move(tensor)) {} +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/callstack_debug_info_serialization.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/callstack_debug_info_serialization.h new file mode 100644 index 0000000000000000000000000000000000000000..6d09bf56b2c1d295feae30eb6054d017c7c3e9c6 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/callstack_debug_info_serialization.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include + +namespace c10 { +struct IValue; +} + +namespace torch::jit { + +class Pickler; +class InlinedCallStackSerializer { + public: + // Serialize InlinedCallStack as + // SerializedInlinedCallStack = + // [module_info, source range tag, SerializedInlinedCallStack] + // module_info = [ClassType.qualifiedName, instance_name] + // source_range_tag = unique source range id + c10::IValue serialize( + const InlinedCallStackPtr& cs_ptr, + const SourceRangeTagMap& source_range_tags); + + private: + // module_info = [ClassType.qualifiedName, instance_name] + c10::IValue serialize_module_instance_info( + const std::optional& m); + + // This caches serialized inlined callstack ptr, since many + // InlinedCallStackPtr can refer to the same one. + ska::flat_hash_map + serialized_inlined_callstack_; + // This caches serialized module instance info. + // There might be many nodes that are part of the same + // parent, grandparent etc. module. + ska::flat_hash_map serialized_module_instance_info_; +}; + +class TORCH_API CallStackDebugInfoPickler { + public: + CallStackDebugInfoPickler() = default; + + std::vector pickle( + const std::unordered_map& callstack_ptrs, + const SourceRangeTagMap& source_range_tags); + + private: + InlinedCallStackSerializer css_; +}; + +class InlinedCallStackDeserializer { + public: + InlinedCallStackPtr deserialize( + const c10::IValue& iv, + const ska::flat_hash_map& source_range_map, + const std::shared_ptr& cu); + + private: + std::optional deserialize_module_instance_info( + const c10::IValue& iv, + const std::shared_ptr& cu); + + ska:: + flat_hash_map, InlinedCallStackPtr> + cached_inlined_callstacks_; + ska::flat_hash_map, ModuleInstanceInfo> + cached_module_instance_info_; +}; + +class TORCH_API CallStackDebugInfoUnpickler { + public: + ska::flat_hash_map unpickle( + const at::DataPtr& data, + size_t size, + const ska::flat_hash_map& source_range_map, + const std::shared_ptr& cu); + + private: + InlinedCallStackDeserializer csds_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export.h new file mode 100644 index 0000000000000000000000000000000000000000..8b2d6d84716ae5bfc5763c38f53bb5f349234f7f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export.h @@ -0,0 +1,279 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ONNX_NAMESPACE { +class ModelProto; +} + +namespace torch::jit { + +// This map is used to keep track of parameters that should be exported +// externally. When `defer_weight_export` is true, the returned map contains +// kv pairs that map {external reference name} -> {at::Tensor to be exported}. +// It is the responsibility of the caller to export these appropriately. +// +// For example, when exporting to a zip archive, the caller may write out files +// for each entry in the export map, with the filename being the key and the +// file contents being the raw tensor data. +using RawDataExportMap = std::unordered_map; + +using SymbolDimMap = std::map; +using DimSymbolMap = std::map; + +using NodeNameMap = std::unordered_map; + +// Used for modularized export settling function and node attributes. +using NodeAttrNameMap = std:: + unordered_map>; + +TORCH_API std::tuple< + std::shared_ptr<::ONNX_NAMESPACE::ModelProto>, + RawDataExportMap, + SymbolDimMap, + bool, + NodeNameMap> +export_onnx( + const std::shared_ptr& graph, + const std::map& initializers, + int64_t onnx_opset_version, + const std::unordered_map< + std::string, + std::unordered_map>& dynamic_axes, + bool defer_weight_export = false, + ::torch::onnx::OperatorExportTypes operator_export_type = + ::torch::onnx::OperatorExportTypes::ONNX, + bool strip_doc_string = true, + bool keep_initializers_as_inputs = true, + const std::map& custom_opsets = {}, + bool add_node_names = true, + bool use_external_data_format = false, + const std::string& onnx_file_path = std::string(), + const NodeAttrNameMap& node_attr_to_name = {}); + +TORCH_API std::string serialize_model_proto_to_string( + const std::shared_ptr<::ONNX_NAMESPACE::ModelProto>& model_proto); + +TORCH_API void check_onnx_proto(const std::string& proto_string); + +// Serializer for both oldsyle and unified format TorchScript serialization +class TORCH_API ScriptModuleSerializer { + public: + explicit ScriptModuleSerializer( + caffe2::serialize::PyTorchStreamWriter& export_writer) + : writer_(export_writer) {} + + void writeFiles(const std::string& code_dir); + void serialize( + const Module& module, + const ExtraFilesMap& extra_files, + bool bytecode_format, + bool save_mobile_debug_info); + void serialize_unified_format(Module& module, uint64_t script_module_id); + SerializationStorageContext& storage_context(); + + ~ScriptModuleSerializer() = default; + + private: + void convertNamedType(const c10::NamedTypePtr& class_type); + void convertTypes(const at::NamedTypePtr& root_type); + void writeExtraFiles(const Module& module, const ExtraFilesMap& extra_files); + void writeByteCode(const Module& module, bool save_mobile_debug_info); + void writeArchive( + const IValue& value, + const std::string& archive_name, + const std::string& archive_dir, + const std::string& tensor_dir, + bool use_storage_context = false, + bool skip_tensor_data = false); + void updateSourceRangeTags(const SourceRangeRecords& ranges); + + caffe2::serialize::PyTorchStreamWriter& writer_; + std::vector constant_table_; + + std::unordered_set converted_types_; + PrintDepsTable class_deps_; + TypeNameUniquer type_name_uniquer_; + // qualifier, e.g. '__torch__.Bar' -> PythonPrint for the file that will be + // created + OrderedDict file_streams_; + // Used to keep references of storages around during serialization to solve + // for ABA memory reuse problem hit when storages are created/destroyed + // during serialization process. Also used to coordinate sharing of storages + // between Script and eager modules in torch.package. + SerializationStorageContext storage_context_; + + // Uniquely identifies a SourceRange in a model. + // SourceRanges are associated with Nodes of Graphs. + // However for mobile deployment we dont intend to ship + // full JIT with capabilities of reading code and constructing + // graphs. + // Instead we serialize the Code generated from graph of the methods. + // Code is serialized in bytecode format that contains instructions + // corresponding to the nodes of the graph. Since original graph is gone, the + // question is how do we identify where the ops, in serialized bytecode, come + // from in original model code. We do this in two parts. + // 1. Associate a unique tag to SourceRange. + // 2. Serialize this unique_tag. + // 2.1 Meaning save instead of + // + // 3. During serializing model for mobile, i.e. bytecode generation, + // save unique tag of SourceRange corresponding to the Node. + // 4. During deserialization, read all the debug_pkl, to construct a map + // of and use tag saved with OPs in bytecode + // to lookup the source range. + // Strictly speaking we will serialize InlinedCallStack directly, which + // contains SourceRange. This way we have access to entire callstack and not + // just source information about where the node is, since bytecode inlines the + // graph before saving it. + SourceRangeTagMap source_range_tags_; + int64_t current_source_range_tag_{0}; +}; + +// For testing purposes +TORCH_API std::string pretty_print_onnx( + const std::shared_ptr& graph, + const std::map& initializers, + int64_t onnx_opset_version, + bool defer_weight_export, + ::torch::onnx::OperatorExportTypes operator_export_type = + ::torch::onnx::OperatorExportTypes::ONNX, + bool google_printer = false, + bool keep_initializers_as_inputs = true, + const std::map& custom_opsets = {}, + bool add_node_names = true); + +TORCH_API void ExportModule( + const Module& module, + std::ostream& out, + const ExtraFilesMap& metadata = ExtraFilesMap(), + bool bytecode_format = false, + bool save_mobile_debug_info = false, + bool use_flatbuffer = false); + +TORCH_API void ExportModule( + const Module& module, + const std::string& filename, + const ExtraFilesMap& metadata = ExtraFilesMap(), + bool bytecode_format = false, + bool save_mobile_debug_info = false, + bool use_flatbuffer = false); + +TORCH_API void ExportModule( + const Module& module, + const std::function& writer_func, + const ExtraFilesMap& metadata = ExtraFilesMap(), + bool bytecode_format = false, + bool save_mobile_debug_info = false, + bool use_flatbuffer = false); + +// Write the bytes of a pickle archive and the tensors referenced inside that +// archive +TORCH_API void writeArchiveAndTensors( + const std::string& archive_name, + const char* pickle_bytes, + size_t size, + const std::vector& tensors, + caffe2::serialize::PyTorchStreamWriter& out); + +// Surrounding system can install an additional hook to produce extra files +// with metadata based on environment every time a module is serialized. +using ExportModuleExtraFilesHook = std::function; +TORCH_API void SetExportModuleExtraFilesHook(ExportModuleExtraFilesHook hook); + +/** + * Generates new bytecode for a Script module and returns what the op list + * would be for a LiteScriptModule based off the current code base. If you + * have a LiteScriptModule and want to get the currently present + * list of ops call _export_operator_list instead. + */ +TORCH_API std::vector export_opnames(const Module& m); + +struct TORCH_API BytecodeEmitMode { + static bool is_default_value_for_unspecified_arg_enabled(); + static void set_default_value_for_unspecified_arg_enabled(bool enabled); + + static bool is_default_args_before_out_args_enabled(); + static void set_default_args_before_out_args_enabled(bool enabled); + + static bool is_emit_promoted_ops_enabled(); + static void set_default_emit_promoted_ops_enabled(bool enabled); +}; + +// RAII guard to switch the way JIT emits the bytecode for inputs. +// default_value_for_unspecified_arg: +// true: instruction of default argument values (like LOADC) is emitted. +// false: instruction of default argument values are not emitted. Instead +// they are fetched from operator schema. +// default_args_before_out_args (to forward compatibile support +// operators allowing out arguments and default arguments): +// true: the number of specified arguments will deserialized to (#all_args - +// #default_args). false: the number of specified arguments will deserialized to +// (#all_args). +struct TORCH_API BytecodeEmitModeGuard { + BytecodeEmitModeGuard( + bool enable_default_value_for_unspecified_arg, + bool enable_default_args_before_out_args, + bool enable_emit_promoted_ops) + : prev_default_value_for_unspecified_arg_mode( + BytecodeEmitMode::is_default_value_for_unspecified_arg_enabled()), + prev_default_args_before_out_args( + BytecodeEmitMode::is_default_args_before_out_args_enabled()), + prev_default_emit_promoted_ops( + BytecodeEmitMode::is_emit_promoted_ops_enabled()) { + BytecodeEmitMode::set_default_value_for_unspecified_arg_enabled( + enable_default_value_for_unspecified_arg); + BytecodeEmitMode::set_default_args_before_out_args_enabled( + enable_default_args_before_out_args); + BytecodeEmitMode::set_default_emit_promoted_ops_enabled( + enable_emit_promoted_ops); + } + ~BytecodeEmitModeGuard() { + BytecodeEmitMode::set_default_value_for_unspecified_arg_enabled( + prev_default_value_for_unspecified_arg_mode); + BytecodeEmitMode::set_default_args_before_out_args_enabled( + prev_default_args_before_out_args); + BytecodeEmitMode::set_default_emit_promoted_ops_enabled( + prev_default_emit_promoted_ops); + } + bool prev_default_value_for_unspecified_arg_mode; + bool prev_default_args_before_out_args; + bool prev_default_emit_promoted_ops; +}; + +TORCH_API IValue to_tuple(std::vector ivalues); +TORCH_API IValue +Table(const std::vector>& entries); + +// TODO remove these switches once interface call is rolled out. +TORCH_API void enableMobileInterfaceCallExport(); +bool getMobileInterfaceCallExport(); + +TORCH_API CompilationOptions getOptionsFromGlobal(); + +TORCH_API void save_jit_module( + const Module& module, + const std::string& filename, + const ExtraFilesMap& extra_files = ExtraFilesMap()); + +TORCH_API DetachedBuffer::UniqueDetachedBuffer save_jit_module_to_bytes( + const Module& module, + const ExtraFilesMap& extra_files = ExtraFilesMap()); + +TORCH_API void save_jit_module_to_write_func( + const Module& module, + const ExtraFilesMap& extra_files, + bool save_mobile_debug_info, + const std::function& writer_func); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export_bytecode.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export_bytecode.h new file mode 100644 index 0000000000000000000000000000000000000000..d06a2e0c137d424fdd2514a096ae0a547e343740 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/export_bytecode.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +struct TORCH_API CompilationOptions { + bool incl_interface_call = false; + bool enable_default_value_for_unspecified_arg = false; + bool enable_default_args_before_out_args = true; + bool enable_emit_promoted_ops = true; + int model_version = caffe2::serialize::kProducedBytecodeVersion; +}; + +TORCH_API mobile::Module jitModuleToMobile( + const Module& module, + const CompilationOptions& options); + +mobile::Code compileGraphToMobileCode( + const std::string& name, + const std::shared_ptr& graph, + const CompilationOptions& compilation_options, + BackendDebugInfoRecorder& debug_info_recorder); + +TORCH_API std::unique_ptr convertJitFunctionToMobileFunction( + const GraphFunction& function, + const CompilationOptions& options); + +TORCH_API IValue convertMobileFunctionToCodeTable( + const mobile::Function& func, + const CompilationOptions& compilation_options); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..5474e48ccf1fc6d4a41fc3c0d3349f07c0d7fad8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * Defines the public API for serializing mobile modules to flatbuffer. + * Note that this header must not include or depend on flatbuffer-defined + * types, to avoid leaking those details to PyTorch clients. + */ + +namespace torch::jit { + +/// Maps file names to file contents. +using ExtraFilesMap = std::unordered_map; + +/** + * Represents a span of data. Typically owned by a UniqueDetachedBuffer. + */ +class TORCH_API DetachedBuffer final { + public: + /// Creates a new DetachedBuffer with an optional data owner. This interface + /// is provided to let users create objects of this type for testing. + DetachedBuffer(void* data, size_t size, void* internal_data_owner = nullptr) + : data_(data), size_(size), data_owner_(internal_data_owner) {} + + /// Returns a pointer to the data. + [[nodiscard]] void* data() { + return data_; + } + /// Returns a pointer to the data. + [[nodiscard]] const void* data() const { + return data_; + } + /// Returns the size of the data, in bytes. + [[nodiscard]] size_t size() const { + return size_; + } + + /// Wrapper type that typically owns data_owner_. + using UniqueDetachedBuffer = + std::unique_ptr>; + + private: + /// Deletes the owner, if present, and the buf itself. + /// Note: we could have provided a movable type with a destructor that did + /// this work, but the unique wrapper was easier in practice. + static void destroy(DetachedBuffer* buf); + + /// Provides access to destroy() for implementation and testing. + friend struct DetachedBufferFriend; + friend struct DetachedBufferTestingFriend; + + /// Pointer to the data. Not owned by this class. + void* data_; + /// The size of `data_`, in bytes. + size_t size_; + /// Opaque pointer to the underlying owner of `data_`. This class + /// (DetachedBuffer) does not own the owner or the data. It will typically be + /// owned by a UniqueDetachedBuffer that knows how to delete the owner along + /// with this class. + void* data_owner_; +}; + +TORCH_API void save_mobile_module( + const mobile::Module& module, + const std::string& filename, + const ExtraFilesMap& extra_files = ExtraFilesMap(), + const ExtraFilesMap& jit_sources = ExtraFilesMap(), + const std::vector& jit_constants = {}); + +TORCH_API DetachedBuffer::UniqueDetachedBuffer save_mobile_module_to_bytes( + const mobile::Module& module, + const ExtraFilesMap& extra_files = ExtraFilesMap(), + const ExtraFilesMap& jit_sources = ExtraFilesMap(), + const std::vector& jit_constants = {}); + +TORCH_API void save_mobile_module_to_func( + const mobile::Module& module, + const std::function& writer_func); + +// TODO(qihan): delete +TORCH_API bool register_flatbuffer_serializer(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer_jit.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer_jit.h new file mode 100644 index 0000000000000000000000000000000000000000..17cac01783e352a45a1267aae8f51f17abbc899c --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/flatbuffer_serializer_jit.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::jit { + +TORCH_API bool register_flatbuffer_all(); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import.h new file mode 100644 index 0000000000000000000000000000000000000000..0e2024483f4a0d9542796d2f597a05e571be9b92 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import.h @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace caffe2::serialize { +class ReadAdapterInterface; +} // namespace caffe2::serialize + +namespace torch::jit { + +class DeserializationStorageContext; + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + const std::string& filename, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::istream& in, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::unique_ptr rai, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + const std::string& filename, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true, + bool restore_shapes = false); + +// For reading unified serialization format from torch.Package +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::shared_ptr reader, + std::shared_ptr storage_context, + std::optional device, + const std::string& ts_id /* torchscript identifier inside package */); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::istream& in, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true, + bool restore_shapes = false); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::unique_ptr rai, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true); + +TORCH_API Module import_ir_module( + std::shared_ptr cu, + std::shared_ptr rai, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true); + +/// Loads a serialized `Module` from the given `istream`. +/// +/// The istream must contain a serialized `Module`, exported via +/// `torch::jit::ExportModule` in C++. +TORCH_API Module load( + std::istream& in, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module load( + std::istream& in, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true); + +/// Loads a serialized `Module` from the given `filename`. +/// +/// The file stored at the location given in `filename` must contain a +/// serialized `Module`, exported either via `ScriptModule.save()` in +/// Python or `torch::jit::ExportModule` in C++. +TORCH_API Module load( + const std::string& filename, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module load( + const std::string& filename, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true); + +/// Loads a serialized `Module` from the given shared_ptr `rai`. +/// +/// The reader adapter, which is for customized input stream, must contain a +/// serialized `Module`, exported either via `ScriptModule.save()` in +/// Python or `torch::jit::ExportModule` in C++. +TORCH_API Module load( + std::shared_ptr rai, + std::optional device = std::nullopt, + bool load_debug_files = true); + +TORCH_API Module load( + std::shared_ptr rai, + std::optional device, + ExtraFilesMap& extra_files, + bool load_debug_files = true); + +TORCH_API Module jitModuleFromSourceAndConstants( + const IValue& ivalue, + const ExtraFilesMap& source, + const std::vector& constants, + int32_t version); + +TORCH_API Module parse_and_initialize_jit_module( + const std::shared_ptr& data, + size_t size, + ExtraFilesMap& extra_files, + std::optional device = std::nullopt); + +TORCH_API Module load_jit_module_from_file( + const std::string& filename, + ExtraFilesMap& extra_files, + std::optional device = std::nullopt); + +TORCH_API Module load_jit_module_from_stream( + std::istream& in, + ExtraFilesMap& extra_files, + std::optional device = std::nullopt); + +TORCH_API Module parse_and_initialize_jit_module( + const std::shared_ptr& data, + size_t size, + ExtraFilesMap& extra_files, + std::optional device); + +TORCH_API c10::intrusive_ptr ObjLoaderFunc( + const at::StrongTypePtr& type, + IValue input); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_constants.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..dda1bd8fd8bb0da61f6f1dd098cd60414471bd5a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_constants.h @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace torch::jit { +constexpr size_t BYTECODE_INDEX_INSTRUCTION = 0; +constexpr size_t BYTECODE_INDEX_OPERATOR = 1; +constexpr size_t BYTECODE_INDEX_CONSTANT = 2; +constexpr size_t BYTECODE_INDEX_TYPE = 3; +constexpr size_t BYTECODE_INDEX_REGISTER_SIZE = 4; + +constexpr size_t BYTECODE_INDEX_SCHEMA_ARGUMENTS = 0; +constexpr size_t BYTECODE_INDEX_SCHEMA_RETURNS = 1; + +constexpr size_t BYTECODE_INDEX_ARGUMENT_NAME = 0; +constexpr size_t BYTECODE_INDEX_ARGUMENT_TYPE = 1; +constexpr size_t BYTECODE_INDEX_ARGUMENT_DEFAULT_VALUE = 2; + +constexpr size_t BYTECODE_INDEX_MODULE_DEBUG_HANDLES = 0; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_functions.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_functions.h new file mode 100644 index 0000000000000000000000000000000000000000..b267f4924e1b8032159546afbe6a1778214f1401 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_functions.h @@ -0,0 +1,15 @@ +#pragma once +#include + +// Functions that are used in both import and export processes + +namespace torch::jit { +using c10::IValue; +IValue expect_field( + c10::ivalue::TupleElements& elements, + const std::string& expected_name, + size_t entry); +std::string operator_str( + const std::string& name, + const std::string& overloadname); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_helpers.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..4cd2bd8c43c8b84c70d1b8bbf009c10bb5a5081a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_export_helpers.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace caffe2::serialize { +class PyTorchStreamReader; +} + +namespace torch::jit { + +struct Source; + +// Convert a class type's qualifier name to the corresponding path the source +// file it should be written to. +// +// Qualifier is like: foo.bar.baz +// Returns: libs/foo/bar/baz.py +std::string qualifierToArchivePath( + const std::string& qualifier, + const std::string& export_prefix); + +std::shared_ptr findSourceInArchiveFromQualifier( + caffe2::serialize::PyTorchStreamReader& reader, + const std::string& export_prefix, + const std::string& qualifier); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_read.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_read.h new file mode 100644 index 0000000000000000000000000000000000000000..90629bc86736f159453329f87c4c6e18d5a1bd65 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_read.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace caffe2::serialize { +class PyTorchStreamReader; +} // namespace caffe2::serialize + +namespace torch::jit { + +TORCH_API IValue readArchiveAndTensors( + const std::string& archive_name, + const std::string& pickle_prefix, + const std::string& tensor_prefix, + std::optional type_resolver, + std::optional obj_loader, + std::optional device, + caffe2::serialize::PyTorchStreamReader& stream_reader, + c10::TypePtr (*type_parser)(const std::string&) = + Unpickler::defaultTypeParser, + std::shared_ptr storage_context = nullptr); + +bool check_zip_file( + const std::shared_ptr& rai); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_source.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_source.h new file mode 100644 index 0000000000000000000000000000000000000000..f8510e6aa851a8bb67f5b527557d254d70f31548 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/import_source.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +using SourceLoader = std::function(const std::string&)>; + +struct SourceImporterImpl : public Resolver, + std::enable_shared_from_this { + SourceImporterImpl( + std::shared_ptr cu, + const std::vector* constant_table, + SourceLoader source_loader, + size_t version); + TypePtr findNamedType(const QualifiedName& name); + Function* findFunction(const QualifiedName& name); + void parseSourceIfNeeded(const std::string& qualifier); + void LEGACY_import_methods( + const Module& mod, + const std::shared_ptr& src); + + std::shared_ptr resolveValue( + const std::string& name, + GraphFunction& m, + const SourceRange& loc) override; + TypePtr resolveType(const std::string& name, const SourceRange& loc) override; + + private: + void importFunction(const std::string& qualifier, const Def& def); + void importNamedType(const std::string& qualifier, const ClassDef& class_def); + std::optional attributeAssignmentSpecialHandlingHack( + const QualifiedName& qualified_classname, + const Assign& assign); + void importClass( + const QualifiedName& qualified_classname, + const ClassDef& class_def, + bool is_module); + void importEnum( + const QualifiedName& qualified_name, + const ClassDef& enum_def); + void importNamedTuple( + const QualifiedName& qualified_name, + const ClassDef& named_tuple_def); + + void parsePossibleVersionNumber(Lexer& L); + + void parseImports(Lexer& L); + + std::shared_ptr cu_; + std::unordered_map> env_; + SourceLoader source_loader_; + std::optional version_ = std::nullopt; + std::unordered_set loaded_sources_; + // named types and functions loaded from a file but not yet defined because + // their type has not been requested yet. + std::unordered_map to_be_defined_; +}; + +// Given a directory of serialized TorchScript sources, +// This class allows the loading of individual named types in source. +// Resolves the dependencies between source files and parses +// the source files as necessary. + +struct TORCH_API SourceImporter { + SourceImporter( + // The compilation unit that will own the imported source + std::shared_ptr cu, + const std::vector* constant_table, + SourceLoader loader, + size_t version); + + TypePtr loadType(const QualifiedName& name) const; + + // Add the methods defined in `src` to the module `mod`, using SourceImporter + // to resolve any classes via loadType + void LEGACY_import_methods( + const Module& mod, + const std::shared_ptr& src); + ~SourceImporter(); + + private: + std::shared_ptr pImpl; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/mobile_bytecode_generated.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/mobile_bytecode_generated.h new file mode 100644 index 0000000000000000000000000000000000000000..cffe8bc7a636457ea3452fba9c040a71cb71fa5e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/mobile_bytecode_generated.h @@ -0,0 +1,2599 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_MOBILEBYTECODE_TORCH_JIT_MOBILE_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_MOBILEBYTECODE_TORCH_JIT_MOBILE_SERIALIZATION_H_ + +#include "flatbuffers/flatbuffers.h" + +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 3 && + FLATBUFFERS_VERSION_REVISION == 3, + "Non-compatible flatbuffers version included"); + +namespace torch { +namespace jit { +namespace mobile { +namespace serialization { + +struct Int; + +struct Bool; + +struct Double; + +struct PerTensorAffineSchema; + +struct QuantizedSchema; +struct QuantizedSchemaBuilder; + +struct TensorMetadata; +struct TensorMetadataBuilder; + +struct String; +struct StringBuilder; + +struct Device; +struct DeviceBuilder; + +struct List; +struct ListBuilder; + +struct IntList; +struct IntListBuilder; + +struct DoubleList; +struct DoubleListBuilder; + +struct BoolList; +struct BoolListBuilder; + +struct Tuple; +struct TupleBuilder; + +struct Dict; +struct DictBuilder; + +struct ObjectType; +struct ObjectTypeBuilder; + +struct Object; +struct ObjectBuilder; + +struct ComplexDouble; + +struct EnumValue; +struct EnumValueBuilder; + +struct Instruction; + +struct Operator; +struct OperatorBuilder; + +struct Arg; +struct ArgBuilder; + +struct Schema; +struct SchemaBuilder; + +struct DebugInfo; +struct DebugInfoBuilder; + +struct Function; +struct FunctionBuilder; + +struct StorageData; +struct StorageDataBuilder; + +struct IValue; +struct IValueBuilder; + +struct ExtraFile; +struct ExtraFileBuilder; + +struct Module; +struct ModuleBuilder; + +enum class TypeType : uint8_t { + UNSET = 0, + CLASS_WITH_FIELD = 1, + CUSTOM_CLASS = 2, + CLASS_WITH_SETSTATE = 3, + NON_OBJ = 4, + MIN = UNSET, + MAX = NON_OBJ +}; + +inline const TypeType (&EnumValuesTypeType())[5] { + static const TypeType values[] = { + TypeType::UNSET, + TypeType::CLASS_WITH_FIELD, + TypeType::CUSTOM_CLASS, + TypeType::CLASS_WITH_SETSTATE, + TypeType::NON_OBJ + }; + return values; +} + +inline const char * const *EnumNamesTypeType() { + static const char * const names[6] = { + "UNSET", + "CLASS_WITH_FIELD", + "CUSTOM_CLASS", + "CLASS_WITH_SETSTATE", + "NON_OBJ", + nullptr + }; + return names; +} + +inline const char *EnumNameTypeType(TypeType e) { + if (::flatbuffers::IsOutRange(e, TypeType::UNSET, TypeType::NON_OBJ)) return ""; + const size_t index = static_cast(e); + return EnumNamesTypeType()[index]; +} + +enum class IValueUnion : uint8_t { + NONE = 0, + Int = 1, + Bool = 2, + Double = 3, + ComplexDouble = 4, + TensorMetadata = 5, + String = 6, + List = 7, + Tuple = 8, + Dict = 9, + Object = 10, + IntList = 11, + DoubleList = 12, + BoolList = 13, + Device = 14, + EnumValue = 15, + Function = 16, + MIN = NONE, + MAX = Function +}; + +inline const IValueUnion (&EnumValuesIValueUnion())[17] { + static const IValueUnion values[] = { + IValueUnion::NONE, + IValueUnion::Int, + IValueUnion::Bool, + IValueUnion::Double, + IValueUnion::ComplexDouble, + IValueUnion::TensorMetadata, + IValueUnion::String, + IValueUnion::List, + IValueUnion::Tuple, + IValueUnion::Dict, + IValueUnion::Object, + IValueUnion::IntList, + IValueUnion::DoubleList, + IValueUnion::BoolList, + IValueUnion::Device, + IValueUnion::EnumValue, + IValueUnion::Function + }; + return values; +} + +inline const char * const *EnumNamesIValueUnion() { + static const char * const names[18] = { + "NONE", + "Int", + "Bool", + "Double", + "ComplexDouble", + "TensorMetadata", + "String", + "List", + "Tuple", + "Dict", + "Object", + "IntList", + "DoubleList", + "BoolList", + "Device", + "EnumValue", + "Function", + nullptr + }; + return names; +} + +inline const char *EnumNameIValueUnion(IValueUnion e) { + if (::flatbuffers::IsOutRange(e, IValueUnion::NONE, IValueUnion::Function)) return ""; + const size_t index = static_cast(e); + return EnumNamesIValueUnion()[index]; +} + +template struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::NONE; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Int; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Bool; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Double; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::ComplexDouble; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::TensorMetadata; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::String; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::List; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Tuple; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Dict; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Object; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::IntList; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::DoubleList; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::BoolList; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Device; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::EnumValue; +}; + +template<> struct IValueUnionTraits { + static const IValueUnion enum_value = IValueUnion::Function; +}; + +bool VerifyIValueUnion(::flatbuffers::Verifier &verifier, const void *obj, IValueUnion type); +bool VerifyIValueUnionVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Int FLATBUFFERS_FINAL_CLASS { + private: + int64_t int_val_; + + public: + Int() + : int_val_(0) { + } + Int(int64_t _int_val) + : int_val_(::flatbuffers::EndianScalar(_int_val)) { + } + int64_t int_val() const { + return ::flatbuffers::EndianScalar(int_val_); + } + void mutate_int_val(int64_t _int_val) { + ::flatbuffers::WriteScalar(&int_val_, _int_val); + } +}; +FLATBUFFERS_STRUCT_END(Int, 8); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(1) Bool FLATBUFFERS_FINAL_CLASS { + private: + uint8_t bool_val_; + + public: + Bool() + : bool_val_(0) { + } + Bool(bool _bool_val) + : bool_val_(::flatbuffers::EndianScalar(static_cast(_bool_val))) { + } + bool bool_val() const { + return ::flatbuffers::EndianScalar(bool_val_) != 0; + } + void mutate_bool_val(bool _bool_val) { + ::flatbuffers::WriteScalar(&bool_val_, static_cast(_bool_val)); + } +}; +FLATBUFFERS_STRUCT_END(Bool, 1); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Double FLATBUFFERS_FINAL_CLASS { + private: + double double_val_; + + public: + Double() + : double_val_(0) { + } + Double(double _double_val) + : double_val_(::flatbuffers::EndianScalar(_double_val)) { + } + double double_val() const { + return ::flatbuffers::EndianScalar(double_val_); + } + void mutate_double_val(double _double_val) { + ::flatbuffers::WriteScalar(&double_val_, _double_val); + } +}; +FLATBUFFERS_STRUCT_END(Double, 8); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) PerTensorAffineSchema FLATBUFFERS_FINAL_CLASS { + private: + double q_scale_; + int32_t q_zero_point_; + int32_t padding0__; + + public: + PerTensorAffineSchema() + : q_scale_(0), + q_zero_point_(0), + padding0__(0) { + (void)padding0__; + } + PerTensorAffineSchema(double _q_scale, int32_t _q_zero_point) + : q_scale_(::flatbuffers::EndianScalar(_q_scale)), + q_zero_point_(::flatbuffers::EndianScalar(_q_zero_point)), + padding0__(0) { + (void)padding0__; + } + double q_scale() const { + return ::flatbuffers::EndianScalar(q_scale_); + } + void mutate_q_scale(double _q_scale) { + ::flatbuffers::WriteScalar(&q_scale_, _q_scale); + } + int32_t q_zero_point() const { + return ::flatbuffers::EndianScalar(q_zero_point_); + } + void mutate_q_zero_point(int32_t _q_zero_point) { + ::flatbuffers::WriteScalar(&q_zero_point_, _q_zero_point); + } +}; +FLATBUFFERS_STRUCT_END(PerTensorAffineSchema, 16); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ComplexDouble FLATBUFFERS_FINAL_CLASS { + private: + double real_; + double imag_; + + public: + ComplexDouble() + : real_(0), + imag_(0) { + } + ComplexDouble(double _real, double _imag) + : real_(::flatbuffers::EndianScalar(_real)), + imag_(::flatbuffers::EndianScalar(_imag)) { + } + double real() const { + return ::flatbuffers::EndianScalar(real_); + } + void mutate_real(double _real) { + ::flatbuffers::WriteScalar(&real_, _real); + } + double imag() const { + return ::flatbuffers::EndianScalar(imag_); + } + void mutate_imag(double _imag) { + ::flatbuffers::WriteScalar(&imag_, _imag); + } +}; +FLATBUFFERS_STRUCT_END(ComplexDouble, 16); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Instruction FLATBUFFERS_FINAL_CLASS { + private: + int8_t op_; + int8_t padding0__; + uint16_t n_; + int32_t x_; + + public: + Instruction() + : op_(0), + padding0__(0), + n_(0), + x_(0) { + (void)padding0__; + } + Instruction(int8_t _op, uint16_t _n, int32_t _x) + : op_(::flatbuffers::EndianScalar(_op)), + padding0__(0), + n_(::flatbuffers::EndianScalar(_n)), + x_(::flatbuffers::EndianScalar(_x)) { + (void)padding0__; + } + int8_t op() const { + return ::flatbuffers::EndianScalar(op_); + } + void mutate_op(int8_t _op) { + ::flatbuffers::WriteScalar(&op_, _op); + } + uint16_t n() const { + return ::flatbuffers::EndianScalar(n_); + } + void mutate_n(uint16_t _n) { + ::flatbuffers::WriteScalar(&n_, _n); + } + int32_t x() const { + return ::flatbuffers::EndianScalar(x_); + } + void mutate_x(int32_t _x) { + ::flatbuffers::WriteScalar(&x_, _x); + } +}; +FLATBUFFERS_STRUCT_END(Instruction, 8); + +struct QuantizedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef QuantizedSchemaBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_QSCHEME = 4, + VT_SCALE = 6, + VT_ZERO_POINT = 8, + VT_SCALES = 10, + VT_ZERO_POINTS = 12, + VT_AXIS = 14 + }; + int8_t qscheme() const { + return GetField(VT_QSCHEME, 0); + } + bool mutate_qscheme(int8_t _qscheme = 0) { + return SetField(VT_QSCHEME, _qscheme, 0); + } + double scale() const { + return GetField(VT_SCALE, 0.0); + } + bool mutate_scale(double _scale = 0.0) { + return SetField(VT_SCALE, _scale, 0.0); + } + int32_t zero_point() const { + return GetField(VT_ZERO_POINT, 0); + } + bool mutate_zero_point(int32_t _zero_point = 0) { + return SetField(VT_ZERO_POINT, _zero_point, 0); + } + const torch::jit::mobile::serialization::TensorMetadata *scales() const { + return GetPointer(VT_SCALES); + } + torch::jit::mobile::serialization::TensorMetadata *mutable_scales() { + return GetPointer(VT_SCALES); + } + const torch::jit::mobile::serialization::TensorMetadata *zero_points() const { + return GetPointer(VT_ZERO_POINTS); + } + torch::jit::mobile::serialization::TensorMetadata *mutable_zero_points() { + return GetPointer(VT_ZERO_POINTS); + } + int32_t axis() const { + return GetField(VT_AXIS, 0); + } + bool mutate_axis(int32_t _axis = 0) { + return SetField(VT_AXIS, _axis, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_QSCHEME, 1) && + VerifyField(verifier, VT_SCALE, 8) && + VerifyField(verifier, VT_ZERO_POINT, 4) && + VerifyOffset(verifier, VT_SCALES) && + verifier.VerifyTable(scales()) && + VerifyOffset(verifier, VT_ZERO_POINTS) && + verifier.VerifyTable(zero_points()) && + VerifyField(verifier, VT_AXIS, 4) && + verifier.EndTable(); + } +}; + +struct QuantizedSchemaBuilder { + typedef QuantizedSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_qscheme(int8_t qscheme) { + fbb_.AddElement(QuantizedSchema::VT_QSCHEME, qscheme, 0); + } + void add_scale(double scale) { + fbb_.AddElement(QuantizedSchema::VT_SCALE, scale, 0.0); + } + void add_zero_point(int32_t zero_point) { + fbb_.AddElement(QuantizedSchema::VT_ZERO_POINT, zero_point, 0); + } + void add_scales(::flatbuffers::Offset scales) { + fbb_.AddOffset(QuantizedSchema::VT_SCALES, scales); + } + void add_zero_points(::flatbuffers::Offset zero_points) { + fbb_.AddOffset(QuantizedSchema::VT_ZERO_POINTS, zero_points); + } + void add_axis(int32_t axis) { + fbb_.AddElement(QuantizedSchema::VT_AXIS, axis, 0); + } + explicit QuantizedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateQuantizedSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + int8_t qscheme = 0, + double scale = 0.0, + int32_t zero_point = 0, + ::flatbuffers::Offset scales = 0, + ::flatbuffers::Offset zero_points = 0, + int32_t axis = 0) { + QuantizedSchemaBuilder builder_(_fbb); + builder_.add_scale(scale); + builder_.add_axis(axis); + builder_.add_zero_points(zero_points); + builder_.add_scales(scales); + builder_.add_zero_point(zero_point); + builder_.add_qscheme(qscheme); + return builder_.Finish(); +} + +struct TensorMetadata FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef TensorMetadataBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_STORAGE_LOCATION_INDEX = 4, + VT_SCALAR_TYPE = 6, + VT_STORAGE_OFFSET = 8, + VT_SIZES = 10, + VT_STRIDES = 12, + VT_REQUIRES_GRAD = 14, + VT_QUANTIZED_SCHEMA = 16 + }; + uint32_t storage_location_index() const { + return GetField(VT_STORAGE_LOCATION_INDEX, 0); + } + bool mutate_storage_location_index(uint32_t _storage_location_index = 0) { + return SetField(VT_STORAGE_LOCATION_INDEX, _storage_location_index, 0); + } + int8_t scalar_type() const { + return GetField(VT_SCALAR_TYPE, 0); + } + bool mutate_scalar_type(int8_t _scalar_type = 0) { + return SetField(VT_SCALAR_TYPE, _scalar_type, 0); + } + int32_t storage_offset() const { + return GetField(VT_STORAGE_OFFSET, 0); + } + bool mutate_storage_offset(int32_t _storage_offset = 0) { + return SetField(VT_STORAGE_OFFSET, _storage_offset, 0); + } + const ::flatbuffers::Vector *sizes() const { + return GetPointer *>(VT_SIZES); + } + ::flatbuffers::Vector *mutable_sizes() { + return GetPointer<::flatbuffers::Vector *>(VT_SIZES); + } + const ::flatbuffers::Vector *strides() const { + return GetPointer *>(VT_STRIDES); + } + ::flatbuffers::Vector *mutable_strides() { + return GetPointer<::flatbuffers::Vector *>(VT_STRIDES); + } + bool requires_grad() const { + return GetField(VT_REQUIRES_GRAD, 0) != 0; + } + bool mutate_requires_grad(bool _requires_grad = 0) { + return SetField(VT_REQUIRES_GRAD, static_cast(_requires_grad), 0); + } + const torch::jit::mobile::serialization::QuantizedSchema *quantized_schema() const { + return GetPointer(VT_QUANTIZED_SCHEMA); + } + torch::jit::mobile::serialization::QuantizedSchema *mutable_quantized_schema() { + return GetPointer(VT_QUANTIZED_SCHEMA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_STORAGE_LOCATION_INDEX, 4) && + VerifyField(verifier, VT_SCALAR_TYPE, 1) && + VerifyField(verifier, VT_STORAGE_OFFSET, 4) && + VerifyOffset(verifier, VT_SIZES) && + verifier.VerifyVector(sizes()) && + VerifyOffset(verifier, VT_STRIDES) && + verifier.VerifyVector(strides()) && + VerifyField(verifier, VT_REQUIRES_GRAD, 1) && + VerifyOffset(verifier, VT_QUANTIZED_SCHEMA) && + verifier.VerifyTable(quantized_schema()) && + verifier.EndTable(); + } +}; + +struct TensorMetadataBuilder { + typedef TensorMetadata Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_storage_location_index(uint32_t storage_location_index) { + fbb_.AddElement(TensorMetadata::VT_STORAGE_LOCATION_INDEX, storage_location_index, 0); + } + void add_scalar_type(int8_t scalar_type) { + fbb_.AddElement(TensorMetadata::VT_SCALAR_TYPE, scalar_type, 0); + } + void add_storage_offset(int32_t storage_offset) { + fbb_.AddElement(TensorMetadata::VT_STORAGE_OFFSET, storage_offset, 0); + } + void add_sizes(::flatbuffers::Offset<::flatbuffers::Vector> sizes) { + fbb_.AddOffset(TensorMetadata::VT_SIZES, sizes); + } + void add_strides(::flatbuffers::Offset<::flatbuffers::Vector> strides) { + fbb_.AddOffset(TensorMetadata::VT_STRIDES, strides); + } + void add_requires_grad(bool requires_grad) { + fbb_.AddElement(TensorMetadata::VT_REQUIRES_GRAD, static_cast(requires_grad), 0); + } + void add_quantized_schema(::flatbuffers::Offset quantized_schema) { + fbb_.AddOffset(TensorMetadata::VT_QUANTIZED_SCHEMA, quantized_schema); + } + explicit TensorMetadataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateTensorMetadata( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t storage_location_index = 0, + int8_t scalar_type = 0, + int32_t storage_offset = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> sizes = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> strides = 0, + bool requires_grad = false, + ::flatbuffers::Offset quantized_schema = 0) { + TensorMetadataBuilder builder_(_fbb); + builder_.add_quantized_schema(quantized_schema); + builder_.add_strides(strides); + builder_.add_sizes(sizes); + builder_.add_storage_offset(storage_offset); + builder_.add_storage_location_index(storage_location_index); + builder_.add_requires_grad(requires_grad); + builder_.add_scalar_type(scalar_type); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateTensorMetadataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t storage_location_index = 0, + int8_t scalar_type = 0, + int32_t storage_offset = 0, + const std::vector *sizes = nullptr, + const std::vector *strides = nullptr, + bool requires_grad = false, + ::flatbuffers::Offset quantized_schema = 0) { + auto sizes__ = sizes ? _fbb.CreateVector(*sizes) : 0; + auto strides__ = strides ? _fbb.CreateVector(*strides) : 0; + return torch::jit::mobile::serialization::CreateTensorMetadata( + _fbb, + storage_location_index, + scalar_type, + storage_offset, + sizes__, + strides__, + requires_grad, + quantized_schema); +} + +struct String FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef StringBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::String *data() const { + return GetPointer(VT_DATA); + } + ::flatbuffers::String *mutable_data() { + return GetPointer<::flatbuffers::String *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyString(data()) && + verifier.EndTable(); + } +}; + +struct StringBuilder { + typedef String Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::String> data) { + fbb_.AddOffset(String::VT_DATA, data); + } + explicit StringBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateString( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> data = 0) { + StringBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateStringDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *data = nullptr) { + auto data__ = data ? _fbb.CreateString(data) : 0; + return torch::jit::mobile::serialization::CreateString( + _fbb, + data__); +} + +struct Device FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef DeviceBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_STR = 4 + }; + const ::flatbuffers::String *str() const { + return GetPointer(VT_STR); + } + ::flatbuffers::String *mutable_str() { + return GetPointer<::flatbuffers::String *>(VT_STR); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_STR) && + verifier.VerifyString(str()) && + verifier.EndTable(); + } +}; + +struct DeviceBuilder { + typedef Device Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_str(::flatbuffers::Offset<::flatbuffers::String> str) { + fbb_.AddOffset(Device::VT_STR, str); + } + explicit DeviceBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateDevice( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> str = 0) { + DeviceBuilder builder_(_fbb); + builder_.add_str(str); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateDeviceDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *str = nullptr) { + auto str__ = str ? _fbb.CreateString(str) : 0; + return torch::jit::mobile::serialization::CreateDevice( + _fbb, + str__); +} + +struct List FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ListBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ITEMS = 4, + VT_ANNOTATION_STR = 6 + }; + const ::flatbuffers::Vector *items() const { + return GetPointer *>(VT_ITEMS); + } + ::flatbuffers::Vector *mutable_items() { + return GetPointer<::flatbuffers::Vector *>(VT_ITEMS); + } + const ::flatbuffers::String *annotation_str() const { + return GetPointer(VT_ANNOTATION_STR); + } + ::flatbuffers::String *mutable_annotation_str() { + return GetPointer<::flatbuffers::String *>(VT_ANNOTATION_STR); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ITEMS) && + verifier.VerifyVector(items()) && + VerifyOffset(verifier, VT_ANNOTATION_STR) && + verifier.VerifyString(annotation_str()) && + verifier.EndTable(); + } +}; + +struct ListBuilder { + typedef List Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_items(::flatbuffers::Offset<::flatbuffers::Vector> items) { + fbb_.AddOffset(List::VT_ITEMS, items); + } + void add_annotation_str(::flatbuffers::Offset<::flatbuffers::String> annotation_str) { + fbb_.AddOffset(List::VT_ANNOTATION_STR, annotation_str); + } + explicit ListBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateList( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> items = 0, + ::flatbuffers::Offset<::flatbuffers::String> annotation_str = 0) { + ListBuilder builder_(_fbb); + builder_.add_annotation_str(annotation_str); + builder_.add_items(items); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateListDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *items = nullptr, + const char *annotation_str = nullptr) { + auto items__ = items ? _fbb.CreateVector(*items) : 0; + auto annotation_str__ = annotation_str ? _fbb.CreateString(annotation_str) : 0; + return torch::jit::mobile::serialization::CreateList( + _fbb, + items__, + annotation_str__); +} + +struct IntList FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef IntListBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ITEMS = 4 + }; + const ::flatbuffers::Vector *items() const { + return GetPointer *>(VT_ITEMS); + } + ::flatbuffers::Vector *mutable_items() { + return GetPointer<::flatbuffers::Vector *>(VT_ITEMS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ITEMS) && + verifier.VerifyVector(items()) && + verifier.EndTable(); + } +}; + +struct IntListBuilder { + typedef IntList Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_items(::flatbuffers::Offset<::flatbuffers::Vector> items) { + fbb_.AddOffset(IntList::VT_ITEMS, items); + } + explicit IntListBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateIntList( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> items = 0) { + IntListBuilder builder_(_fbb); + builder_.add_items(items); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateIntListDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *items = nullptr) { + auto items__ = items ? _fbb.CreateVector(*items) : 0; + return torch::jit::mobile::serialization::CreateIntList( + _fbb, + items__); +} + +struct DoubleList FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef DoubleListBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ITEMS = 4 + }; + const ::flatbuffers::Vector *items() const { + return GetPointer *>(VT_ITEMS); + } + ::flatbuffers::Vector *mutable_items() { + return GetPointer<::flatbuffers::Vector *>(VT_ITEMS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ITEMS) && + verifier.VerifyVector(items()) && + verifier.EndTable(); + } +}; + +struct DoubleListBuilder { + typedef DoubleList Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_items(::flatbuffers::Offset<::flatbuffers::Vector> items) { + fbb_.AddOffset(DoubleList::VT_ITEMS, items); + } + explicit DoubleListBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateDoubleList( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> items = 0) { + DoubleListBuilder builder_(_fbb); + builder_.add_items(items); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateDoubleListDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *items = nullptr) { + auto items__ = items ? _fbb.CreateVector(*items) : 0; + return torch::jit::mobile::serialization::CreateDoubleList( + _fbb, + items__); +} + +struct BoolList FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef BoolListBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ITEMS = 4 + }; + const ::flatbuffers::Vector *items() const { + return GetPointer *>(VT_ITEMS); + } + ::flatbuffers::Vector *mutable_items() { + return GetPointer<::flatbuffers::Vector *>(VT_ITEMS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ITEMS) && + verifier.VerifyVector(items()) && + verifier.EndTable(); + } +}; + +struct BoolListBuilder { + typedef BoolList Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_items(::flatbuffers::Offset<::flatbuffers::Vector> items) { + fbb_.AddOffset(BoolList::VT_ITEMS, items); + } + explicit BoolListBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateBoolList( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> items = 0) { + BoolListBuilder builder_(_fbb); + builder_.add_items(items); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateBoolListDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *items = nullptr) { + auto items__ = items ? _fbb.CreateVector(*items) : 0; + return torch::jit::mobile::serialization::CreateBoolList( + _fbb, + items__); +} + +struct Tuple FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef TupleBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ITEMS = 4 + }; + const ::flatbuffers::Vector *items() const { + return GetPointer *>(VT_ITEMS); + } + ::flatbuffers::Vector *mutable_items() { + return GetPointer<::flatbuffers::Vector *>(VT_ITEMS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ITEMS) && + verifier.VerifyVector(items()) && + verifier.EndTable(); + } +}; + +struct TupleBuilder { + typedef Tuple Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_items(::flatbuffers::Offset<::flatbuffers::Vector> items) { + fbb_.AddOffset(Tuple::VT_ITEMS, items); + } + explicit TupleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateTuple( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> items = 0) { + TupleBuilder builder_(_fbb); + builder_.add_items(items); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateTupleDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *items = nullptr) { + auto items__ = items ? _fbb.CreateVector(*items) : 0; + return torch::jit::mobile::serialization::CreateTuple( + _fbb, + items__); +} + +struct Dict FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef DictBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_KEYS = 4, + VT_VALUES = 6, + VT_ANNOTATION_STR = 8 + }; + const ::flatbuffers::Vector *keys() const { + return GetPointer *>(VT_KEYS); + } + ::flatbuffers::Vector *mutable_keys() { + return GetPointer<::flatbuffers::Vector *>(VT_KEYS); + } + const ::flatbuffers::Vector *values() const { + return GetPointer *>(VT_VALUES); + } + ::flatbuffers::Vector *mutable_values() { + return GetPointer<::flatbuffers::Vector *>(VT_VALUES); + } + const ::flatbuffers::String *annotation_str() const { + return GetPointer(VT_ANNOTATION_STR); + } + ::flatbuffers::String *mutable_annotation_str() { + return GetPointer<::flatbuffers::String *>(VT_ANNOTATION_STR); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_KEYS) && + verifier.VerifyVector(keys()) && + VerifyOffset(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && + VerifyOffset(verifier, VT_ANNOTATION_STR) && + verifier.VerifyString(annotation_str()) && + verifier.EndTable(); + } +}; + +struct DictBuilder { + typedef Dict Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_keys(::flatbuffers::Offset<::flatbuffers::Vector> keys) { + fbb_.AddOffset(Dict::VT_KEYS, keys); + } + void add_values(::flatbuffers::Offset<::flatbuffers::Vector> values) { + fbb_.AddOffset(Dict::VT_VALUES, values); + } + void add_annotation_str(::flatbuffers::Offset<::flatbuffers::String> annotation_str) { + fbb_.AddOffset(Dict::VT_ANNOTATION_STR, annotation_str); + } + explicit DictBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateDict( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> keys = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> values = 0, + ::flatbuffers::Offset<::flatbuffers::String> annotation_str = 0) { + DictBuilder builder_(_fbb); + builder_.add_annotation_str(annotation_str); + builder_.add_values(values); + builder_.add_keys(keys); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateDictDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *keys = nullptr, + const std::vector *values = nullptr, + const char *annotation_str = nullptr) { + auto keys__ = keys ? _fbb.CreateVector(*keys) : 0; + auto values__ = values ? _fbb.CreateVector(*values) : 0; + auto annotation_str__ = annotation_str ? _fbb.CreateString(annotation_str) : 0; + return torch::jit::mobile::serialization::CreateDict( + _fbb, + keys__, + values__, + annotation_str__); +} + +struct ObjectType FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ObjectTypeBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TYPE_NAME = 4, + VT_TYPE = 6, + VT_ATTR_NAMES = 8 + }; + const ::flatbuffers::String *type_name() const { + return GetPointer(VT_TYPE_NAME); + } + ::flatbuffers::String *mutable_type_name() { + return GetPointer<::flatbuffers::String *>(VT_TYPE_NAME); + } + torch::jit::mobile::serialization::TypeType type() const { + return static_cast(GetField(VT_TYPE, 0)); + } + bool mutate_type(torch::jit::mobile::serialization::TypeType _type = static_cast(0)) { + return SetField(VT_TYPE, static_cast(_type), 0); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *attr_names() const { + return GetPointer> *>(VT_ATTR_NAMES); + } + ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *mutable_attr_names() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_ATTR_NAMES); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_TYPE_NAME) && + verifier.VerifyString(type_name()) && + VerifyField(verifier, VT_TYPE, 1) && + VerifyOffset(verifier, VT_ATTR_NAMES) && + verifier.VerifyVector(attr_names()) && + verifier.VerifyVectorOfStrings(attr_names()) && + verifier.EndTable(); + } +}; + +struct ObjectTypeBuilder { + typedef ObjectType Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_type_name(::flatbuffers::Offset<::flatbuffers::String> type_name) { + fbb_.AddOffset(ObjectType::VT_TYPE_NAME, type_name); + } + void add_type(torch::jit::mobile::serialization::TypeType type) { + fbb_.AddElement(ObjectType::VT_TYPE, static_cast(type), 0); + } + void add_attr_names(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> attr_names) { + fbb_.AddOffset(ObjectType::VT_ATTR_NAMES, attr_names); + } + explicit ObjectTypeBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateObjectType( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> type_name = 0, + torch::jit::mobile::serialization::TypeType type = torch::jit::mobile::serialization::TypeType::UNSET, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> attr_names = 0) { + ObjectTypeBuilder builder_(_fbb); + builder_.add_attr_names(attr_names); + builder_.add_type_name(type_name); + builder_.add_type(type); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateObjectTypeDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *type_name = nullptr, + torch::jit::mobile::serialization::TypeType type = torch::jit::mobile::serialization::TypeType::UNSET, + const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *attr_names = nullptr) { + auto type_name__ = type_name ? _fbb.CreateString(type_name) : 0; + auto attr_names__ = attr_names ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*attr_names) : 0; + return torch::jit::mobile::serialization::CreateObjectType( + _fbb, + type_name__, + type, + attr_names__); +} + +struct Object FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ObjectBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TYPE_INDEX = 4, + VT_STATE = 6, + VT_ATTRS = 8, + VT_SETSTATE_FUNC = 10 + }; + uint32_t type_index() const { + return GetField(VT_TYPE_INDEX, 0); + } + bool mutate_type_index(uint32_t _type_index = 0) { + return SetField(VT_TYPE_INDEX, _type_index, 0); + } + uint32_t state() const { + return GetField(VT_STATE, 0); + } + bool mutate_state(uint32_t _state = 0) { + return SetField(VT_STATE, _state, 0); + } + const ::flatbuffers::Vector *attrs() const { + return GetPointer *>(VT_ATTRS); + } + ::flatbuffers::Vector *mutable_attrs() { + return GetPointer<::flatbuffers::Vector *>(VT_ATTRS); + } + uint32_t setstate_func() const { + return GetField(VT_SETSTATE_FUNC, 0); + } + bool mutate_setstate_func(uint32_t _setstate_func = 0) { + return SetField(VT_SETSTATE_FUNC, _setstate_func, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_TYPE_INDEX, 4) && + VerifyField(verifier, VT_STATE, 4) && + VerifyOffset(verifier, VT_ATTRS) && + verifier.VerifyVector(attrs()) && + VerifyField(verifier, VT_SETSTATE_FUNC, 4) && + verifier.EndTable(); + } +}; + +struct ObjectBuilder { + typedef Object Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_type_index(uint32_t type_index) { + fbb_.AddElement(Object::VT_TYPE_INDEX, type_index, 0); + } + void add_state(uint32_t state) { + fbb_.AddElement(Object::VT_STATE, state, 0); + } + void add_attrs(::flatbuffers::Offset<::flatbuffers::Vector> attrs) { + fbb_.AddOffset(Object::VT_ATTRS, attrs); + } + void add_setstate_func(uint32_t setstate_func) { + fbb_.AddElement(Object::VT_SETSTATE_FUNC, setstate_func, 0); + } + explicit ObjectBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateObject( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t type_index = 0, + uint32_t state = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> attrs = 0, + uint32_t setstate_func = 0) { + ObjectBuilder builder_(_fbb); + builder_.add_setstate_func(setstate_func); + builder_.add_attrs(attrs); + builder_.add_state(state); + builder_.add_type_index(type_index); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateObjectDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t type_index = 0, + uint32_t state = 0, + const std::vector *attrs = nullptr, + uint32_t setstate_func = 0) { + auto attrs__ = attrs ? _fbb.CreateVector(*attrs) : 0; + return torch::jit::mobile::serialization::CreateObject( + _fbb, + type_index, + state, + attrs__, + setstate_func); +} + +struct EnumValue FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef EnumValueBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TYPE_NAME = 4, + VT_VALUE = 6 + }; + const ::flatbuffers::String *type_name() const { + return GetPointer(VT_TYPE_NAME); + } + ::flatbuffers::String *mutable_type_name() { + return GetPointer<::flatbuffers::String *>(VT_TYPE_NAME); + } + uint32_t value() const { + return GetField(VT_VALUE, 0); + } + bool mutate_value(uint32_t _value = 0) { + return SetField(VT_VALUE, _value, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_TYPE_NAME) && + verifier.VerifyString(type_name()) && + VerifyField(verifier, VT_VALUE, 4) && + verifier.EndTable(); + } +}; + +struct EnumValueBuilder { + typedef EnumValue Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_type_name(::flatbuffers::Offset<::flatbuffers::String> type_name) { + fbb_.AddOffset(EnumValue::VT_TYPE_NAME, type_name); + } + void add_value(uint32_t value) { + fbb_.AddElement(EnumValue::VT_VALUE, value, 0); + } + explicit EnumValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateEnumValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> type_name = 0, + uint32_t value = 0) { + EnumValueBuilder builder_(_fbb); + builder_.add_value(value); + builder_.add_type_name(type_name); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateEnumValueDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *type_name = nullptr, + uint32_t value = 0) { + auto type_name__ = type_name ? _fbb.CreateString(type_name) : 0; + return torch::jit::mobile::serialization::CreateEnumValue( + _fbb, + type_name__, + value); +} + +struct Operator FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef OperatorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_OVERLOAD_NAME = 6, + VT_NUM_ARGS_SERIALIZED = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + ::flatbuffers::String *mutable_name() { + return GetPointer<::flatbuffers::String *>(VT_NAME); + } + const ::flatbuffers::String *overload_name() const { + return GetPointer(VT_OVERLOAD_NAME); + } + ::flatbuffers::String *mutable_overload_name() { + return GetPointer<::flatbuffers::String *>(VT_OVERLOAD_NAME); + } + int32_t num_args_serialized() const { + return GetField(VT_NUM_ARGS_SERIALIZED, -1); + } + bool mutate_num_args_serialized(int32_t _num_args_serialized = -1) { + return SetField(VT_NUM_ARGS_SERIALIZED, _num_args_serialized, -1); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyOffset(verifier, VT_OVERLOAD_NAME) && + verifier.VerifyString(overload_name()) && + VerifyField(verifier, VT_NUM_ARGS_SERIALIZED, 4) && + verifier.EndTable(); + } +}; + +struct OperatorBuilder { + typedef Operator Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(Operator::VT_NAME, name); + } + void add_overload_name(::flatbuffers::Offset<::flatbuffers::String> overload_name) { + fbb_.AddOffset(Operator::VT_OVERLOAD_NAME, overload_name); + } + void add_num_args_serialized(int32_t num_args_serialized) { + fbb_.AddElement(Operator::VT_NUM_ARGS_SERIALIZED, num_args_serialized, -1); + } + explicit OperatorBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateOperator( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset<::flatbuffers::String> overload_name = 0, + int32_t num_args_serialized = -1) { + OperatorBuilder builder_(_fbb); + builder_.add_num_args_serialized(num_args_serialized); + builder_.add_overload_name(overload_name); + builder_.add_name(name); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateOperatorDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + const char *overload_name = nullptr, + int32_t num_args_serialized = -1) { + auto name__ = name ? _fbb.CreateString(name) : 0; + auto overload_name__ = overload_name ? _fbb.CreateString(overload_name) : 0; + return torch::jit::mobile::serialization::CreateOperator( + _fbb, + name__, + overload_name__, + num_args_serialized); +} + +struct Arg FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ArgBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_TYPE = 6, + VT_DEFAULT_VALUE = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + ::flatbuffers::String *mutable_name() { + return GetPointer<::flatbuffers::String *>(VT_NAME); + } + const ::flatbuffers::String *type() const { + return GetPointer(VT_TYPE); + } + ::flatbuffers::String *mutable_type() { + return GetPointer<::flatbuffers::String *>(VT_TYPE); + } + uint32_t default_value() const { + return GetField(VT_DEFAULT_VALUE, 0); + } + bool mutate_default_value(uint32_t _default_value = 0) { + return SetField(VT_DEFAULT_VALUE, _default_value, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyOffset(verifier, VT_TYPE) && + verifier.VerifyString(type()) && + VerifyField(verifier, VT_DEFAULT_VALUE, 4) && + verifier.EndTable(); + } +}; + +struct ArgBuilder { + typedef Arg Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(Arg::VT_NAME, name); + } + void add_type(::flatbuffers::Offset<::flatbuffers::String> type) { + fbb_.AddOffset(Arg::VT_TYPE, type); + } + void add_default_value(uint32_t default_value) { + fbb_.AddElement(Arg::VT_DEFAULT_VALUE, default_value, 0); + } + explicit ArgBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateArg( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset<::flatbuffers::String> type = 0, + uint32_t default_value = 0) { + ArgBuilder builder_(_fbb); + builder_.add_default_value(default_value); + builder_.add_type(type); + builder_.add_name(name); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateArgDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + const char *type = nullptr, + uint32_t default_value = 0) { + auto name__ = name ? _fbb.CreateString(name) : 0; + auto type__ = type ? _fbb.CreateString(type) : 0; + return torch::jit::mobile::serialization::CreateArg( + _fbb, + name__, + type__, + default_value); +} + +struct Schema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SchemaBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ARGUMENTS = 4, + VT_RETURNS = 6 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset> *arguments() const { + return GetPointer> *>(VT_ARGUMENTS); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_arguments() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_ARGUMENTS); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *returns() const { + return GetPointer> *>(VT_RETURNS); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_returns() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_RETURNS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ARGUMENTS) && + verifier.VerifyVector(arguments()) && + verifier.VerifyVectorOfTables(arguments()) && + VerifyOffset(verifier, VT_RETURNS) && + verifier.VerifyVector(returns()) && + verifier.VerifyVectorOfTables(returns()) && + verifier.EndTable(); + } +}; + +struct SchemaBuilder { + typedef Schema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_arguments(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> arguments) { + fbb_.AddOffset(Schema::VT_ARGUMENTS, arguments); + } + void add_returns(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> returns) { + fbb_.AddOffset(Schema::VT_RETURNS, returns); + } + explicit SchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> arguments = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> returns = 0) { + SchemaBuilder builder_(_fbb); + builder_.add_returns(returns); + builder_.add_arguments(arguments); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *arguments = nullptr, + const std::vector<::flatbuffers::Offset> *returns = nullptr) { + auto arguments__ = arguments ? _fbb.CreateVector<::flatbuffers::Offset>(*arguments) : 0; + auto returns__ = returns ? _fbb.CreateVector<::flatbuffers::Offset>(*returns) : 0; + return torch::jit::mobile::serialization::CreateSchema( + _fbb, + arguments__, + returns__); +} + +struct DebugInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef DebugInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DEBUG_HANDLE = 4 + }; + const ::flatbuffers::Vector *debug_handle() const { + return GetPointer *>(VT_DEBUG_HANDLE); + } + ::flatbuffers::Vector *mutable_debug_handle() { + return GetPointer<::flatbuffers::Vector *>(VT_DEBUG_HANDLE); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DEBUG_HANDLE) && + verifier.VerifyVector(debug_handle()) && + verifier.EndTable(); + } +}; + +struct DebugInfoBuilder { + typedef DebugInfo Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_debug_handle(::flatbuffers::Offset<::flatbuffers::Vector> debug_handle) { + fbb_.AddOffset(DebugInfo::VT_DEBUG_HANDLE, debug_handle); + } + explicit DebugInfoBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateDebugInfo( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> debug_handle = 0) { + DebugInfoBuilder builder_(_fbb); + builder_.add_debug_handle(debug_handle); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateDebugInfoDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *debug_handle = nullptr) { + auto debug_handle__ = debug_handle ? _fbb.CreateVector(*debug_handle) : 0; + return torch::jit::mobile::serialization::CreateDebugInfo( + _fbb, + debug_handle__); +} + +struct Function FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef FunctionBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_QN = 4, + VT_INSTRUCTIONS = 6, + VT_OPERATORS = 8, + VT_CONSTANTS = 10, + VT_TYPE_ANNOTATIONS = 12, + VT_REGISTER_SIZE = 14, + VT_SCHEMA = 16, + VT_DEBUG_INFO = 18, + VT_CLASS_TYPE = 20 + }; + const ::flatbuffers::String *qn() const { + return GetPointer(VT_QN); + } + ::flatbuffers::String *mutable_qn() { + return GetPointer<::flatbuffers::String *>(VT_QN); + } + const ::flatbuffers::Vector *instructions() const { + return GetPointer *>(VT_INSTRUCTIONS); + } + ::flatbuffers::Vector *mutable_instructions() { + return GetPointer<::flatbuffers::Vector *>(VT_INSTRUCTIONS); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *operators() const { + return GetPointer> *>(VT_OPERATORS); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_operators() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_OPERATORS); + } + const ::flatbuffers::Vector *constants() const { + return GetPointer *>(VT_CONSTANTS); + } + ::flatbuffers::Vector *mutable_constants() { + return GetPointer<::flatbuffers::Vector *>(VT_CONSTANTS); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *type_annotations() const { + return GetPointer> *>(VT_TYPE_ANNOTATIONS); + } + ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *mutable_type_annotations() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_TYPE_ANNOTATIONS); + } + int32_t register_size() const { + return GetField(VT_REGISTER_SIZE, 0); + } + bool mutate_register_size(int32_t _register_size = 0) { + return SetField(VT_REGISTER_SIZE, _register_size, 0); + } + const torch::jit::mobile::serialization::Schema *schema() const { + return GetPointer(VT_SCHEMA); + } + torch::jit::mobile::serialization::Schema *mutable_schema() { + return GetPointer(VT_SCHEMA); + } + const torch::jit::mobile::serialization::DebugInfo *debug_info() const { + return GetPointer(VT_DEBUG_INFO); + } + torch::jit::mobile::serialization::DebugInfo *mutable_debug_info() { + return GetPointer(VT_DEBUG_INFO); + } + uint32_t class_type() const { + return GetField(VT_CLASS_TYPE, 0); + } + bool mutate_class_type(uint32_t _class_type = 0) { + return SetField(VT_CLASS_TYPE, _class_type, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_QN) && + verifier.VerifyString(qn()) && + VerifyOffset(verifier, VT_INSTRUCTIONS) && + verifier.VerifyVector(instructions()) && + VerifyOffset(verifier, VT_OPERATORS) && + verifier.VerifyVector(operators()) && + verifier.VerifyVectorOfTables(operators()) && + VerifyOffset(verifier, VT_CONSTANTS) && + verifier.VerifyVector(constants()) && + VerifyOffset(verifier, VT_TYPE_ANNOTATIONS) && + verifier.VerifyVector(type_annotations()) && + verifier.VerifyVectorOfStrings(type_annotations()) && + VerifyField(verifier, VT_REGISTER_SIZE, 4) && + VerifyOffset(verifier, VT_SCHEMA) && + verifier.VerifyTable(schema()) && + VerifyOffset(verifier, VT_DEBUG_INFO) && + verifier.VerifyTable(debug_info()) && + VerifyField(verifier, VT_CLASS_TYPE, 4) && + verifier.EndTable(); + } +}; + +struct FunctionBuilder { + typedef Function Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_qn(::flatbuffers::Offset<::flatbuffers::String> qn) { + fbb_.AddOffset(Function::VT_QN, qn); + } + void add_instructions(::flatbuffers::Offset<::flatbuffers::Vector> instructions) { + fbb_.AddOffset(Function::VT_INSTRUCTIONS, instructions); + } + void add_operators(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> operators) { + fbb_.AddOffset(Function::VT_OPERATORS, operators); + } + void add_constants(::flatbuffers::Offset<::flatbuffers::Vector> constants) { + fbb_.AddOffset(Function::VT_CONSTANTS, constants); + } + void add_type_annotations(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> type_annotations) { + fbb_.AddOffset(Function::VT_TYPE_ANNOTATIONS, type_annotations); + } + void add_register_size(int32_t register_size) { + fbb_.AddElement(Function::VT_REGISTER_SIZE, register_size, 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(Function::VT_SCHEMA, schema); + } + void add_debug_info(::flatbuffers::Offset debug_info) { + fbb_.AddOffset(Function::VT_DEBUG_INFO, debug_info); + } + void add_class_type(uint32_t class_type) { + fbb_.AddElement(Function::VT_CLASS_TYPE, class_type, 0); + } + explicit FunctionBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateFunction( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> qn = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> instructions = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> operators = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> constants = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> type_annotations = 0, + int32_t register_size = 0, + ::flatbuffers::Offset schema = 0, + ::flatbuffers::Offset debug_info = 0, + uint32_t class_type = 0) { + FunctionBuilder builder_(_fbb); + builder_.add_class_type(class_type); + builder_.add_debug_info(debug_info); + builder_.add_schema(schema); + builder_.add_register_size(register_size); + builder_.add_type_annotations(type_annotations); + builder_.add_constants(constants); + builder_.add_operators(operators); + builder_.add_instructions(instructions); + builder_.add_qn(qn); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateFunctionDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *qn = nullptr, + const std::vector *instructions = nullptr, + const std::vector<::flatbuffers::Offset> *operators = nullptr, + const std::vector *constants = nullptr, + const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *type_annotations = nullptr, + int32_t register_size = 0, + ::flatbuffers::Offset schema = 0, + ::flatbuffers::Offset debug_info = 0, + uint32_t class_type = 0) { + auto qn__ = qn ? _fbb.CreateString(qn) : 0; + auto instructions__ = instructions ? _fbb.CreateVectorOfStructs(*instructions) : 0; + auto operators__ = operators ? _fbb.CreateVector<::flatbuffers::Offset>(*operators) : 0; + auto constants__ = constants ? _fbb.CreateVector(*constants) : 0; + auto type_annotations__ = type_annotations ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*type_annotations) : 0; + return torch::jit::mobile::serialization::CreateFunction( + _fbb, + qn__, + instructions__, + operators__, + constants__, + type_annotations__, + register_size, + schema, + debug_info, + class_type); +} + +struct StorageData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef StorageDataBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + ::flatbuffers::Vector *mutable_data() { + return GetPointer<::flatbuffers::Vector *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct StorageDataBuilder { + typedef StorageData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { + fbb_.AddOffset(StorageData::VT_DATA, data); + } + explicit StorageDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateStorageData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { + StorageDataBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateStorageDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + if (data) { _fbb.ForceVectorAlignment(data->size(), sizeof(uint8_t), 16); } + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return torch::jit::mobile::serialization::CreateStorageData( + _fbb, + data__); +} + +struct IValue FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef IValueBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_VAL_TYPE = 4, + VT_VAL = 6 + }; + torch::jit::mobile::serialization::IValueUnion val_type() const { + return static_cast(GetField(VT_VAL_TYPE, 0)); + } + const void *val() const { + return GetPointer(VT_VAL); + } + template const T *val_as() const; + const torch::jit::mobile::serialization::Int *val_as_Int() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Int ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Bool *val_as_Bool() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Bool ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Double *val_as_Double() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Double ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::ComplexDouble *val_as_ComplexDouble() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::ComplexDouble ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::TensorMetadata *val_as_TensorMetadata() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::TensorMetadata ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::String *val_as_String() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::String ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::List *val_as_List() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::List ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Tuple *val_as_Tuple() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Tuple ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Dict *val_as_Dict() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Dict ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Object *val_as_Object() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Object ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::IntList *val_as_IntList() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::IntList ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::DoubleList *val_as_DoubleList() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::DoubleList ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::BoolList *val_as_BoolList() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::BoolList ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Device *val_as_Device() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Device ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::EnumValue *val_as_EnumValue() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::EnumValue ? static_cast(val()) : nullptr; + } + const torch::jit::mobile::serialization::Function *val_as_Function() const { + return val_type() == torch::jit::mobile::serialization::IValueUnion::Function ? static_cast(val()) : nullptr; + } + void *mutable_val() { + return GetPointer(VT_VAL); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_VAL_TYPE, 1) && + VerifyOffset(verifier, VT_VAL) && + VerifyIValueUnion(verifier, val(), val_type()) && + verifier.EndTable(); + } +}; + +template<> inline const torch::jit::mobile::serialization::Int *IValue::val_as() const { + return val_as_Int(); +} + +template<> inline const torch::jit::mobile::serialization::Bool *IValue::val_as() const { + return val_as_Bool(); +} + +template<> inline const torch::jit::mobile::serialization::Double *IValue::val_as() const { + return val_as_Double(); +} + +template<> inline const torch::jit::mobile::serialization::ComplexDouble *IValue::val_as() const { + return val_as_ComplexDouble(); +} + +template<> inline const torch::jit::mobile::serialization::TensorMetadata *IValue::val_as() const { + return val_as_TensorMetadata(); +} + +template<> inline const torch::jit::mobile::serialization::String *IValue::val_as() const { + return val_as_String(); +} + +template<> inline const torch::jit::mobile::serialization::List *IValue::val_as() const { + return val_as_List(); +} + +template<> inline const torch::jit::mobile::serialization::Tuple *IValue::val_as() const { + return val_as_Tuple(); +} + +template<> inline const torch::jit::mobile::serialization::Dict *IValue::val_as() const { + return val_as_Dict(); +} + +template<> inline const torch::jit::mobile::serialization::Object *IValue::val_as() const { + return val_as_Object(); +} + +template<> inline const torch::jit::mobile::serialization::IntList *IValue::val_as() const { + return val_as_IntList(); +} + +template<> inline const torch::jit::mobile::serialization::DoubleList *IValue::val_as() const { + return val_as_DoubleList(); +} + +template<> inline const torch::jit::mobile::serialization::BoolList *IValue::val_as() const { + return val_as_BoolList(); +} + +template<> inline const torch::jit::mobile::serialization::Device *IValue::val_as() const { + return val_as_Device(); +} + +template<> inline const torch::jit::mobile::serialization::EnumValue *IValue::val_as() const { + return val_as_EnumValue(); +} + +template<> inline const torch::jit::mobile::serialization::Function *IValue::val_as() const { + return val_as_Function(); +} + +struct IValueBuilder { + typedef IValue Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_val_type(torch::jit::mobile::serialization::IValueUnion val_type) { + fbb_.AddElement(IValue::VT_VAL_TYPE, static_cast(val_type), 0); + } + void add_val(::flatbuffers::Offset val) { + fbb_.AddOffset(IValue::VT_VAL, val); + } + explicit IValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateIValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + torch::jit::mobile::serialization::IValueUnion val_type = torch::jit::mobile::serialization::IValueUnion::NONE, + ::flatbuffers::Offset val = 0) { + IValueBuilder builder_(_fbb); + builder_.add_val(val); + builder_.add_val_type(val_type); + return builder_.Finish(); +} + +struct ExtraFile FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ExtraFileBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_CONTENT = 6 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + ::flatbuffers::String *mutable_name() { + return GetPointer<::flatbuffers::String *>(VT_NAME); + } + const ::flatbuffers::String *content() const { + return GetPointer(VT_CONTENT); + } + ::flatbuffers::String *mutable_content() { + return GetPointer<::flatbuffers::String *>(VT_CONTENT); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyOffset(verifier, VT_CONTENT) && + verifier.VerifyString(content()) && + verifier.EndTable(); + } +}; + +struct ExtraFileBuilder { + typedef ExtraFile Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(ExtraFile::VT_NAME, name); + } + void add_content(::flatbuffers::Offset<::flatbuffers::String> content) { + fbb_.AddOffset(ExtraFile::VT_CONTENT, content); + } + explicit ExtraFileBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateExtraFile( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset<::flatbuffers::String> content = 0) { + ExtraFileBuilder builder_(_fbb); + builder_.add_content(content); + builder_.add_name(name); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateExtraFileDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + const char *content = nullptr) { + auto name__ = name ? _fbb.CreateString(name) : 0; + auto content__ = content ? _fbb.CreateString(content) : 0; + return torch::jit::mobile::serialization::CreateExtraFile( + _fbb, + name__, + content__); +} + +struct Module FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ModuleBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BYTECODE_VERSION = 4, + VT_EXTRA_FILES = 6, + VT_METHODS = 8, + VT_STATE_OBJ = 10, + VT_IVALUES = 12, + VT_STORAGE_DATA_SIZE = 14, + VT_STORAGE_DATA = 16, + VT_OBJECT_TYPES = 18, + VT_JIT_SOURCES = 20, + VT_JIT_CONSTANTS = 22, + VT_OPERATOR_VERSION = 24, + VT_MOBILE_IVALUE_SIZE = 26 + }; + uint32_t bytecode_version() const { + return GetField(VT_BYTECODE_VERSION, 0); + } + bool mutate_bytecode_version(uint32_t _bytecode_version = 0) { + return SetField(VT_BYTECODE_VERSION, _bytecode_version, 0); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *extra_files() const { + return GetPointer> *>(VT_EXTRA_FILES); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_extra_files() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_EXTRA_FILES); + } + const ::flatbuffers::Vector *methods() const { + return GetPointer *>(VT_METHODS); + } + ::flatbuffers::Vector *mutable_methods() { + return GetPointer<::flatbuffers::Vector *>(VT_METHODS); + } + uint32_t state_obj() const { + return GetField(VT_STATE_OBJ, 0); + } + bool mutate_state_obj(uint32_t _state_obj = 0) { + return SetField(VT_STATE_OBJ, _state_obj, 0); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *ivalues() const { + return GetPointer> *>(VT_IVALUES); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_ivalues() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_IVALUES); + } + int32_t storage_data_size() const { + return GetField(VT_STORAGE_DATA_SIZE, 0); + } + bool mutate_storage_data_size(int32_t _storage_data_size = 0) { + return SetField(VT_STORAGE_DATA_SIZE, _storage_data_size, 0); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *storage_data() const { + return GetPointer> *>(VT_STORAGE_DATA); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_storage_data() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_STORAGE_DATA); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *object_types() const { + return GetPointer> *>(VT_OBJECT_TYPES); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_object_types() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_OBJECT_TYPES); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *jit_sources() const { + return GetPointer> *>(VT_JIT_SOURCES); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_jit_sources() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_JIT_SOURCES); + } + const ::flatbuffers::Vector *jit_constants() const { + return GetPointer *>(VT_JIT_CONSTANTS); + } + ::flatbuffers::Vector *mutable_jit_constants() { + return GetPointer<::flatbuffers::Vector *>(VT_JIT_CONSTANTS); + } + uint32_t operator_version() const { + return GetField(VT_OPERATOR_VERSION, 0); + } + bool mutate_operator_version(uint32_t _operator_version = 0) { + return SetField(VT_OPERATOR_VERSION, _operator_version, 0); + } + uint32_t mobile_ivalue_size() const { + return GetField(VT_MOBILE_IVALUE_SIZE, 0); + } + bool mutate_mobile_ivalue_size(uint32_t _mobile_ivalue_size = 0) { + return SetField(VT_MOBILE_IVALUE_SIZE, _mobile_ivalue_size, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_BYTECODE_VERSION, 4) && + VerifyOffset(verifier, VT_EXTRA_FILES) && + verifier.VerifyVector(extra_files()) && + verifier.VerifyVectorOfTables(extra_files()) && + VerifyOffset(verifier, VT_METHODS) && + verifier.VerifyVector(methods()) && + VerifyField(verifier, VT_STATE_OBJ, 4) && + VerifyOffset(verifier, VT_IVALUES) && + verifier.VerifyVector(ivalues()) && + verifier.VerifyVectorOfTables(ivalues()) && + VerifyField(verifier, VT_STORAGE_DATA_SIZE, 4) && + VerifyOffset(verifier, VT_STORAGE_DATA) && + verifier.VerifyVector(storage_data()) && + verifier.VerifyVectorOfTables(storage_data()) && + VerifyOffset(verifier, VT_OBJECT_TYPES) && + verifier.VerifyVector(object_types()) && + verifier.VerifyVectorOfTables(object_types()) && + VerifyOffset(verifier, VT_JIT_SOURCES) && + verifier.VerifyVector(jit_sources()) && + verifier.VerifyVectorOfTables(jit_sources()) && + VerifyOffset(verifier, VT_JIT_CONSTANTS) && + verifier.VerifyVector(jit_constants()) && + VerifyField(verifier, VT_OPERATOR_VERSION, 4) && + VerifyField(verifier, VT_MOBILE_IVALUE_SIZE, 4) && + verifier.EndTable(); + } +}; + +struct ModuleBuilder { + typedef Module Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_bytecode_version(uint32_t bytecode_version) { + fbb_.AddElement(Module::VT_BYTECODE_VERSION, bytecode_version, 0); + } + void add_extra_files(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> extra_files) { + fbb_.AddOffset(Module::VT_EXTRA_FILES, extra_files); + } + void add_methods(::flatbuffers::Offset<::flatbuffers::Vector> methods) { + fbb_.AddOffset(Module::VT_METHODS, methods); + } + void add_state_obj(uint32_t state_obj) { + fbb_.AddElement(Module::VT_STATE_OBJ, state_obj, 0); + } + void add_ivalues(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> ivalues) { + fbb_.AddOffset(Module::VT_IVALUES, ivalues); + } + void add_storage_data_size(int32_t storage_data_size) { + fbb_.AddElement(Module::VT_STORAGE_DATA_SIZE, storage_data_size, 0); + } + void add_storage_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> storage_data) { + fbb_.AddOffset(Module::VT_STORAGE_DATA, storage_data); + } + void add_object_types(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> object_types) { + fbb_.AddOffset(Module::VT_OBJECT_TYPES, object_types); + } + void add_jit_sources(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> jit_sources) { + fbb_.AddOffset(Module::VT_JIT_SOURCES, jit_sources); + } + void add_jit_constants(::flatbuffers::Offset<::flatbuffers::Vector> jit_constants) { + fbb_.AddOffset(Module::VT_JIT_CONSTANTS, jit_constants); + } + void add_operator_version(uint32_t operator_version) { + fbb_.AddElement(Module::VT_OPERATOR_VERSION, operator_version, 0); + } + void add_mobile_ivalue_size(uint32_t mobile_ivalue_size) { + fbb_.AddElement(Module::VT_MOBILE_IVALUE_SIZE, mobile_ivalue_size, 0); + } + explicit ModuleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateModule( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t bytecode_version = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> extra_files = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> methods = 0, + uint32_t state_obj = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> ivalues = 0, + int32_t storage_data_size = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> storage_data = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> object_types = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> jit_sources = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> jit_constants = 0, + uint32_t operator_version = 0, + uint32_t mobile_ivalue_size = 0) { + ModuleBuilder builder_(_fbb); + builder_.add_mobile_ivalue_size(mobile_ivalue_size); + builder_.add_operator_version(operator_version); + builder_.add_jit_constants(jit_constants); + builder_.add_jit_sources(jit_sources); + builder_.add_object_types(object_types); + builder_.add_storage_data(storage_data); + builder_.add_storage_data_size(storage_data_size); + builder_.add_ivalues(ivalues); + builder_.add_state_obj(state_obj); + builder_.add_methods(methods); + builder_.add_extra_files(extra_files); + builder_.add_bytecode_version(bytecode_version); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateModuleDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint32_t bytecode_version = 0, + const std::vector<::flatbuffers::Offset> *extra_files = nullptr, + const std::vector *methods = nullptr, + uint32_t state_obj = 0, + const std::vector<::flatbuffers::Offset> *ivalues = nullptr, + int32_t storage_data_size = 0, + const std::vector<::flatbuffers::Offset> *storage_data = nullptr, + const std::vector<::flatbuffers::Offset> *object_types = nullptr, + const std::vector<::flatbuffers::Offset> *jit_sources = nullptr, + const std::vector *jit_constants = nullptr, + uint32_t operator_version = 0, + uint32_t mobile_ivalue_size = 0) { + auto extra_files__ = extra_files ? _fbb.CreateVector<::flatbuffers::Offset>(*extra_files) : 0; + auto methods__ = methods ? _fbb.CreateVector(*methods) : 0; + auto ivalues__ = ivalues ? _fbb.CreateVector<::flatbuffers::Offset>(*ivalues) : 0; + auto storage_data__ = storage_data ? _fbb.CreateVector<::flatbuffers::Offset>(*storage_data) : 0; + auto object_types__ = object_types ? _fbb.CreateVector<::flatbuffers::Offset>(*object_types) : 0; + auto jit_sources__ = jit_sources ? _fbb.CreateVector<::flatbuffers::Offset>(*jit_sources) : 0; + auto jit_constants__ = jit_constants ? _fbb.CreateVector(*jit_constants) : 0; + return torch::jit::mobile::serialization::CreateModule( + _fbb, + bytecode_version, + extra_files__, + methods__, + state_obj, + ivalues__, + storage_data_size, + storage_data__, + object_types__, + jit_sources__, + jit_constants__, + operator_version, + mobile_ivalue_size); +} + +inline bool VerifyIValueUnion(::flatbuffers::Verifier &verifier, const void *obj, IValueUnion type) { + switch (type) { + case IValueUnion::NONE: { + return true; + } + case IValueUnion::Int: { + return verifier.VerifyField(static_cast(obj), 0, 8); + } + case IValueUnion::Bool: { + return verifier.VerifyField(static_cast(obj), 0, 1); + } + case IValueUnion::Double: { + return verifier.VerifyField(static_cast(obj), 0, 8); + } + case IValueUnion::ComplexDouble: { + return verifier.VerifyField(static_cast(obj), 0, 8); + } + case IValueUnion::TensorMetadata: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::String: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::List: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::Tuple: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::Dict: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::Object: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::IntList: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::DoubleList: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::BoolList: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::Device: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::EnumValue: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case IValueUnion::Function: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifyIValueUnionVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifyIValueUnion( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline const torch::jit::mobile::serialization::Module *GetModule(const void *buf) { + return ::flatbuffers::GetRoot(buf); +} + +inline const torch::jit::mobile::serialization::Module *GetSizePrefixedModule(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline Module *GetMutableModule(void *buf) { + return ::flatbuffers::GetMutableRoot(buf); +} + +inline torch::jit::mobile::serialization::Module *GetMutableSizePrefixedModule(void *buf) { + return ::flatbuffers::GetMutableSizePrefixedRoot(buf); +} + +inline const char *ModuleIdentifier() { + return "PTMF"; +} + +inline bool ModuleBufferHasIdentifier(const void *buf) { + return ::flatbuffers::BufferHasIdentifier( + buf, ModuleIdentifier()); +} + +inline bool SizePrefixedModuleBufferHasIdentifier(const void *buf) { + return ::flatbuffers::BufferHasIdentifier( + buf, ModuleIdentifier(), true); +} + +inline bool VerifyModuleBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(ModuleIdentifier()); +} + +inline bool VerifySizePrefixedModuleBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(ModuleIdentifier()); +} + +inline void FinishModuleBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root, ModuleIdentifier()); +} + +inline void FinishSizePrefixedModuleBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root, ModuleIdentifier()); +} + +} // namespace serialization +} // namespace mobile +} // namespace jit +} // namespace torch + +#endif // FLATBUFFERS_GENERATED_MOBILEBYTECODE_TORCH_JIT_MOBILE_SERIALIZATION_H_ diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/onnx.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/onnx.h new file mode 100644 index 0000000000000000000000000000000000000000..e9886690406605a5d576b232a514204cb9f2fee4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/onnx.h @@ -0,0 +1,18 @@ +#pragma once + +C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED( + "-Winconsistent-missing-destructor-override") +C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED("-Wsuggest-override") +C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED( + "-Wdeprecated-dynamic-exception-spec") +#include +C10_DIAGNOSTIC_POP() +C10_DIAGNOSTIC_POP() +C10_DIAGNOSTIC_POP() +#include + +namespace torch::jit { + +TORCH_API std::string prettyPrint(const ::ONNX_NAMESPACE::ModelProto& model); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickle.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickle.h new file mode 100644 index 0000000000000000000000000000000000000000..7a8538ab270f7757360240255d648e1daef358c8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickle.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +/// Pickle an IValue by calling a function to handle writing the data. +/// +/// `writer` is a function that takes in a pointer to a chunk of memory and its +/// size and consumes it. +/// +/// See `jit::pickle` for more details. +TORCH_API void pickle( + std::function writer, + const IValue& ivalue, + std::vector* tensor_table = nullptr); + +/// Save a `torch::IValue` in a format compatible with Python's `pickle` module +/// +/// If present, `tensor_table` is a pointer to a table in which tensors that +/// are contained within `ivalue` are stored, and the bytes returned by the +/// pickler will only include references to these tensors in the table. This can +/// be used to keep the binary blob size small. +/// If not provided, tensors are stored in the same byte stream as the pickle +/// data, similar to `torch.save()` in eager Python. +/// +/// Pickled values can be loaded in Python and C++: +/// \rst +/// .. code-block:: cpp +/// +/// torch::IValue float_value(2.3); +/// +/// // TODO: when tensors are stored in the pickle, delete this +/// std::vector tensor_table; +/// auto data = torch::jit::pickle(float_value, &tensor_table); +/// +/// std::vector ivalues = +/// torch::jit::unpickle(data.data(), data.size()); +/// +/// .. code-block:: python +/// +/// values = torch.load('data.pkl') +/// print(values) +/// +/// \endrst +TORCH_API std::vector pickle( + const IValue& ivalue, + std::vector* tensor_table = nullptr); + +/// Save a `torch::IValue` in a format that can be loaded by both +/// `torch::pickle_load` in C++ and `torch.load` in Python. +TORCH_API std::vector pickle_save(const IValue& ivalue); + +/// Deserialize a `torch::IValue` from bytes produced by either +/// `torch::pickle_save` in C++ or `torch.save` in Python +TORCH_API IValue pickle_load(const std::vector& data); + +/// Deserialize a `torch::IValue` from bytes produced by either +/// `torch::pickle_save` in C++ or `torch.save` in Python with custom object. +TORCH_API IValue pickle_load_obj(std::string_view data); + +/// `reader` is a function that takes in a size to read from some pickled +/// binary. `reader` should remember where it last read, and return +/// the number of bytes read. +/// See `torch::pickle` for details. +/// type_resolver is used to resolve any JIT type based on type str +TORCH_API IValue unpickle( + std::function reader, + TypeResolver type_resolver, + c10::ArrayRef tensor_table, + c10::TypePtr (*type_parser)(const std::string&) = + Unpickler::defaultTypeParser, + ObjLoader obj_loader = nullptr); + +/// Decode a chunk of memory containing pickled data into its `torch::IValue`s. +/// +/// If any `torch::IValue`s in the pickled data are `Object`s, then a +/// `class_resolver` function must be provided. +/// +/// See `torch::pickle` for details. +TORCH_API IValue unpickle( + const char* data, + size_t size, + TypeResolver type_resolver = nullptr, + c10::ArrayRef tensor_table = {}, + c10::TypePtr (*type_parser)(const std::string&) = + Unpickler::defaultTypeParser); + +/// Decode a chunk of memory containing pickled data into its `torch::IValue`s. +/// +/// If any `torch::IValue`s in the pickled data are `Object`s, then a +/// `class_resolver` function must be provided. +/// +/// See `torch::pickle` for details. +TORCH_API IValue unpickle( + const char* data, + size_t size, + ObjLoader obj_loader, + TypeResolver type_resolver = nullptr, + c10::ArrayRef tensor_table = {}, + c10::TypePtr (*type_parser)(const std::string&) = + Unpickler::defaultTypeParser); + +#ifndef C10_MOBILE +class VectorReader : public caffe2::serialize::ReadAdapterInterface { + public: + VectorReader(std::vector data) : data_(std::move(data)) {} + + size_t size() const override { + return data_.size(); + } + + size_t read(uint64_t pos, void* buf, size_t n, const char* what) + const override; + + private: + std::vector data_; +}; + +class StringViewReader : public caffe2::serialize::ReadAdapterInterface { + public: + StringViewReader(std::string_view data) : data_(data) {} + + size_t size() const override { + return data_.size(); + } + + size_t read(uint64_t pos, void* buf, size_t n, const char* what) + const override; + + private: + std::string_view data_; +}; +#endif +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickler.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickler.h new file mode 100644 index 0000000000000000000000000000000000000000..8accfa229b845affb0defe0c92a3808dc2d49884 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/pickler.h @@ -0,0 +1,425 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +// See Python's pickletools.py for a detailed description of each of these codes +enum class PickleOpCode : char { + MARK = '(', + STOP = '.', + POP = '0', + POP_MARK = '1', + DUP = '2', + FLOAT = 'F', + INT = 'I', + BININT = 'J', + BININT1 = 'K', + LONG = 'L', + BININT2 = 'M', + NONE = 'N', + PERSID = 'P', + BINPERSID = 'Q', + REDUCE = 'R', + STRING = 'S', + BINSTRING = 'T', + SHORT_BINSTRING = 'U', + // NB: Avoid using UNICODE as it is a macro in the Windows API + UNICODE_ = 'V', + BINUNICODE = 'X', + APPEND = 'a', + BUILD = 'b', + GLOBAL = 'c', + DICT = 'd', + EMPTY_DICT = '}', + APPENDS = 'e', + GET = 'g', + BINGET = 'h', + INST = 'i', + LONG_BINGET = 'j', + LIST = 'l', + EMPTY_LIST = ']', + OBJ = 'o', + PUT = 'p', + BINPUT = 'q', + LONG_BINPUT = 'r', + SETITEM = 's', + TUPLE = 't', + EMPTY_TUPLE = ')', + SETITEMS = 'u', + BINFLOAT = 'G', + + // Protocol 2 + PROTO = char('\x80'), + NEWOBJ = '\x81', + EXT1 = '\x82', + EXT2 = '\x83', + EXT4 = '\x84', + TUPLE1 = '\x85', + TUPLE2 = '\x86', + TUPLE3 = '\x87', + NEWTRUE = '\x88', + NEWFALSE = '\x89', + LONG1 = '\x8a', + LONG4 = '\x8b', + + // Protocol 3 (Python 3.x) + BINBYTES = 'B', + SHORT_BINBYTES = 'C', + + // Protocol 4 + SHORT_BINUNICODE = char('\x8c'), + BINUNICODE8 = '\x8d', + BINBYTES8 = '\x8e', + EMPTY_SET = '\x8f', + ADDITEMS = '\x90', + FROZENSET = '\x91', + NEWOBJ_EX = '\x92', + STACK_GLOBAL = '\x93', + MEMOIZE = '\x94', + FRAME = '\x95' +}; + +using ::c10::IValue; + +struct WriteableTensorData { + const char* data() const { + return static_cast(tensor_.storage().data()); + } + size_t sizeInBytes() const { + return size_; + } + size_t nbytes() const { + return tensor_.storage().nbytes(); + } + bool storageHasDeleter() const { + return tensor_.storage().data_ptr().get_context() != nullptr; + } + + private: + friend TORCH_API WriteableTensorData + getWriteableTensorData(const at::Tensor& tensor, bool to_cpu); + at::Tensor tensor_; + uint64_t size_; +}; + +void setTypeTags(bool state); +bool getTypeTags(); + +class TORCH_API Pickler { + AT_DISALLOW_COPY_AND_ASSIGN(Pickler); + + public: + Pickler(std::function writer) + : Pickler(std::move(writer), nullptr, nullptr, nullptr) {} + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + Pickler( + std::function writer, + std::vector* tensor_table, + std::function type_renamer, + std::vector* memoized_class_types, + std::function get_tensor_id = nullptr, + bool tag_aggregates = true) + : writer_(std::move(writer)), + tensor_table_(tensor_table), + type_renamer_(std::move(type_renamer)), + memoized_class_types_(memoized_class_types), + get_tensor_id_(std::move(get_tensor_id)), + tag_aggregates_(tag_aggregates) {} + ~Pickler(); + + // Push protocol onto the stack + void protocol(); + + // Push STOP PickleOpCode onto the stack + void stop(); + + void pushIValue(const IValue& ivalue); + + void startTuple(); + void endTuple(); + + const std::vector& tensorData() { + return tensor_data_; + } + + void pushEmptyDict(); + void pushDict(const IValue& ivalue); + void pushInt(int64_t value); + void pushLong(const std::string& data); + + private: + void pushIValueImpl(const IValue& ivalue); + void startTypeTag(); + void endTypeTag(const IValue& value); + void pushBool(bool value); + void pushDouble(double value); + void pushComplexDouble(const IValue& value); + void pushGenericList(const IValue& ivalue); + void pushIntList(const IValue& ivalue); + void pushList(const IValue& ivalue); + void pushTensor(const IValue& ivalue); + void pushTensorReference(const IValue& ivalue); + void pushLiteralTensor(const IValue& ivalue); + void pushLiteralSparseTensor(const at::Tensor& tensor); + void pushTuple(const IValue& ivalue); + void pushString(const std::string& string); + void pushDevice(const IValue& ivalue); +#ifdef USE_DISTRIBUTED + void pushRRef(const IValue& ivalue); +#endif + // unmemoized version + void pushStringImpl(const std::string& string); + void pushStorageOfTensor(const at::Tensor& tensor); + + void pushBinGet(uint32_t memo_id); + void pushSpecializedList( + const IValue& ivalue, + const char* list_name, + const std::function& item_pusher); + void pushGlobal(std::string_view module_name, std::string_view class_name); + // raw string data is appended directly to the byte stream + void pushBytes(const std::string& string); + void pushTensorData(const at::Tensor& tensor); + + // Add a BINPUT op and return the memoization id used + size_t pushNextBinPut(); + + const void* getPointer(const IValue& ivalue); + + // Caller checks that bufferPos_ > 0 + void flushNonEmpty() { + writer_(buffer_.data(), bufferPos_); + bufferPos_ = 0; + } + + void flush() { + if (bufferPos_ != 0) { + flushNonEmpty(); + } + } + + // These convert values to bytes and add them to the stack (NB: since T is to + // the left of a '::', its type cannot be deduced by the compiler so one must + // explicitly instantiate the template, i.e. push(int) works, push(int) + // does not) + static constexpr size_t kBufferSize = 256; + template + void push(std::common_type_t value) { + const char* begin = reinterpret_cast(&value); + if (bufferPos_ + sizeof(T) > buffer_.size()) { + flushNonEmpty(); + } + static_assert(sizeof(T) <= kBufferSize, "Buffer size assumption"); + memcpy(buffer_.data() + bufferPos_, begin, sizeof(T)); + bufferPos_ += sizeof(T); + } + + // Stream to write binary data to + // Code shouldn't call writer_ directly without first flushing. + std::function writer_; + + // Buffer to avoid calling a writer_ on a per-byte basis. + std::array buffer_; + size_t bufferPos_{0}; + + // Stack of opcodes/data + std::vector stack_; + + // External table of tensors to serialize. If this is missing, then tensors + // are serialized directly into the pickle + std::vector* tensor_table_; + + // TODO: only use this if necessary (add a pass to find all shared ivalues, + // and only memoize those) + uint32_t memo_id_ = 0; + + // Memoization of IValues that have been written (index in table is used for + // BINPUT opcodes) to enable shared references + c10::FastMap memoized_ivalue_map_; + + // because we de-dup ivalues based on their raw pointer address in the above + // map we need to keep all the memoized values alive during the pickle. + // Otherwise, it is possible that a raw address gets reused for another + // object, and we will alias it to the old object at that address. + std::vector memoized_ivalues_; + + std::function type_renamer_; + + // List of all the types that it wrote, inspect from the IValues it wrote. + std::vector* memoized_class_types_; + + // Function to grab next id_name for tensor storage, function is responsible + // for returning unique ids + std::function get_tensor_id_; + + // List of tensor storages to serialize in the same binary as the pickle data + // similar to ivalues, they are memoized using BINPUT + std::vector tensor_data_; + c10::FastMap memoized_storage_map_; + + c10::FastMap memoized_globals_map_; + c10::FastMap memoized_strings_map_; + c10::FastMap memoized_devices_map_; + // when true, List and Dict objects will be wrapped in a + // torch.jit._pickle.restore_type_tag call to correctly set the dynamic + // TorchScript type for the object. When true the thing unpickling must have + // torch installed. + bool tag_aggregates_; +}; + +// returns a (tensor, record_size) for a tensor, converting it to a CPU tensor +// if it was CUDA and to_cpu is True. +TORCH_API WriteableTensorData +getWriteableTensorData(const at::Tensor& tensor, bool to_cpu = true); + +// return the value of the tensor's storage pointer +uint64_t getStorageKey(const at::Tensor& tensor); + +// if the cls has __getstate__/__setstate__ +// assert they have the right schema and return true, +// otherwise return false +bool checkHasValidSetGetState(const std::shared_ptr& cls); + +// Declare BackendMeta serialization and deserialization function pointer types. +using BackendMetaPtr = std::function< + void(const at::Tensor&, std::unordered_map&)>; + +// A allowlist of device type, currently available is PrivateUse1 +inline std::unordered_set& GetBackendMetaAllowlist() { + static std::unordered_set DeviceTypeAllowlist{ + c10::DeviceType::PrivateUse1}; + return DeviceTypeAllowlist; +} + +// Dynamically obtain serialization function pairs +// that require the corresponding backend. +inline std::array< + std::optional>, + at::COMPILE_TIME_MAX_DEVICE_TYPES>& +GetBackendMetaSerialization() { + // The array to save function pointer for BackendMeta serialization. + // key is the DeviceType, value is std::pair obj. + // value.first represent get function and value.seconde represent set function + static std::array< + std::optional>, + at::COMPILE_TIME_MAX_DEVICE_TYPES> + BackendMetaSerialization; + return BackendMetaSerialization; +} + +// Register function pointer of Tensor BackendMetadata for serialization. +TORCH_API inline void TensorBackendMetaRegistry( + c10::DeviceType t, + const BackendMetaPtr& get_fptr, + const BackendMetaPtr& set_fptr) { + // allowlist verification + // Only if the devicetype is in the allowlist, + // we allow the serialization extension to be registered for backendmeta data. + const auto& DeviceTypeAllowlist = GetBackendMetaAllowlist(); + TORCH_CHECK( + DeviceTypeAllowlist.find(t) != DeviceTypeAllowlist.end(), + "It is not allowed to register the serialization method ", + "of backendMeta data for PrivateUse1. ", + "If you have related serialization requirements, ", + "please expand the allowlist"); + // Register function pointer + int device_type = static_cast(t); + auto& BackendMetaSerialization = GetBackendMetaSerialization(); + TORCH_CHECK( + !BackendMetaSerialization[device_type].has_value(), + "The tensor BackendMeta serialization function pointer for ", + t, + " has been registered."); + BackendMetaSerialization[device_type] = + std::optional>( + std::make_pair(get_fptr, set_fptr)); +} + +// Return a map of Tensor Metadata which including BackendMetaData for +// serialization. For now, it only takes care of `conj` and `neg` bit. +inline std::unordered_map getTensorMetadata( + const at::Tensor& t) { + // We don't support serializing `ZeroTensor` as it is not public + // facing yet. + TORCH_CHECK( + !t._is_zerotensor(), + "ZeroTensor is not serializable,", + " please file an issue if required."); + std::unordered_map metadata{}; + + // Only add meta-data if the value is not default. + if (t.is_conj()) { + metadata["conj"] = true; + } + if (t.is_neg()) { + metadata["neg"] = true; + } + // Only add BackendMetaData for custom backend if the function pointer is + // registered. + int device_type = static_cast(t.device().type()); + const auto& BackendMetaSerialization = GetBackendMetaSerialization(); + if (BackendMetaSerialization[device_type].has_value()) { + // Pass the tensor and metadata map references as parameters to the custom + // serialization function. + BackendMetaPtr fptr = BackendMetaSerialization[device_type].value().first; + fptr(t, metadata); + } + return metadata; +} + +// set Tensor Metadata based on the map. +// Refer: getTensorMetadata +inline void setTensorMetadata( + const at::Tensor& t, + std::unordered_map metadata) { + auto iter_end = metadata.end(); + auto iter_temp = metadata.find("conj"); + if (iter_temp != iter_end) { + t._set_conj(true); + metadata.erase(iter_temp); + } + iter_temp = metadata.find("neg"); + if (iter_temp != iter_end) { + t._set_neg(true); + metadata.erase(iter_temp); + } + // Only set BackendMetaData for custom backend if the function pointer is + // registered. + int device_type = static_cast(t.device().type()); + const auto& BackendMetaSerialization = GetBackendMetaSerialization(); + if (BackendMetaSerialization[device_type].has_value()) { + // Pass the tensor and metadata map references as parameters to the custom + // deserialization function. + BackendMetaPtr fptr = BackendMetaSerialization[device_type].value().second; + fptr(t, metadata); + } +} + +// set Tensor metadata based on the map. +// NOTE: This overload is required by unpickler.cpp +inline void setTensorMetadata( + const at::Tensor& t, + const c10::Dict& metadata_idict) { + std::unordered_map metadata; + for (auto& pair : metadata_idict) { + auto key = *pair.key().toString(); + metadata[key] = pair.value().toBool(); + } + setTensorMetadata(t, std::move(metadata)); +} + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/python_print.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/python_print.h new file mode 100644 index 0000000000000000000000000000000000000000..e877fd48a0027286dd64707cbe28355a33c85837 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/python_print.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include + +namespace torch::jit { + +struct Method; +struct Module; +struct PythonPrintImpl; + +struct PrintDepsTable { + void add(const c10::NamedTypePtr& type); + + size_t size() const { + return table_.size(); + } + + const c10::NamedTypePtr& operator[](size_t index) const { + return table_[index]; + } + + private: + std::vector table_; + std::unordered_set non_unique_; +}; + +struct TORCH_API PythonPrint { + PythonPrint( + std::vector& constant_table, + PrintDepsTable& deps_table, + c10::TypePrinter type_printer = nullptr, + bool enforce_importable = false); + + void printNamedType(const c10::NamedTypePtr& classType); + void printFunction(const Function& callee); + void printMethod(const Function& callee); + + std::string str() const; + const SourceRangeRecords& ranges() const; + uint64_t minVersion() const; + + private: + std::shared_ptr pImpl; +}; + +TORCH_API bool printerHasSpecialCaseFor(c10::Symbol sym); + +TORCH_API void jitModuleToPythonCodeAndConstants( + const Module& module, + ExtraFilesMap* jit_sources, // output + std::vector* constants // output +); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization.h new file mode 100644 index 0000000000000000000000000000000000000000..382676a6230e48ccccabb6487b1643dc3e651605 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace c10 { +struct IValue; +} + +namespace torch::jit { + +class Pickler; +class SourceRangeSerializer; +static constexpr size_t kByteOffsetIndex = 0; +static constexpr size_t kSourceRangeIndex = 1; +static constexpr size_t kSourceRangeTagIndex = 2; +constexpr std::string_view kFormatWithStringTable = "FORMAT_WITH_STRING_TABLE"; + +class SourceRangePickler { + public: + SourceRangePickler(); + + std::vector pickle( + const SourceRangeRecords& ranges, + const SourceRangeTagMap& source_range_tags); + + private: + std::shared_ptr srs; +}; + +class SourceRangeDeserializer { + public: + SourceRangeDeserializer() = default; + explicit SourceRangeDeserializer(const c10::IValue& text_table) { + for (const auto& x : text_table.toTuple()->elements()) { + text_table_.emplace_back(std::make_shared(x.toStringRef())); + } + } + SourceRange deserialize(const c10::IValue& iv); + + private: + std::shared_ptr deserialize_source(const c10::IValue& iv); + std::unordered_map< + c10::intrusive_ptr, + std::shared_ptr> + cached_sources; + std::vector> text_table_; +}; + +class SourceRangeUnpickler { + public: + virtual std::optional findSourceRangeThatGenerated( + const SourceRange& range) = 0; + + virtual ~SourceRangeUnpickler() = default; +}; + +TORCH_API void setShouldUseFormatWithStringTable( + bool should_use_format_with_string_table); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization_impl.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..43903958d8f799158ba59c658f5ffcba60b56736 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/source_range_serialization_impl.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace torch::jit { + +// Do this clownyness with virtual functions because of the split +// between ATen core and torch + +class ConcreteSourceRangeUnpickler : public SourceRangeUnpickler { + public: + ConcreteSourceRangeUnpickler(at::DataPtr&& data, size_t size); + + std::optional findSourceRangeThatGenerated( + const SourceRange& range) override; + + private: + at::DataPtr data; + size_t size; + + void unpickle(); + + std::mutex mutex; + std::shared_ptr deserializer; + std::shared_ptr unpickled_records; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/storage_context.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/storage_context.h new file mode 100644 index 0000000000000000000000000000000000000000..b44508107bae4e1aa065429d4bb3d449dced319a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/storage_context.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +namespace torch::jit { + +// Used in torch.package and TorchScript serialization to coordinate +// sharing of storages between models. Also used to create deterministic +// naming for storages. +class TORCH_API SerializationStorageContext { + public: + explicit SerializationStorageContext() = default; + SerializationStorageContext operator=(const SerializationStorageContext&) = + delete; + SerializationStorageContext(const SerializationStorageContext&) = delete; + + uint64_t getOrAddStorage(const c10::Storage& storage) { + if (!hasStorage(storage)) { + uint64_t size = storage_id_map_.size(); + storage_id_map_[storage] = size; + } + return storage_id_map_[storage]; + } + + bool hasStorage(const c10::Storage& storage) { + return storage_id_map_.find(storage) != storage_id_map_.end(); + } + + ~SerializationStorageContext() = default; + + private: + class StorageSerializationHash { + public: + size_t operator()(const c10::Storage& storage) const { + return std::hash()( + reinterpret_cast(storage.unsafeGetStorageImpl())); + } + }; + + class StorageSerializationEqual { + public: + bool operator()(const c10::Storage& lhs, const c10::Storage& rhs) const { + return lhs.unsafeGetStorageImpl() == rhs.unsafeGetStorageImpl(); + } + }; + + std::unordered_map< + c10::Storage, + uint64_t, + StorageSerializationHash, + StorageSerializationEqual> + storage_id_map_; +}; + +// Used in torch.package and TorchScript deserialization to coordinate +// sharing of storages between models. +class TORCH_API DeserializationStorageContext { + public: + explicit DeserializationStorageContext() = default; + DeserializationStorageContext operator=( + const DeserializationStorageContext&) = delete; + DeserializationStorageContext(const DeserializationStorageContext&) = delete; + + void addStorage(std::string name, c10::Storage storage) { + TORCH_INTERNAL_ASSERT(!hasStorage(name)); + name_storage_map_.emplace(std::move(name), std::move(storage)); + } + + bool hasStorage(const std::string& name) { + return name_storage_map_.find(name) != name_storage_map_.end(); + } + + c10::Storage getStorage(const std::string& name) { + TORCH_INTERNAL_ASSERT(hasStorage(name)); + return name_storage_map_.find(name)->second; + } + ~DeserializationStorageContext() = default; + + private: + std::unordered_map name_storage_map_; +}; + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/type_name_uniquer.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/type_name_uniquer.h new file mode 100644 index 0000000000000000000000000000000000000000..78eea7755fe081a64178654f4daf624868595ce5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/type_name_uniquer.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace torch::jit { + +/** + * class TypeNameUniquer + * + * Generates a unique name for every type `t` passed in. Types that compare + * equal with EqualType will receive the same unique name. + * + * This is used during Module::save(), to resolve type name collisions during + * serialization. + */ +class TORCH_API TypeNameUniquer { + public: + c10::QualifiedName getUniqueName(c10::ConstNamedTypePtr t); + + private: + NameMangler mangler_; + std::unordered_set used_names_; + std::unordered_map< + c10::ConstNamedTypePtr, + c10::QualifiedName, + HashType, + EqualType> + name_map_; +}; +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/unpickler.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/unpickler.h new file mode 100644 index 0000000000000000000000000000000000000000..aea91ebe80c50497bfbd079801f3201f2e9a28fd --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/serialization/unpickler.h @@ -0,0 +1,201 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace torch::jit { + +using TypeResolver = + std::function; + +using ObjLoader = std::function< + c10::intrusive_ptr(const at::StrongTypePtr&, IValue)>; + +class DeserializationStorageContext; + +// [unpickler refactor] there is some cruft around PickleOpCode::BUILD, +// PickleOpCode::NEWOBJ, and the last_opcode_ member below that should be +// deleted at some point, the Pickler doesn't produce it and it's only around to +// support models saved before 1.1 +class TORCH_API Unpickler { + AT_DISALLOW_COPY_AND_ASSIGN(Unpickler); + + using TypeParserT = c10::TypePtr (*)(const std::string&); + + public: + // tensors inside the pickle are references to the tensor_table. + // class_resolver is to resolve strong class type, type_resolver_ is + // to resolve any JIT type. class_resolver and type_resolver are not merged + // here because some use cases need to get strong class type that + // type_resolver_ can not return. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + Unpickler( + std::function reader, + TypeResolver type_resolver, + c10::ArrayRef tensor_table, + TypeParserT type_parser = defaultTypeParser) + : reader_(std::move(reader)), + tensor_table_(tensor_table), + type_resolver_(std::move(type_resolver)), + use_storage_device_(false), + type_parser_(type_parser), + version_(caffe2::serialize::kProducedFileFormatVersion) {} + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + Unpickler( + std::function reader, + TypeResolver type_resolver, + c10::ArrayRef tensor_table, + ObjLoader obj_loader, + TypeParserT type_parser = defaultTypeParser) + : reader_(std::move(reader)), + tensor_table_(tensor_table), + type_resolver_(std::move(type_resolver)), + obj_loader_(std::move(obj_loader)), + use_storage_device_(false), + type_parser_(type_parser), + version_(caffe2::serialize::kProducedFileFormatVersion) {} + + // tensors inside the pickle contain meta-data, the raw tensor + // dead is retrieved by calling `read_record`. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + Unpickler( + std::function reader, + TypeResolver type_resolver, + ObjLoader obj_loader, + std::function read_record, + std::optional device, + bool use_storage_device = false, + TypeParserT type_parser = defaultTypeParser, + std::shared_ptr storage_context = nullptr) + : reader_(std::move(reader)), + tensor_table_(), + type_resolver_(std::move(type_resolver)), + obj_loader_(std::move(obj_loader)), + read_record_(std::move(read_record)), + device_(device), + use_storage_device_(use_storage_device), + type_parser_(type_parser), + storage_context_(std::move(storage_context)), + version_(caffe2::serialize::kProducedFileFormatVersion) {} + + // consume the pickle stream, producing an IValue from the contents. + // Type Tags: the pickler will restore the type tags on + // List and Dict objects when possible IValue is an Object. + // Otherwise, Dict and List objects will end up with Any as their tag. + // If you know the type of the ivalue, tags can be restored with + // restoreAccurateTypeTags + IValue parse_ivalue(); + + // [type tag serialization] + // This is used to determine whether to restore type tags be recursively + // descending into the returned stack object (if version_number <= 2), or + // if version_number >= 3, to use the type strings included in the pickle + // archive for container types. By default this is set to + // `kProducedFileFormatVersion` so unless you're loading a pickle file + // from alongside a corresponding `version` file, you don't need to set + // the version manually. + void set_version(uint64_t version_number) { + version_ = version_number; + } + + static c10::TypePtr defaultTypeParser(const std::string& str) { + ScriptTypeParser parser; + return parser.parseType(str); + } + + private: + // No arguments ensures that a template argument must be specified + // so that the number of bytes read / type read is explicit + template + T read() { + T item; + if (sizeof(T) <= buffer_remaining_) { + // Fast path: entirely from buffer. + memcpy(&item, buffer_.data() + buffer_pos_, sizeof(T)); + buffer_remaining_ -= sizeof(T); + buffer_pos_ += sizeof(T); + } else { + // Don't over-template the slow path, to avoid code size bloat. + readSlowWithBuffer(reinterpret_cast(&item), sizeof(T)); + } + return item; + } + void readSlowWithBuffer(char* dest, size_t sz); + std::string readBytes(size_t num_bytes); + + double readFloat(); + void readGlobal( + const std::string& module_name, + const std::string& class_name); + void rebuildTensor(bool quantized); + void rebuildTensorFromTypeV2(); + void rebuildSparseTensor(); +#ifdef USE_DISTRIBUTED + void rebuildRRef(); +#endif + PickleOpCode readInstruction(); + PickleOpCode readOpCode() { + return static_cast(read()); + } + std::string readString(); + void readList(IValue list_ivalue); + void readListElements(IValue list_ivalue, size_t start); + void setInput(size_t memo_id); + void run(); + + // Returns the number of bytes read. This should statefully + // remember the position. Don't call reader_ directly. + std::function reader_; + // Small buffer to avoid calling reader_ on a per-byte basis. + std::array buffer_; + size_t buffer_pos_{0}; + size_t buffer_remaining_{0}; + + std::vector stack_; + + // globals are represented on the stack as IValue integer indices + // into this list + std::vector> globals_; + std::vector memo_table_; + std::vector marks_; + c10::ArrayRef tensor_table_; + + // When deserializing types on lists and dicts, cache the type here + // so we don't have to parse the same type multiple times. Strings + // are already de-duplicated and replaced with BINGETs in the + // pickler, so we can just use the actual data pointer of each string. + std::unordered_map type_cache_; + + // optionally nullptr, needs to be present for creating classes + TypeResolver type_resolver_; + ObjLoader obj_loader_; + IValue empty_tuple_; + + std::function read_record_; + std::optional device_; + // When set to true, Unpickler will ignore the pickled device and use the + // device of the DataPtr returned by the read_record_ function. The default + // value of this flag is false. + const bool use_storage_device_; + + TypeParserT type_parser_{defaultTypeParser}; + + // Used for torch.package to enable sharing of storages across + // ScriptModules and eager modules + std::shared_ptr storage_context_; + + // See [type tag serialization] + uint64_t version_; + + // See [NOTE] skip_next_read_global + uint8_t skip_next_read_global = 0; +}; + +void restoreAccurateTypeTags(const IValue& root, const c10::TypePtr& type_tag); + +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/analysis.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/analysis.h new file mode 100644 index 0000000000000000000000000000000000000000..e6a2791023f8baca6bbc78cdb78272861bbc0321 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/analysis.h @@ -0,0 +1,398 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { +class HasRand : public IRVisitor { + public: + HasRand(StmtPtr stmt) : stmt_(std::move(stmt)) { + stmt_->accept(this); + } + + bool has_rand() const { + return has_rand_; + } + + private: + void visit(const IntrinsicsPtr& v) override { + if (v->op_type() == IntrinsicsOp::kRand) { + has_rand_ = true; + } else { + IRVisitor::visit(v); + } + } + StmtPtr stmt_; + bool has_rand_ = false; +}; + +template +class NodeFinder : public IRVisitor { + public: + void visit(const NodePtr& v) override { + nodes.push_back((NodePtr)v); + IRVisitor::visit(v); + } + + static std::vector> find(const StmtPtr& s) { + NodeFinder nf; + s->accept(&nf); + return nf.nodes; + } + + static std::vector> find(const ExprPtr& e) { + NodeFinder nf; + e->accept(&nf); + return nf.nodes; + } + + std::vector> nodes; +}; + +class VarFinder : public IRVisitor { + public: + void visit(const VarPtr& v) override { + vars_.insert(v); + IRVisitor::visit(v); + } + + static std::unordered_set find(const StmtPtr& s) { + VarFinder nf; + s->accept(&nf); + return nf.vars(); + } + + static std::unordered_set find(const ExprPtr& e) { + VarFinder nf; + e->accept(&nf); + return nf.vars(); + } + + const std::unordered_set& vars() { + return vars_; + } + + private: + std::unordered_set vars_; +}; + +class BufFinder : public IRVisitor { + public: + void visit(const BufPtr& v) override { + bufs_.insert(v); + IRVisitor::visit(v); + } + + static std::unordered_set find(const StmtPtr& s) { + BufFinder nf; + s->accept(&nf); + return nf.bufs(); + } + + static std::unordered_set find(const ExprPtr& e) { + BufFinder nf; + e->accept(&nf); + return nf.bufs(); + } + + const std::unordered_set& bufs() { + return bufs_; + } + + private: + std::unordered_set bufs_; +}; + +// Finds all kinds of write operations to the provided Buf. +class WritesToBuf : public IRVisitor { + public: + WritesToBuf(BufPtr target) : target_(std::move(target)) {} + + std::vector writes() { + return writes_; + } + + static std::vector find(const StmtPtr& s, BufPtr b) { + WritesToBuf finder(std::move(b)); + s->accept(&finder); + return finder.writes(); + } + + private: + void visit(const StorePtr& v) override { + if (v->buf() == target_) { + writes_.push_back(v); + } + } + + void visit(const AtomicAddPtr& v) override { + if (v->buf() == target_) { + writes_.push_back(v); + } + } + + BufPtr target_; + std::vector writes_; +}; + +class StmtsReadingBuf : public IRVisitor { + public: + StmtsReadingBuf(BufPtr target) : target_(std::move(target)) {} + + std::vector reads() { + return reads_; + } + + static std::vector find(const StmtPtr& s, BufPtr b) { + StmtsReadingBuf finder(std::move(b)); + s->accept(&finder); + return finder.reads(); + } + + private: + bool readsBuffer(const StmtPtr& s) { + auto loads = NodeFinder::find(s); + for (const auto& l : loads) { + if (l->buf() == target_) { + return true; + } + } + return false; + } + + void visit(const StorePtr& v) override { + if (readsBuffer(v)) { + reads_.push_back(v); + } + } + + void visit(const LetPtr& v) override { + if (readsBuffer(v)) { + reads_.push_back(v); + } + } + + void visit(const CondPtr& v) override { + if (readsBuffer(v)) { + reads_.push_back(v); + } + } + + void visit(const AtomicAddPtr& v) override { + if (readsBuffer(v)) { + reads_.push_back(v); + } + } + + BufPtr target_; + std::vector reads_; +}; + +class ExternalAllocBufFinder : public IRVisitor { + public: + void visit(const ExternalCallWithAllocPtr& v) override { + const auto& bufs_out = v->buf_out_args(); + bufs_.insert(bufs_out.begin(), bufs_out.end()); + IRVisitor::visit(v); + } + + static std::unordered_set find(const StmtPtr& s) { + ExternalAllocBufFinder f; + s->accept(&f); + return f.bufs(); + } + + static std::unordered_set find(const ExprPtr& e) { + ExternalAllocBufFinder f; + e->accept(&f); + return f.bufs(); + } + + const std::unordered_set& bufs() { + return bufs_; + } + + private: + std::unordered_set bufs_; +}; + +// Traverses the IR to determine if a particular Var is modified within it. +class ModifiesVarChecker : public IRVisitor { + public: + ModifiesVarChecker(VarPtr v) : var_(std::move(v)) {} + + static bool check(const StmtPtr& s, VarPtr v) { + ModifiesVarChecker checker(std::move(v)); + s->accept(&checker); + return checker.found(); + } + + bool found() { + return found_; + } + + private: + void visit(const StorePtr& v) override { + if (v->buf()->base_handle() == var_) { + found_ = true; + return; + } + IRVisitor::visit(v); + } + + void visit(const AtomicAddPtr& v) override { + if (v->buf()->base_handle() == var_) { + found_ = true; + return; + } + IRVisitor::visit(v); + } + + void visit(const LetPtr& v) override { + if (v->var() == var_) { + found_ = true; + return; + } + IRVisitor::visit(v); + } + + void visit(const ForPtr& v) override { + if (v->var() == var_) { + found_ = true; + return; + } + IRVisitor::visit(v); + } + + VarPtr var_; + bool found_{false}; +}; + +// Traverse the Block stmt to identify the live range of the specified buf. The +// live range, indicated by a pair of integers, specifies the first and last +// stmt in block stmts that access to the buf. +class BufLiveRange : public IRVisitor { + public: + BufLiveRange(BufPtr b) : buf_(std::move(b)) {} + + static std::tuple liveRange(const StmtPtr& s, BufPtr b) { + BlockPtr block = to(s); + // We Only analyze buffer live ranges for block stmts. + if (!block) { + return std::make_tuple(0, 0); + } + + BufLiveRange analyzer(std::move(b)); + block->accept(&analyzer); + return analyzer.getLiveRange(); + } + + private: + std::tuple getLiveRange() { + return std::make_tuple(begin_, end_); + } + + bool hasBufReads(const StmtPtr& s) { + auto loads1 = NodeFinder::find(s); + for (const auto& l : loads1) { + if (l->buf() == buf_) { + return true; + } + } + auto loads2 = NodeFinder::find(s); + for (const auto& l : loads2) { + for (const auto& lb : l->buf_args()) { + if (lb == buf_) { + return true; + } + } + } + auto loads3 = NodeFinder::find(s); + for (const auto& l : loads3) { + for (const auto& lb : l->buf_args()) { + if (lb == buf_) { + return true; + } + } + } + return false; + } + + bool hasBufWrites(const StmtPtr& s) { + auto writes1 = NodeFinder::find(s); + for (const auto& w : writes1) { + if (w->buf() == buf_) { + return true; + } + } + auto writes2 = NodeFinder::find(s); + for (const auto& w : writes2) { + if (w->buf() == buf_) { + return true; + } + } + auto writes3 = NodeFinder::find(s); + for (const auto& w : writes3) { + for (const auto& wb : w->buf_out_args()) { + if (wb == buf_) { + return true; + } + } + } + return false; + } + + void findAccAndUpdateLiveRange(const StmtPtr& s) { + bool has_reads = hasBufReads(s), has_writes = hasBufWrites(s); + if (has_reads || has_writes) { + if (begin_ == -1) { + begin_ = curr_index_; + }; + end_ = curr_index_; + } + } + + void visit(const BlockPtr& v) override { + for (const StmtPtr& s : *v) { + curr_index_ += 1; + findAccAndUpdateLiveRange(s); + } + } + + BufPtr buf_; + int32_t begin_ = -1; + int32_t end_ = -1; + int32_t curr_index_ = -1; +}; + +// A class that analyzes the given program relevant for Block backend +// It creates a map of multi dim buffers and their flat versions +class CreateBufferMap : public IRVisitor { + public: + const std::unordered_map& getBufferMap() const { + return map_input_to_tensor_bufs_; + } + + private: + void visit(const StorePtr& v) override { + auto load_node = to(v->value()); + if (load_node) { + auto t_buf = load_node->buf(); + map_input_to_tensor_bufs_.emplace(t_buf->name_hint(), v->buf()); + } else { + auto add_node = to(v->value()); + auto mul_node = to(v->value()); + // This means for now, v->value() can be Add or Mul + TORCH_INTERNAL_ASSERT(add_node || mul_node, buildErrorMessage()); + map_input_to_tensor_bufs_.emplace(v->buf()->name_hint(), v->buf()); + } + v->value()->accept(this); + } + std::unordered_map map_input_to_tensor_bufs_; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/block_codegen.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/block_codegen.h new file mode 100644 index 0000000000000000000000000000000000000000..45311409ed6605e3a0ea03939e9be61dbac9c6a2 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/block_codegen.h @@ -0,0 +1,146 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +// A class that analyzes the given program relevant for Block backend. +class BlockAnalysis : public IRVisitor { + public: + bool is_buf_store_target(const BufPtr& buf) const { + return store_targets_.count(buf) > 0; + } + + const std::unordered_set& loads() const { + return loads_; + } + + const std::unordered_set& stores() const { + return store_targets_; + } + + int64_t block_size() const { + return block_size_; + } + + bool areBufsInMap(const std::unordered_set& bufs) const; + + BufPtr getMultiDimBuf(const BufPtr& buf) const; + + std::string getInputName(const BufPtr& buf) const; + + std::string getFlatInputName(const BufPtr& buf) const { + return getInputName(buf) + "_flat"; + } + + std::unordered_map getBufferMap() const { + return map_input_to_tensor_bufs_; + } + + private: + void visit(const StorePtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const ForPtr& v) override; + + std::unordered_map map_input_to_tensor_bufs_; + std::unordered_set store_targets_; + std::unordered_set loads_; + int64_t block_size_ = 32; +}; + +// A class that overrides the underlying IRPrinter to produce Block. +class BlockPrinter : public IRPrinter { + public: + BlockPrinter(std::ostream* os, BlockAnalysis* block_analysis) + : IRPrinter(*os), block_analysis_(block_analysis) {} + + using IRPrinter::name_manager; + using IRPrinter::visit; + + private: + BlockAnalysis* block_analysis_; + std::unordered_map dim_values_map; + std::vector dim_names = {"N", "H", "W", "C"}; + std::vector flat_dim_names = {"N", "NH", "NHW", "NHWC"}; + void PrintTensorInfo(const std::unordered_set& bufs); + void PrintArguments(const std::unordered_set& bufs); + void PrintBufferInfo(const std::unordered_set& bufs); + void PrintDistribution(const std::unordered_set& bufs); + void PrintLoop(const std::unordered_set& bufs, bool block_idx = true); + void PrintReshapeInfo( + const std::unordered_set& bufs, + bool reverse = false); + void PrintDMAs(const std::unordered_set& bufs); + void PrintAdjustBuffers(const std::unordered_set& bufs); + + void visit(const ForPtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const StorePtr& v) override; + void visit(const BlockPtr& v) override; + void visit(const AddPtr& v) override; + void visit(const MulPtr& v) override; +}; + +class TORCH_API BlockCodeGen : public CodeGen { + public: + template + /* implicit */ + BlockCodeGen(StmtPtr stmt, Ts... ts) + : CodeGen( + stmt, + std::vector({BufferArg(ts)...}), + at::Device(at::kCPU)) { + Initialize(); + } + + BlockCodeGen( + StmtPtr stmt, + const std::vector& buffer_args, + at::Device device = at::Device(at::kCPU), + const std::string& kernel_func_name = "func") + : CodeGen(std::move(stmt), buffer_args, device, kernel_func_name) { + Initialize(); + } + + ~BlockCodeGen() override; + + void call(const std::vector& args) override; + void call_raw(const std::vector& args) override; + + void Initialize(); + + std::string getCodeText(const std::string& attr = "") override { + return oss_.str(); + } + + private: + UniqueNameManager* name_manager() { + if (!printer_) { + throw std::runtime_error("Null IRPrinter is not expected"); + } + return printer_->name_manager(); + } + + std::ostream& os() { + return printer_->os(); + } + + std::ostringstream oss_; + std::unique_ptr printer_; + std::unique_ptr block_analysis_; + + std::string GetUniqueFuncName(const std::string& func_prefix); +}; +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/bounds_overlap.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/bounds_overlap.h new file mode 100644 index 0000000000000000000000000000000000000000..0dbb69727875a8007c38b03ad1e29aef5751032a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/bounds_overlap.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include + +#include +#include + +namespace torch::jit::tensorexpr::analysis { + +// A simple class containing the start and end of a range in a single dimension. +struct TORCH_API Bound { + ExprPtr start{nullptr}; + ExprPtr end{nullptr}; + + // This stores whether or not the start and end of this Bound have previously + // been swapped. This occurs when the bound is in a loop with a negative + // stride. + bool swapped{false}; + + Bound() = default; + Bound(ExprPtr s, ExprPtr e) : start(std::move(s)), end(std::move(e)) {} + + void print() const; + bool equals(const Bound& other) const; + + // The comparison operators are conservative. If the compare operator returns + // true, it means that all the elements satisfy the logical expression. But + // the false does not mean the opposite comparison is satisfied. It could be + // but not always. + bool operator==(const Bound& other) const; + bool operator!=(const Bound& other) const; + bool operator<(const Bound& other) const; + bool operator<=(const Bound& other) const; + bool operator>(const Bound& other) const; + bool operator>=(const Bound& other) const; + + void swap() { + std::swap(start, end); + swapped = !swapped; + } +}; + +struct BoundHash { + size_t operator()(const Bound& b) const { + return std::hash()(b.start) ^ std::hash()(b.end); + } +}; + +// The type of overlap found. Each condition is true only if none of the +// previous conditions hold. +// ContainedOrEqual: All elements in the Bound A are in the Bound B (this +// includes the case where the bounds are equal). +// Contains: All elements in the Bound B are in the Bound B. +// PartialOverlap: Any elements in the Bound B are in the Bound A. +// NoOverlap: No elements in the Bound A are in the bound B. +enum class OverlapKind { + ContainedOrEqual, + Contains, + PartialOverlap, + NoOverlap +}; + +// The Bound comparison result. +// True: Every Bound element always satisfies the given comparison operator +// False: Every Bound element always does NOT satisfy the given comparison +// operator +// NotDetermined: Some elements satisfy the given comparison operator and +// some elements not +enum class CmpEvalResult { True, False, NotDetermined }; + +// Returns the kind of overlap between Bound A and Bound A in a single +// dimension. +OverlapKind TORCH_API boundOverlap(const Bound& A, const Bound& B); + +// The comparison is conservative and the compare result is deterministic. +// It means that every element of the Bound to be compared needs to satisfy +// the given comparison operator. +CmpEvalResult TORCH_API compareBound( + const Bound& a, + const Bound& b, + const CompareSelectOperation& cmp_op); + +// A multi dimensional bound representing the bound of a set of indices. +using IndexBounds = std::vector; + +// Returns true if two IndexBounds are equivalent. +bool TORCH_API indexBoundsEquals(const IndexBounds& A, const IndexBounds& B); + +// Flattens a multi dimensional bound to a single dimension. The IndexBounds "a" +// *must* encapsulate the entire range of the buffer. +Bound TORCH_API flattenBounds(const IndexBounds& a); + +// Determines the kind of overlap in X dimensions. +OverlapKind TORCH_API overlaps(const IndexBounds& a, const IndexBounds& b); + +// Returns the Bound slices created by subtracing bound B from bound A. +// Multiple Bounds can be returned in the case where B slices A into two +// distinct regions with no overlap. +// +// For example: +// subtractBound((0, 10), (2, 4)) => [(0, 1), (5, 10)] +// bound A: (0, 10) +// bound B: (2, 4) +// If we remove slice (2, 4) from the slice (0, 10), we will be left +// with 2 slices, one at the start (0, 1), and one at the end (5, 10). +// So, the result of this subtraction is [(0, 1), (5, 10)]. +// +// Note: this doesn't use IndexBounds because the Bounds returned do not +// represent multiple different dimensions. +std::vector TORCH_API subtractBound(const Bound& a, const Bound& b); + +// Returns the bound slices created by subtracting the IndexBounds B from A. +std::vector TORCH_API subtractIndicesBounds( + const IndexBounds& A, + const IndexBounds& B, + OverlapKind overlap); +std::vector TORCH_API +subtractIndicesBounds(const IndexBounds& A, const IndexBounds& B); + +} // namespace torch::jit::tensorexpr::analysis diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/codegen.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/codegen.h new file mode 100644 index 0000000000000000000000000000000000000000..cad930b58bd9303acb999b97b0cf52742ed53e1f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/codegen.h @@ -0,0 +1,269 @@ +#pragma once + +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +template +class PaddedBuffer; + +class TORCH_API CodeGen { + public: + class BufferArg; + class CallArg; + + template + CodeGen(StmtPtr stmt, Ts... ts) + : stmt_(std::move(stmt)), buffer_args_({BufferArg(ts)...}) {} + + CodeGen( + StmtPtr stmt, + std::vector buffer_args, + at::Device device = at::kCPU, + std::string kernel_func_name = "func"); + + virtual ~CodeGen() = default; + + StmtPtr stmt() const { + return stmt_; + } + + void set_stmt(StmtPtr s) { + stmt_ = std::move(s); + } + + void apply_mutator(IRMutator* mutator) { + stmt_ = stmt_->accept_mutator(mutator); + } + + void apply_visitor(IRVisitor* visitor) { + stmt_->accept(visitor); + } + + std::vector& buffer_args() { + return buffer_args_; + } + + const std::vector& buffer_args() const { + return buffer_args_; + } + + at::Device device() { + return device_; + } + + // This function returns the generated code as + // a string. + virtual std::string getCodeText( + const std::string& attr [[maybe_unused]] = "") { + return ""; + } + + // TODO: Figure out how to unify these call interfaces. + + /// Call a function with a vector of CallArgs, which are tagged + /// unions that properly type the arguments. + virtual void call(const std::vector& args) = 0; + + /// Call a function faster than a regular `call` by assuming that + /// the generated kernel already knows the type of the arguments, so + /// they can be type-punned with `void*`s. + virtual void call_raw(const std::vector& args) = 0; + + /// Call a function even faster than a regular call, by assuming + /// that the number of thread blocks can be derived from `numel` via + /// a simple division, rather than evaluating an expression. + virtual void call_with_numel(void** args, int64_t numel); + + virtual at::Tensor empty_strided( + c10::IntArrayRef size, + c10::IntArrayRef stride, + std::optional dtype_opt, + std::optional layout_opt, + std::optional device_opt, + std::optional pin_memory_opt) { + return at::empty_strided( + size, stride, dtype_opt, layout_opt, device_opt, pin_memory_opt); + } + + const std::string& kernel_func_name() const { + return kernel_func_name_; + } + + void allocIntermediateBufs(); + + protected: + static void* argToPtr(const BufferArg& bufferArg, const CallArg& callArg); + + private: + StmtPtr stmt_; + std::vector buffer_args_; + at::Device device_ = at::kCPU; + std::string kernel_func_name_ = "func"; +}; + +class TORCH_API ExtCallMemoryReuse : public IRMutator { + static std::unordered_map makeExtCallFuncNameMap(); + static const std::unordered_map extCallFuncNameMap_; + + public: + explicit ExtCallMemoryReuse( + const std::vector& bufferArgs); + ~ExtCallMemoryReuse() override = default; + StmtPtr mutate(const ExternalCallPtr& v) override; + + private: + std::unordered_set bufferArgs_; +}; + +class CodeGen::BufferArg { + public: + BufferArg(const Tensor& tensor) : buf_(tensor.buf()) {} + BufferArg(const VarHandle& var) : var_(var.node()), isVar_(true) {} + BufferArg(const BufHandle& buf) : buf_(buf.node()) {} + BufferArg(BufPtr buf) : buf_(std::move(buf)) {} + + VarPtr var() const { + return isVar_ ? var_ : buf_->base_handle(); + } + + BufPtr buf() const { + return buf_; + } + + bool isVar() const { + return isVar_; + } + + Dtype dtype() const { + return isVar_ ? var_->dtype() : buf_->dtype(); + } + + private: + VarPtr var_ = nullptr; + BufPtr buf_ = nullptr; + bool isVar_ = false; +}; + +class CodeGen::CallArg { + public: + template + CallArg(const PaddedBuffer& buffer); + + template + CallArg(const std::vector& buffer) + : data_(const_cast(buffer.data())) {} + + CallArg(void* ptr) : data_(ptr) {} + +#define ARG_TYPE_CTOR(Type, Name) \ + CallArg(Type v) { \ + memcpy(buffer_, &v, sizeof(Type)); \ + data_ = (void*)buffer_; \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, ARG_TYPE_CTOR) +#undef ARG_TYPE_CTOR + + void* data() const { + return data_; + } + + CallArg(const CallArg& rhs) { + if (rhs.data_ == rhs.buffer_) { + memcpy(this->buffer_, rhs.buffer_, sizeof(rhs.buffer_)); + this->data_ = (void*)(this->buffer_); + } else { + this->data_ = rhs.data_; + } + } + + CallArg& operator=(const CallArg& rhs) { + if (this == &rhs) { + return *this; + } + if (rhs.data_ == rhs.buffer_) { + memcpy(this->buffer_, rhs.buffer_, sizeof(rhs.buffer_)); + this->data_ = (void*)(this->buffer_); + } else { + this->data_ = rhs.data_; + } + return *this; + } + +#define ARG_PTR_DEFINE(Type, Name) \ + Type* Name##Ptr() const { \ + TORCH_INTERNAL_ASSERT(data_ == (void*)buffer_); \ + return (Type*)data_; \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, ARG_PTR_DEFINE) +#undef ARG_PTR_DEFINE + + private: + void* data_; + // Regarding a scalar value, CallArg uses void**=&data_ to store it. But the + // bit width of a pointer is 32bit on a 32bit platform. It cannot store the + // scalar if the bit width of the scalar is larger than 32bit, such as double + // and long. Hence, we add 8 bytes buffer dedicated to storing the scalar + // value regardless its bit width is less or greater than 32bits. + char buffer_[8] = {0}; // 64bits +}; + +class RegisterCodeGenList { + public: + TORCH_API static RegisterCodeGenList& GetInstance(); + + using StmtFactoryMethod = std::function( + StmtPtr stmt, + const std::vector&, + at::Device device, + const std::string& kernel_func_name)>; + + TORCH_API StmtFactoryMethod FindStmtFactoryMethod(const std::string& name); + RegisterCodeGenList(const RegisterCodeGenList&) = delete; + RegisterCodeGenList& operator=(const RegisterCodeGenList&) = delete; + + private: + template + friend class RegisterCodeGen; + RegisterCodeGenList() = default; + TORCH_API void AddStmtFactoryMethod( + const std::string& name, + const StmtFactoryMethod& stmt_factory_method); + + std::unordered_map stmt_factory_methods_; +}; + +template +class RegisterCodeGen { + public: + explicit RegisterCodeGen(const std::string& name) { + RegisterCodeGenList& codegen_list = RegisterCodeGenList::GetInstance(); + codegen_list.AddStmtFactoryMethod( + name, + [](const StmtPtr& stmt, + const std::vector& params, + at::Device device, + const std::string& kernel_func_name) { + return std::make_unique( + stmt, params, device, kernel_func_name); + }); + } +}; + +TORCH_API std::unique_ptr CreateCodeGen( + const std::string& name, + StmtPtr stmt, + const std::vector& params, + at::Device device = at::kCPU, + const std::string& kernel_func_name = "func"); + +class TORCH_API GenericIntrinsicsExpander : public IRMutator { + protected: + ExprPtr mutate(const IntrinsicsPtr& v) override; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cpp_intrinsics.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cpp_intrinsics.h new file mode 100644 index 0000000000000000000000000000000000000000..0e4bb6a615254b043825cd316cc4b85e09f5fb58 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cpp_intrinsics.h @@ -0,0 +1,32 @@ +#pragma once + +namespace torch::jit::tensorexpr { + +constexpr auto cpp_intrinsics_definition = R"( +namespace std { + +template , int> = 0> +T rsqrt(T v) { + return 1.0f / std::sqrt(v); +} + +template , int> = 0> +T frac(T v) { + T intpart; + return std::modf(v, &intpart); +} + +template +To bitcast(const From& v) { + assert(sizeof(To) == sizeof(From)); + To res; + std::memcpy(&res, &v, sizeof(From)); + return res; +} + +} // namespace std +)"; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_codegen.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_codegen.h new file mode 100644 index 0000000000000000000000000000000000000000..394b6ad242d4c90de9a6983b604171cf72208ac4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_codegen.h @@ -0,0 +1,286 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +// A class that analyzes the given program relevant for Cuda backends. +class CudaAnalysis : public IRVisitor { + public: + CudaAnalysis() { + gpu_block_extents_ = {alloc(1), alloc(1), alloc(1)}; + gpu_thread_extents_ = { + alloc(1), alloc(1), alloc(1)}; + } + bool is_buf_store_target(const BufPtr& buf) const { + return store_targets_.count(buf) > 0; + } + + const std::unordered_set& thread_local_bufs() const { + return thread_local_bufs_; + } + + const std::unordered_set& cross_block_bufs() const { + return cross_block_bufs_; + } + + const std::vector& gpu_block_extents() const { + return gpu_block_extents_; + } + + const std::vector& gpu_thread_extents() const { + return gpu_thread_extents_; + } + + private: + void visit(const StorePtr& v) override { + store_targets_.insert(v->buf()); + } + + void visit(const AllocatePtr& v) override; + void visit(const FreePtr& v) override; + void visit(const PlacementAllocatePtr& v) override; + void visit(const ForPtr& v) override; + + std::unordered_set store_targets_; + std::unordered_set thread_local_bufs_; + std::unordered_set cross_block_bufs_; + + std::vector gpu_block_extents_; + std::vector gpu_thread_extents_; +}; + +// An IRMutator that replaces binding loop options with Cuda metavars, and masks +// statements blocks which should execute with less reach than the launch +// parameter extent. +// +// We do this by segmenting each block into chunks which should have the same +// execution parameters, then if those params differ from the max mask each dim. +class GPUMetaVarRewriter : public IRMutator { + public: + explicit GPUMetaVarRewriter(const CudaAnalysis* cuda_analysis) + : cuda_analysis_(cuda_analysis) { + gpu_block_vars_ = { + alloc("blockIdx.x", kInt), + alloc("blockIdx.y", kInt), + alloc("blockIdx.z", kInt)}; + gpu_thread_vars_ = { + alloc("threadIdx.x", kInt), + alloc("threadIdx.y", kInt), + alloc("threadIdx.z", kInt)}; + + current_block_reach_ = { + alloc(1), alloc(1), alloc(1)}; + current_thread_reach_ = { + alloc(1), alloc(1), alloc(1)}; + } + + StmtPtr mutate(const ForPtr& v) override; + StmtPtr mutate(const BlockPtr& v) override; + + const std::vector& gpu_block_vars() const { + return gpu_block_vars_; + } + + const std::vector& gpu_thread_vars() const { + return gpu_thread_vars_; + } + + const std::vector& gpu_block_extents() const { + return cuda_analysis_->gpu_block_extents(); + } + + const std::vector& gpu_thread_extents() const { + return cuda_analysis_->gpu_thread_extents(); + } + + private: + // When processing a block, stores the contents of each sub-segment. + class Segment { + public: + void reset(bool mask) { + stmts_.clear(); + mask_ = mask; + } + + bool empty() const { + return stmts_.empty(); + } + + std::vector& stmts() { + return stmts_; + } + bool mask() { + return mask_; + } + + private: + std::vector stmts_; + bool mask_{true}; + }; + + // Returns true if the current execution scope is equivalent to the launch + // parameters. + bool isFullExtent(); + + std::vector gpu_block_vars_; + std::vector gpu_thread_vars_; + + std::vector current_block_reach_; + std::vector current_thread_reach_; + + const CudaAnalysis* cuda_analysis_; +}; + +// A class that overrides the underlying IRPrinter to produce Cuda C. +class CudaPrinter : public IRPrinter { + public: + explicit CudaPrinter( + std::ostream* os, + const CudaAnalysis* cuda_analysis, + bool has_random) + : IRPrinter(*os), cuda_analysis_(cuda_analysis) { + if (has_random) { + rand_func_ = alloc("rand", kHandle); + } + } + + void visit(const CastPtr& v) override; + void visit(const IntrinsicsPtr& v) override; + void visit(const ForPtr& v) override; + + void visit(const LoadPtr& v) override; + void visit(const StorePtr& v) override; + void visit(const AtomicAddPtr& v) override; + void visit(const MaxPtr& v) override; + void visit(const MinPtr& v) override; + void visit(const IfThenElsePtr& v) override; + void visit(const BlockPtr& v) override; + void visit(const AllocatePtr& v) override; + void visit(const FreePtr& v) override; + void visit(const LetPtr& v) override; + + void visit(const ExternalCallPtr& v) override; + + VarPtr rand_func() const { + return rand_func_; + } + + std::string dtypeToCppString(const Dtype& dtype) override; + + using IRPrinter::name_manager; + using IRPrinter::visit; + + private: + VarPtr rand_func_; + const CudaAnalysis* cuda_analysis_; + + void print_flat_alloc(const AllocatePtr& alloc); +}; + +// Construct Cuda C from the buffer and tensor input, and invoke the +// kernel when real arguments are provided. +class TORCH_CUDA_CU_API CudaCodeGen : public CodeGen { + public: + template + CudaCodeGen(StmtPtr stmt, Ts... ts) + : CodeGen( + stmt, + std::vector({BufferArg(ts)...}), + at::Device(at::kCUDA, at::cuda::current_device())) { + Initialize(); + } + + CudaCodeGen( + StmtPtr stmt, + const std::vector& buffer_args, + at::Device device = at::Device(at::kCUDA, at::cuda::current_device()), + const std::string& kernel_func_name = "func") + : CodeGen(std::move(stmt), buffer_args, device, kernel_func_name) { + Initialize(); + } + + ~CudaCodeGen() override; + + void call(const std::vector& args) override; + void call_raw(const std::vector& args) override; + void call_with_numel(void** args, int64_t numel) override; + + template + void operator()(const Ts&... ts) { + call(std::vector({CallArg(ts)...})); + } + + at::Tensor empty_strided( + c10::IntArrayRef size, + c10::IntArrayRef stride, + std::optional dtype_opt, + std::optional layout_opt, + std::optional device_opt, + std::optional pin_memory_opt) override; + + const std::vector& gpu_block_extents() const { + return cuda_analysis_->gpu_block_extents(); + } + + const std::vector& gpu_thread_extents() const { + return cuda_analysis_->gpu_thread_extents(); + } + + std::string getCodeText(const std::string& attr = "") override { + return oss_.str(); + } + + private: + void Initialize(); + + void CompileToNVRTC(const std::string& code, const std::string& func_name); + + UniqueNameManager* name_manager() { + if (!printer_) { + throw std::runtime_error("Null IRPrinter is not expected"); + } + return printer_->name_manager(); + } + + std::ostream& os() { + return printer_->os(); + } + + std::ostringstream oss_; + std::unique_ptr printer_; + std::unique_ptr cuda_analysis_; + std::unique_ptr metavar_rewriter_; + std::unordered_set taken_func_names; + std::mutex eval_lock_; + CUfunction function_{nullptr}; + bool has_random_ = false; + int thread_block_size_ = -1; + + std::vector arg_pos_in_extents_; +#ifdef TORCH_ENABLE_LLVM + std::vector> block_extents_eval_; + std::vector> thread_extents_eval_; +#else + std::vector> block_extents_eval_; + std::vector> thread_extents_eval_; +#endif + + std::string GetUniqueFuncName(const std::string& func_prefix); +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_random.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_random.h new file mode 100644 index 0000000000000000000000000000000000000000..ce59bba11e8773762ec0292dbd06d211770f1e14 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/cuda_random.h @@ -0,0 +1,100 @@ +#pragma once + +namespace torch::jit::tensorexpr { + +constexpr auto philox_random_string = R"( + +class Philox { +public: + __device__ inline Philox(unsigned long long seed, + unsigned long long subsequence, + unsigned long long offset) { + key.x = (unsigned int)seed; + key.y = (unsigned int)(seed >> 32); + counter = make_uint4(0, 0, 0, 0); + counter.z = (unsigned int)(subsequence); + counter.w = (unsigned int)(subsequence >> 32); + STATE = 0; + incr_n(offset / 4); + } + + __device__ inline unsigned long operator()() { + if(STATE == 0) { + uint4 counter_ = counter; + uint2 key_ = key; + for(int i = 0; i < 9; i++) { + counter_ = single_round(counter_, key_); + key_.x += (kPhilox10A); key_.y += (kPhilox10B); + } + output = single_round(counter_, key_); + incr(); + } + unsigned long ret; + switch(STATE) { + case 0: ret = output.x; break; + case 1: ret = output.y; break; + case 2: ret = output.z; break; + case 3: ret = output.w; break; + } + STATE = (STATE + 1) % 4; + return ret; + } + +private: + uint4 counter; + uint4 output; + uint2 key; + unsigned int STATE; + __device__ inline void incr_n(unsigned long long n) { + unsigned int nlo = (unsigned int)(n); + unsigned int nhi = (unsigned int)(n >> 32); + counter.x += nlo; + if (counter.x < nlo) + nhi++; + counter.y += nhi; + if (nhi <= counter.y) + return; + if (++counter.z) + return; + ++counter.w; + } + __device__ inline void incr() { + if (++counter.x) + return; + if (++counter.y) + return; + if (++counter.z) + return; + ++counter.w; + } + __device__ unsigned int mulhilo32(unsigned int a, unsigned int b, + unsigned int *result_high) { + *result_high = __umulhi(a, b); + return a*b; + } + + __device__ inline uint4 single_round(uint4 ctr, uint2 key) { + unsigned int hi0; + unsigned int hi1; + unsigned int lo0 = mulhilo32(kPhiloxSA, ctr.x, &hi0); + unsigned int lo1 = mulhilo32(kPhiloxSB, ctr.z, &hi1); + + uint4 ret = {hi1 ^ ctr.y ^ key.x, lo1, hi0 ^ ctr.w ^ key.y, lo0}; + return ret; + } + + static const unsigned long kPhilox10A = 0x9E3779B9; + static const unsigned long kPhilox10B = 0xBB67AE85; + static const unsigned long kPhiloxSA = 0xD2511F53; + static const unsigned long kPhiloxSB = 0xCD9E8D57; +}; + +// Inverse of 2^32. +#define M_RAN_INVM32 2.3283064e-10f +__device__ __inline__ float Uint32ToFloat(unsigned int x) { + return x * M_RAN_INVM32; +} + +)"; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/eval.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/eval.h new file mode 100644 index 0000000000000000000000000000000000000000..8cbc1689e0c9b9f8836b2b1c493d33b33ec3072e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/eval.h @@ -0,0 +1,325 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +class InterpValue { + public: + InterpValue() : dtype_(kInt) { + Intvalues.push_back(0); + } + + template + InterpValue(Dtype dtype, T v) : dtype_(dtype) { +#define TYPE_CASE(Type, Name) \ + if (dtype == k##Name) { \ + Name##values.push_back(v); \ + return; \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TYPE_CASE) +#undef TYPE_CASE + throw unsupported_dtype(); + } + +#define VALUE_CTOR(Type, Name) \ + InterpValue(Type v) : dtype_(k##Name) { \ + Name##values.push_back(v); \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, VALUE_CTOR) +#undef VALUE_CTOR + + explicit InterpValue(c10::quint8 v) : dtype_(kQUInt8) { + QUInt8values.emplace_back(v.val_); + } + + explicit InterpValue(c10::qint8 v) : dtype_(kQInt8) { + QInt8values.emplace_back(v.val_); + } + +#define VALUE_VEC_CTOR(Type, Name) \ + InterpValue(const std::vector& v) \ + : dtype_(Dtype(k##Name, v.size())), Name##values(v) {} + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, VALUE_VEC_CTOR) + VALUE_VEC_CTOR(c10::quint8, QUInt8) + VALUE_VEC_CTOR(c10::qint8, QInt8) +#undef VALUE_VEC_CTOR + + template + T as() const; + + template + const std::vector& as_vec() const; + + int64_t intValue() const; + + Dtype dtype() const { + return dtype_; + } + + private: + Dtype dtype_; + +#define VALUE_STORAGE(Type, Name) std::vector Name##values; + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, VALUE_STORAGE) + VALUE_STORAGE(c10::qint8, QInt8) + VALUE_STORAGE(c10::quint8, QUInt8) +#undef VALUE_STORAGE + void* ptr{nullptr}; +}; + +#define VALUE_AS_DISPATCH(Type, Name) \ + template <> \ + inline Type InterpValue::as() const { \ + if (dtype_ != k##Name) { \ + throw unsupported_dtype(); \ + } \ + return Name##values[0]; \ + } +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, VALUE_AS_DISPATCH) +VALUE_AS_DISPATCH(c10::quint8, QUInt8) +VALUE_AS_DISPATCH(c10::qint8, QInt8) +#undef VALUE_AS_DISPATCH + +#define VALUE_AS_VEC_DISPATCH(Type, Name) \ + template <> \ + inline const std::vector& InterpValue::as_vec() const { \ + if (dtype_.scalar_type() != ScalarType::Name) { \ + throw unsupported_dtype(); \ + } \ + return Name##values; \ + } +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, VALUE_AS_VEC_DISPATCH) +VALUE_AS_VEC_DISPATCH(c10::quint8, QUInt8) +VALUE_AS_VEC_DISPATCH(c10::qint8, QInt8) +#undef VALUE_AS_VEC_DISPATCH + +template +auto underlyingValue(Type x) { + return x; +} + +template <> +inline auto underlyingValue(c10::quint8 x) { + return x.val_; +} + +template <> +inline auto underlyingValue(c10::qint8 x) { + return x.val_; +} + +template +To raw_bitcast(const From& src) { + TORCH_CHECK(sizeof(To) == sizeof(From), "Invalid bitcast invocation"); + To storage; + std::memcpy(&storage, &src, sizeof(To)); + return reinterpret_cast(storage); +} + +class SimpleIREvaluatorImpl; +class TORCH_API SimpleIREvaluator : public CodeGen { + public: + SimpleIREvaluator( + StmtPtr stmt, + const std::vector& buffer_args, + at::Device device = at::kCPU, + const std::string& kernel_func_name = "func"); + + ~SimpleIREvaluator() override; + + void call(const std::vector& args) override; + void call_raw(const std::vector& args) override; + + template + void operator()(const Ts&... ts) { + std::vector args({CallArg(ts)...}); + call(args); + } + + void bindVar(const VarPtr& v, const ExprPtr& e); + InterpValue value() const; + + private: + void bindArg(const BufferArg& buf, void* data); + void expand_intrinsics() { + GenericIntrinsicsExpander intrinsics_expander; + apply_mutator(&intrinsics_expander); + } + + std::unique_ptr impl_; +}; + +template +class ExprEval { + public: + using BufferArg = CodeGen::BufferArg; + using CallArg = CodeGen::CallArg; + + template + ExprEval(const ExprHandle& expr, Ts... ts) + : ExprEval(expr, {BufferArg(ts)...}) {} + + ExprEval(const ExprHandle& expr, const std::vector& buffer_args) + : dtype_(expr.dtype()) { + std::vector buffer_args_extended = buffer_args; + BufHandle ret_buf("ret_val", {1}, dtype_); + std::vector indices; + ExprHandle zero = IntImm::make(0); + indices.reserve(ret_buf.ndim()); + for (size_t i = 0; i < ret_buf.ndim(); i++) { + indices.push_back(zero); + } + StmtPtr store_stmt = Store::make(ret_buf, indices, expr); + buffer_args_extended.emplace_back(ret_buf); + codegen_.reset(new CodeGenType(store_stmt, buffer_args_extended)); + } + + template + void operator()(Ts... ts) { + call(ts...); + } + + void operator()(const std::vector& call_args) { + call(call_args); + } + + void bindVar(VarPtr v, ExprPtr e) { + codegen_->bindVar(v, e); + } + + void bindVar(const VarHandle& v, const ExprHandle& e) { + codegen_->bindVar(v.node(), e.node()); + } + + template + void call(Ts... ts) { + call({CallArg(ts)...}); + } + + void call(const std::vector& call_args) { + std::vector call_args_extended = call_args; + switch (dtype_.scalar_type()) { +#define TYPE_CASE(Type, Name) \ + case ScalarType::Name: { \ + std::vector ret_val_arg(1); \ + call_args_extended.emplace_back(ret_val_arg); \ + codegen_->call(call_args_extended); \ + ret_value_ = InterpValue(ret_val_arg[0]); \ + } break; + AT_FORALL_SCALAR_TYPES_AND2(Half, BFloat16, TYPE_CASE); + TYPE_CASE(c10::quint8, QUInt8); + TYPE_CASE(c10::qint8, QInt8); +#undef TYPE_CASE + case ScalarType::Bool: { + std::vector ret_val_arg(1); + call_args_extended.emplace_back(ret_val_arg.data()); + codegen_->call(call_args_extended); + ret_value_ = InterpValue((bool)ret_val_arg[0]); + } break; + default: + throw unsupported_dtype(); + } + } + + void call_raw(const std::vector& args) { + std::vector args_extended = args; + switch (dtype_.scalar_type()) { +#define TYPE_CASE(Type, Name) \ + case ScalarType::Name: { \ + std::vector ret_val_arg(1); \ + args_extended.push_back(ret_val_arg.data()); \ + codegen_->call_raw(args_extended); \ + ret_value_ = InterpValue(ret_val_arg[0]); \ + } break; + AT_FORALL_SCALAR_TYPES_AND2(Half, BFloat16, TYPE_CASE); + TYPE_CASE(c10::quint8, QUInt8); + TYPE_CASE(c10::qint8, QInt8); +#undef TYPE_CASE + case ScalarType::Bool: { + std::vector ret_val_arg(1); + args_extended.push_back(ret_val_arg.data()); + codegen_->call_raw(args_extended); + ret_value_ = InterpValue((bool)ret_val_arg[0]); + } break; + default: + throw unsupported_dtype(); + } + } + + template + T value(const std::vector& args) { + call_raw(args); + return ret_value_.as(); + } + + template + T value(Ts... ts) { + call(std::forward(ts)...); + return ret_value_.as(); + } + + Dtype dtype() { + return dtype_; + } + + private: + Dtype dtype_; + std::unique_ptr codegen_; + InterpValue ret_value_; +}; + +// Evaluates the given expression and returns an int64_t value if the result of +// the given expression is int64_t. +std::optional evalInt(ExprPtr e); + +// Substitutes the given vars with their corresponding expressions in the input +// expression. +inline ExprPtr Substitute(const ExprPtr& expr, const VarMapping& var_mapping) { + VarSubMutator var_sub(var_mapping); + return expr->accept_mutator(&var_sub); +} + +// Substitutes the given vars with their corresponding expressions in the input +// statement. +inline StmtPtr Substitute(const StmtPtr& stmt, const VarMapping& var_mapping) { + VarSubMutator var_sub(var_mapping); + return stmt->accept_mutator(&var_sub); +} + +// Creates a clone of the input expression and substitutes the given vars with +// their corresponding expressions in the clone. +// NOTE: This works because cloning reuses variables and does not create new +// ones, and `VarMapping` input has variables as the key. +inline ExprPtr SubstituteInClone( + const ExprPtr& expr, + const VarMapping& var_mapping) { + VarSubMutator var_sub(var_mapping); + return Expr::clone(expr)->accept_mutator(&var_sub); +} + +// Creates a clone of the input statement and substitutes the given vars with +// their corresponding expressions in the clone. +// NOTE: This works because cloning reuses variables and does not create new +// ones, and `VarMapping` input has variables as the key. +inline StmtPtr SubstituteInClone( + const StmtPtr& stmt, + const VarMapping& var_mapping) { + VarSubMutator var_sub(var_mapping); + return Stmt::clone(stmt)->accept_mutator(&var_sub); +} + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/exceptions.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..1241400474a402c665451b333acc252a05d9ece3 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/exceptions.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include + +// Forward declarations of types + +namespace torch::jit::tensorexpr { +class Expr; +class Stmt; +} // namespace torch::jit::tensorexpr + +// Forward declarations of functions +namespace std { +TORCH_API std::string to_string(const torch::jit::tensorexpr::ExprPtr&); +TORCH_API std::string to_string(const torch::jit::tensorexpr::StmtPtr&); +} // namespace std + +namespace torch::jit::tensorexpr { + +class unsupported_dtype : public std::runtime_error { + public: + explicit unsupported_dtype() : std::runtime_error("UNSUPPORTED DTYPE") {} + explicit unsupported_dtype(const std::string& err) + : std::runtime_error("UNSUPPORTED DTYPE: " + err) {} +}; + +class out_of_range_index : public std::runtime_error { + public: + explicit out_of_range_index() : std::runtime_error("OUT OF RANGE INDEX") {} + explicit out_of_range_index(const std::string& err) + : std::runtime_error("OUT OF RANGE INDEX: " + err) {} +}; + +class unimplemented_lowering : public std::runtime_error { + public: + explicit unimplemented_lowering() + : std::runtime_error("UNIMPLEMENTED LOWERING") {} + explicit unimplemented_lowering(const ExprPtr& expr) + : std::runtime_error("UNIMPLEMENTED LOWERING: " + std::to_string(expr)) {} + explicit unimplemented_lowering(const StmtPtr& stmt) + : std::runtime_error("UNIMPLEMENTED LOWERING: " + std::to_string(stmt)) {} +}; + +class malformed_input : public std::runtime_error { + public: + explicit malformed_input() : std::runtime_error("MALFORMED INPUT") {} + explicit malformed_input(const std::string& err) + : std::runtime_error("MALFORMED INPUT: " + err) {} + explicit malformed_input(const ExprPtr& expr) + : std::runtime_error("MALFORMED INPUT: " + std::to_string(expr)) {} + explicit malformed_input(const std::string& err, const ExprPtr& expr) + : std::runtime_error( + "MALFORMED INPUT: " + err + " - " + std::to_string(expr)) {} + explicit malformed_input(const StmtPtr& stmt) + : std::runtime_error("MALFORMED INPUT: " + std::to_string(stmt)) {} + explicit malformed_input(const std::string& err, const StmtPtr& stmt) + : std::runtime_error( + "MALFORMED INPUT: " + err + " - " + std::to_string(stmt)) {} +}; + +class malformed_ir : public std::runtime_error { + public: + explicit malformed_ir() : std::runtime_error("MALFORMED IR") {} + explicit malformed_ir(const std::string& err) + : std::runtime_error("MALFORMED IR: " + err) {} + explicit malformed_ir(const ExprPtr& expr) + : std::runtime_error("MALFORMED IR: " + std::to_string(expr)) {} + explicit malformed_ir(const std::string& err, const ExprPtr& expr) + : std::runtime_error( + "MALFORMED IR: " + err + " - " + std::to_string(expr)) {} + explicit malformed_ir(const StmtPtr& stmt) + : std::runtime_error("MALFORMED IR: " + std::to_string(stmt)) {} + explicit malformed_ir(const std::string& err, const StmtPtr& stmt) + : std::runtime_error( + "MALFORMED IR: " + err + " - " + std::to_string(stmt)) {} +}; + +TORCH_API std::string buildErrorMessage(const std::string& s = ""); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/expr.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/expr.h new file mode 100644 index 0000000000000000000000000000000000000000..30d3ecdccda9df9fd26903ef59dec634b661e8f8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/expr.h @@ -0,0 +1,493 @@ +/** + * This file implements the core classes for Tensor Expressions. + * + * The structure of the expressions is inspired by Halide/TVM IR. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +enum IRNodeType { + kPrimitive, + kAdd, + kSub, + kMul, + kDiv, + kMod, + kMax, + kMin, + kAnd, + kOr, + kLshift, + kRshift, + kXor, + kCompareSelect, + kCast, + kBitCast, + kOther, +}; + +// The common base between all expression node. +class TORCH_API Expr : public std::enable_shared_from_this { + public: + explicit Expr(Dtype dtype, IRNodeType expr_type = kOther) + : dtype_(dtype), expr_type_(expr_type) {} + virtual ~Expr() = default; + Dtype dtype() const { + return dtype_; + } + virtual void accept(IRVisitor* visitor) = 0; + virtual ExprPtr accept_mutator(IRMutator* mutator) = 0; + + IRNodeType expr_type() const { + return expr_type_; + } + // Is this a fixed (constant) immediate value. + virtual bool isConstant() const { + return false; + } + + void set_dtype(Dtype dtype) { + dtype_ = dtype; + } + + /* + * Make a deep copy of the given expression. + * + * All sub-expressions inside the given expressions are also cloned. Note + * that the variables are not deep-copied since they are immutable. + */ + static ExprPtr clone(const ExprPtr& s); + + protected: + std::shared_ptr getptr() { + return shared_from_this(); + } + + private: + Dtype dtype_; + IRNodeType expr_type_; +}; + +// A CRTP pattern to accept visitors for children class, +// and dispatch back to the children. +template +class ExprNode : public Base { + public: + using ExprNodeBase = ExprNode; + void accept(IRVisitor* visitor) override { + visitor->visit(static_to(Base::getptr())); + } + ExprPtr accept_mutator(IRMutator* mutator) override; + // pass the constructor to the base class + using Base::Base; +}; + +// A wrapper object to the underlying ExprNode. +// Also serves the primary way to build and operate on other expressions. +class TORCH_API ExprHandle { + public: + ExprHandle() = default; + explicit ExprHandle(ExprPtr node) : base_expr_node_(std::move(node)) {} + + ExprPtr node() { + return base_expr_node_; + } + + ExprPtr node() const { + return base_expr_node_; + } + + bool empty() const { + return base_expr_node_ == nullptr; + } + +#define IMM_EXPR_DECLARE(Type, Name) ExprHandle(Type v); + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_EXPR_DECLARE) +#undef IMM_EXPR_DECLARE + + template + NodePtr AsNode() { + return to(this->node()); + } + + template + NodePtr AsNode() const { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return const_cast(this)->AsNode(); + } + + Dtype dtype() const { + return node()->dtype(); + } + + // Handling the math operators. + ExprHandle operator+(const ExprHandle& other) const; + ExprHandle operator-(const ExprHandle& other) const; + ExprHandle operator*(const ExprHandle& other) const; + ExprHandle operator/(const ExprHandle& other) const; + ExprHandle operator%(const ExprHandle& other) const; + ExprHandle operator==(const ExprHandle& other) const; + ExprHandle operator!=(const ExprHandle& other) const; + ExprHandle operator>(const ExprHandle& other) const; + ExprHandle operator>=(const ExprHandle& other) const; + ExprHandle operator<(const ExprHandle& other) const; + ExprHandle operator<=(const ExprHandle& other) const; + ExprHandle operator&(const ExprHandle& other) const; + ExprHandle operator|(const ExprHandle& other) const; + ExprHandle operator&&(const ExprHandle& other) const; + ExprHandle operator||(const ExprHandle& other) const; + ExprHandle operator^(const ExprHandle& other) const; + ExprHandle operator<<(const ExprHandle& other) const; + ExprHandle operator>>(const ExprHandle& other) const; + + private: + ExprPtr base_expr_node_ = nullptr; +}; + +// The underlying representation node to a Var. +// Currently, each Var object represents a unique variable, even though the +// names might be the same. We should consider add a unique_name as well. +class TORCH_API Var : public ExprNode { + public: + static ExprHandle make(const std::string& name_hint, Dtype dtype) { + return ExprHandle(alloc(name_hint, dtype)); + } + static ExprHandle make(Dtype dtype) { + return ExprHandle(alloc("", dtype)); + } + + // TODO: unique_name + const std::string& name_hint() const { + return name_hint_; + } + + void set_name_hint(const std::string& name) { + name_hint_ = name; + } + + void set_name_hint(std::string&& name) { + name_hint_ = std::move(name); + } + + Var(std::string name_hint, Dtype dtype) + : ExprNodeBase(dtype, kPrimitive), name_hint_(std::move(name_hint)) {} + + private: + std::string name_hint_; +}; + +TORCH_API std::vector make_contiguous_strides( + const std::vector& dims); +TORCH_API std::vector make_channels_last_strides( + const std::vector& dims); + +class TORCH_API Buf : public ExprNode { + public: + static BufHandle make(const std::vector& dims, Dtype dtype); + + static BufHandle make( + const std::string& name_hint, + const std::vector& dims, + const std::vector& strides, + Dtype dtype); + + static BufHandle make( + const std::string& name_hint, + const std::vector& dims, + Dtype dtype, + std::optional initializer = std::nullopt, + const std::optional>& strides = std::nullopt, + std::optional qscale = std::nullopt, + std::optional qzero = std::nullopt); + + // TODO: unique_name + VarPtr base_handle() const { + return base_handle_; + } + void set_base_handle(VarPtr base_handle) { + base_handle_ = std::move(base_handle); + } + + const std::string& name_hint() const { + return base_handle_->name_hint(); + } + void set_name_hint(const std::string& name_hint) { + base_handle_->set_name_hint(name_hint); + } + + Buf(const std::string& name_hint, + const std::vector& dims, + Dtype dtype, + ExprPtr initializer = nullptr, + std::optional> strides = std::nullopt, + ExprPtr qscale = nullptr, + ExprPtr qzero = nullptr) + : Buf(alloc(name_hint, kHandle), + dims, + dtype, + std::move(initializer), + std::move(strides), + std::move(qscale), + std::move(qzero)) {} + + Buf(const VarPtr& var, + std::vector dims, + Dtype dtype, + ExprPtr initializer = nullptr, + std::optional> strides = std::nullopt, + ExprPtr qscale = nullptr, + ExprPtr qzero = nullptr); + + size_t ndim() const { + return dims_.size(); + } + ExprPtr dim(size_t index) const { + if (index >= ndim()) { + throw out_of_range_index(); + } + return dims_[index]; + } + std::vector dims() const { + return dims_; + } + void set_dims(std::vector dims) { + dims_ = std::move(dims); + } + + std::vector strides() const { + return strides_; + } + + void set_strides(std::vector strides) { + strides_ = std::move(strides); + } + + ExprPtr initializer() const { + return initializer_; + } + + ExprPtr qzero() const { + return qzero_; + } + + ExprPtr qscale() const { + return qscale_; + } + + void set_qzero(ExprPtr qzero) { + qzero_ = std::move(qzero); + } + + void set_qscale(ExprPtr qscale) { + qscale_ = std::move(qscale); + } + + bool hasConstantDims() const { + for (const auto& d : dims_) { + if (!d->isConstant()) { + return false; + } + } + return true; + } + + bool is_contiguous( + at::MemoryFormat memory_format = at::MemoryFormat::Contiguous) const; + + // The channels-last 1d can benefit the performance of some operators like + // conv1d. But the MemoryFormat enum has not covered this layout yet. Hence, + // we abstract a dedicated function to check channels-last 1d contiguous. + // + // Channels-last 1d: + // dims: n c l + // strides(nlc): c*l 1 c + bool is_channels_last_1d_contiguous() const { + if (dims_.size() != 3) { + return false; + } + return is_stride_one(1) && is_cont_with(2, 1) && is_cont_with(0, 2); + } + + private: + bool is_cont_with(int cur_dim, int adjacent_dim) const; + bool is_stride_one(int cur_dim) const; + + VarPtr base_handle_; + std::vector dims_; + std::vector strides_; + ExprPtr initializer_; + // qscale_ and qzero_ are used only for quantized dtypes Bufs: kQUInt8, kQInt8 + ExprPtr qscale_; + ExprPtr qzero_; +}; + +class TORCH_API BufHandle : public ExprHandle { + public: + BufHandle( + const std::string& name_hint, + const std::vector& dims, + Dtype dtype) + : ExprHandle(Buf::make(name_hint, dims, dtype)) {} + + BufHandle( + const std::string& name_hint, + const std::vector& dims, + const std::vector& strides, + Dtype dtype) + : ExprHandle(Buf::make(name_hint, dims, strides, dtype)) {} + + BufHandle(const std::vector& dims, Dtype dtype) + : ExprHandle(Buf::make("_", dims, dtype)) {} + + explicit BufHandle(Dtype dtype) : ExprHandle(Buf::make("_", {}, dtype)) {} + + explicit BufHandle(BufPtr node) : ExprHandle(std::move(node)) {} + BufPtr node() const { + return static_to(ExprHandle::node()); + } + BufPtr node() { + return static_to(ExprHandle::node()); + } + + template + inline ExprHandle load(const Ts&... ts) const; + + template + inline ExprHandle load(const std::vector& args) const; + + inline ExprHandle load(const std::vector& args) const; + + StorePtr store(const std::vector& args, const ExprHandle& val) + const; + + bool operator==(const BufHandle& other) const { + return this->node() == other.node(); + } + bool operator!=(const BufHandle& other) const { + return !(*this == other); + } + + const std::string& name_hint() const { + return this->node()->name_hint(); + } + + bool empty() const { + return (this->node() == nullptr); + } + + size_t ndim() const { + return node()->ndim(); + } + + std::vector dims() const; + + ExprHandle dim(size_t index) const { + return ExprHandle(node()->dim(index)); + } + + bool is_contiguous( + at::MemoryFormat memory_format = at::MemoryFormat::Contiguous) const { + return node()->is_contiguous(memory_format); + } + + bool is_channels_last_1d_contiguous() const { + return node()->is_channels_last_1d_contiguous(); + } +}; + +// An expression to construct the underlying variable node. +// Note: do not store any info here, since it is often possible to slice this +// object. For example: VarHandle x('x'); ExprHandle x2 = x; +class TORCH_API VarHandle : public ExprHandle { + public: + // Creates an empty VarHandle whose base Var is set to nullptr. + VarHandle() : ExprHandle() {} + + explicit VarHandle(Dtype dtype) : ExprHandle(Var::make(dtype)) {} + + VarHandle(const std::string& name_hint, Dtype dtype) + : ExprHandle(Var::make(name_hint, dtype)) {} + + explicit VarHandle(VarPtr node) : ExprHandle(std::move(node)) {} + + VarPtr node() const { + return static_to(ExprHandle::node()); + } + bool operator==(const VarHandle& other) const { + return this->node() == other.node(); + } + bool operator!=(const VarHandle& other) const { + return !(*this == other); + } + + const std::string& name_hint() const { + return this->node()->name_hint(); + } + bool empty() const { + return (this->node() == nullptr); + } +}; + +template +ExprPtr ExprNode::accept_mutator(IRMutator* mutator) { + return mutator->mutate(static_to(Base::getptr())); +} + +inline bool same_node(const ExprHandle& expr1, const ExprHandle& expr2) { + return expr1.AsNode() == expr2.AsNode(); +} + +TORCH_API ExprHandle sin(const ExprHandle& v); +TORCH_API ExprHandle cos(const ExprHandle& v); +TORCH_API ExprHandle tan(const ExprHandle& v); +TORCH_API ExprHandle asin(const ExprHandle& v); +TORCH_API ExprHandle acos(const ExprHandle& v); +TORCH_API ExprHandle atan(const ExprHandle& v); +TORCH_API ExprHandle sinh(const ExprHandle& v); +TORCH_API ExprHandle cosh(const ExprHandle& v); +TORCH_API ExprHandle tanh(const ExprHandle& v); +TORCH_API ExprHandle sigmoid(const ExprHandle& v); +TORCH_API ExprHandle exp(const ExprHandle& v); +TORCH_API ExprHandle expm1(const ExprHandle& v); +TORCH_API ExprHandle abs(const ExprHandle& v); +TORCH_API ExprHandle log(const ExprHandle& v); +TORCH_API ExprHandle fast_tanh(const ExprHandle& v); +TORCH_API ExprHandle fast_sigmoid(const ExprHandle& v); +TORCH_API ExprHandle fast_log(const ExprHandle& v); +TORCH_API ExprHandle log_vml(const ExprHandle& v); +TORCH_API ExprHandle log2(const ExprHandle& v); +TORCH_API ExprHandle log10(const ExprHandle& v); +TORCH_API ExprHandle log1p(const ExprHandle& v); +TORCH_API ExprHandle erf(const ExprHandle& v); +TORCH_API ExprHandle erfc(const ExprHandle& v); +TORCH_API ExprHandle sqrt(const ExprHandle& v); +TORCH_API ExprHandle rsqrt(const ExprHandle& v); +TORCH_API ExprHandle ceil(const ExprHandle& v); +TORCH_API ExprHandle floor(const ExprHandle& v); +TORCH_API ExprHandle round(const ExprHandle& v); +TORCH_API ExprHandle trunc(const ExprHandle& v); +TORCH_API ExprHandle frac(const ExprHandle& v); +TORCH_API ExprHandle lgamma(const ExprHandle& v); +TORCH_API ExprHandle atan2(const ExprHandle& v1, const ExprHandle& v2); +TORCH_API ExprHandle pow(const ExprHandle& v1, const ExprHandle& v2); +TORCH_API ExprHandle fmod(const ExprHandle& v1, const ExprHandle& v2); +TORCH_API ExprHandle remainder(const ExprHandle& v1, const ExprHandle& v2); +TORCH_API ExprHandle isnan(const ExprHandle& v1); +TORCH_API ExprHandle Relu(const ExprHandle& v1); + +TORCH_API ExprHandle +ifThenElse(const ExprHandle& c, const ExprHandle& t, const ExprHandle& f); + +TORCH_API ExprHandle expr_to_vec(const ExprHandle& v, int lanes); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/external_functions_registry.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/external_functions_registry.h new file mode 100644 index 0000000000000000000000000000000000000000..e2466a3deab30ccf2a96a4de69c84544714c3c77 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/external_functions_registry.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +// The external functions that could be called from NNC must have the same +// signature defined by `NNCExternalFunction`. +// +// Why this signature? +// It was picked for two reasons: 1) it should be generic enough to represent +// most of the ops we might want to call, 2) it should be possible to generate a +// code for this call in LLVM codegen. +// The first 5 parameters allow to pass any number of contiguous CPU tensors in +// case we need to run aten ops (TODO: support different devices). The first +// buffer in the array is assumed to be the output buffer. We couldn't use +// `at::Tensor` (or `c10::IValue`) type there directly as it would mean that +// we'd need to declare it in LLVM codegen in LLVM IR form, which would be very +// cumbersome and hard to maintain. Note that the dimensions of all tensors are +// concatenated into a single array buf_dims. We do not need to pass its length, +// since it can be deduced from total number of buffers and their ranks. +// +// The last 2 arguments allow to pass any non-tensor arguments encoded as an +// array of int64_t values. The way they are encoded is not specified and could +// be arbitrary - whatever the most convenient for the specific bridge function +// is. +// +// The bridge functions must not throw exceptions - properly propagating them +// from the generated code is too cumbersome, and thus all calls to functions +// that could throw must be wrapped with try-catch blocks. +using NNCExternalFunction = void (*)( + int64_t bufs_num, + void** buf_data, + int64_t* buf_ranks, + int64_t* buf_dims, + int64_t* buf_strides, + int8_t* buf_dtypes, + int64_t args_num, + int64_t* extra_args); + +// Return a global map "function-name" -> "function-pointer" for all registered +// in NNC external functions +TORCH_API std::unordered_map& +getNNCFunctionRegistry(); + +// To register a new external function in NNC one needs to create an instance of +// this struct +struct RegisterNNCExternalFunction { + RegisterNNCExternalFunction(const std::string& name, NNCExternalFunction fn) { + getNNCFunctionRegistry()[name] = fn; + } +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/fwd_decls.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/fwd_decls.h new file mode 100644 index 0000000000000000000000000000000000000000..0849c8cdb21073ff60b268f458098259674d6f36 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/fwd_decls.h @@ -0,0 +1,125 @@ +#pragma once +#include +#include + +namespace torch::jit::tensorexpr { + +template +using NodePtr = std::shared_ptr; + +template +NodePtr to(const NodePtr& x) { + return std::dynamic_pointer_cast(x); +} + +template +NodePtr static_to(NodePtr x) { + return std::static_pointer_cast(x); +} + +template +NodePtr alloc(Args&&... args) { + return std::make_shared(std::forward(args)...); +} + +class Buf; +class Expr; +class Stmt; +class Var; + +using BufPtr = NodePtr; +using ExprPtr = NodePtr; +using StmtPtr = NodePtr; +using VarPtr = NodePtr; + +class ExprHandle; +class VarHandle; +class BufHandle; + +class Add; +class And; +class BitCast; +class Broadcast; +class Cast; +class CompareSelect; +class Div; +class IfThenElse; +class Intrinsics; +class Let; +class Load; +class Lshift; +class Max; +class MaxTerm; +class Min; +class MinTerm; +class Mod; +class Mul; +class Or; +class Polynomial; +class Ramp; +class ReduceOp; +class RoundOff; +class Rshift; +class Store; +class Sub; +class Term; +class Xor; +using AddPtr = NodePtr; +using AndPtr = NodePtr; +using BitCastPtr = NodePtr; +using BroadcastPtr = NodePtr; +using CastPtr = NodePtr; +using CompareSelectPtr = NodePtr; +using DivPtr = NodePtr
; +using IfThenElsePtr = NodePtr; +using IntrinsicsPtr = NodePtr; +using LetPtr = NodePtr; +using LoadPtr = NodePtr; +using LshiftPtr = NodePtr; +using MaxPtr = NodePtr; +using MaxTermPtr = NodePtr; +using MinPtr = NodePtr; +using MinTermPtr = NodePtr; +using ModPtr = NodePtr; +using MulPtr = NodePtr; +using OrPtr = NodePtr; +using PolynomialPtr = NodePtr; +using RampPtr = NodePtr; +using ReduceOpPtr = NodePtr; +using RoundOffPtr = NodePtr; +using RshiftPtr = NodePtr; +using StorePtr = NodePtr; +using SubPtr = NodePtr; +using TermPtr = NodePtr; +using XorPtr = NodePtr; + +class Allocate; +class AtomicAdd; +class Block; +class Cond; +class ExternalCall; +class ExternalCallWithAlloc; +class For; +class Free; +class FreeExt; +class PlacementAllocate; +class SyncThreads; +using AllocatePtr = NodePtr; +using AtomicAddPtr = NodePtr; +using BlockPtr = NodePtr; +using CondPtr = NodePtr; +using ExternalCallPtr = NodePtr; +using ExternalCallWithAllocPtr = NodePtr; +using ForPtr = NodePtr; +using FreePtr = NodePtr; +using FreeExtPtr = NodePtr; +using PlacementAllocatePtr = NodePtr; +using SyncThreadsPtr = NodePtr; + +#define IMM_DECLARE(Type, Name) \ + class Name##Imm; \ + using Name##ImmPtr = NodePtr; +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_DECLARE) +#undef IMM_DECLARE + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/graph_opt.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/graph_opt.h new file mode 100644 index 0000000000000000000000000000000000000000..95bab317d0a0d0790ce3c23d78c44b0a67ba2fc8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/graph_opt.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +namespace torch::jit::tensorexpr { + +// Optimize aten::cat ops in the given subgraph. +// +// Moving users of cat to its inputs. +// Cat ops get lowered into multiple loops, one per input. When the result +// of cat is used by some other op, it results in a situation where inlining +// of cat does not happen. This in turn results in intermediate buffers +// being created for the result of cat, since it is not inlined. +// +// For example, consider the following graph: +// graph(%x : Float(10, strides=[1], device=cpu), +// %y : Float(20, strides=[1], device=cpu)): +// %dim : int = prim::Constant[value=0]() +// %xy_list : Tensor[] = prim::ListConstruct(%x, %y) +// %cat : Float(60, strides=[1], device=cpu) = aten::cat(%xy_list, %dim) +// %5 : Float(60, strides=[1], device=cpu) = aten::log(%cat) +// return (%5))IR"; +// +// This will get lowered into: +// Allocate(aten_cat); +// for (...) +// aten_cat[...] = x[...] +// for (...) +// aten_cat[...] = y[...] +// for (...) +// aten_log[...] = log(aten_cat[...]) +// Free(aten_cat); +// Note that aten_cat is not inlined into aten_log and it results in +// an intermediate buffer allocation as well. +// +// Optimization: +// We move the ops that use the result of `cat` into its inputs whenever +// possible. +// +// The graph above will be transformed to: +// graph(%x : Float(10, strides=[1], device=cpu), +// %y : Float(20, strides=[1], device=cpu)): +// %3 : int = prim::Constant[value=0]() +// %7 : Float(10, strides=[1], device=cpu) = aten::log(%x) +// %8 : Float(20, strides=[1], device=cpu) = aten::log(%y) +// %9 : Tensor[] = prim::ListConstruct(%7, %8) +// %10 : Float(60, strides=[1], device=cpu) = aten::cat(%9, %3) +// return (%10) +// +// This will get lowered into: +// for (...) +// aten_cat[...] = log(x[...]) +// for (...) +// aten_cat[...] = log(y[...]) +// aten_cat is the output buffer here. + +bool OptimizeCat(const std::shared_ptr& graph); + +TORCH_API void annotateInputShapes( + const std::shared_ptr& graph, + const std::vector>& example_inputs); +TORCH_API std::shared_ptr removeUnusedSelfArgument( + const std::shared_ptr& graph); +TORCH_API std::shared_ptr removeGraphOutput( + const std::shared_ptr& graph, + size_t idx); +TORCH_API std::shared_ptr replaceListOutputWithTuple( + const std::shared_ptr& graph); + +// Perform \p ITERS rounds of "trimming" for the given \p GRAPH. +// +// Trimming means that we try to remove a small portion of the graph while +// keeping it valid. This is useful for debugging when we try to find a minimal +// example reproducing the issue at hand. When ITERS is 0, the graph remains +// unchanged, when ITERS is a big number, the graph usually becomes empty. +TORCH_API std::shared_ptr trimGraph( + const std::shared_ptr& graph, + int64_t iters); + +// Scan all values in the given graph and replace each dimension with a size Xi +// present in \p SIZES with a symbolic shape Yi. Return a vector of symbol +// values [Y0, Y1, .., Yn]. +// +// For example: +// Input: +// graph(%x : Float(10, 20, 30, 40)): +// %y : Float(10, 20, 30, 40) = aten::relu(%x) +// return %y +// +// If we run makeShapesSymbolic(graph, {20, 40}), then we'll get: +// +// graph(%x : Float(10, SS(-3), 30, SS(-5))): +// %y : Float(10, SS(-3), 30, SS(-5)) = aten::relu(%x) +// return %y +// +// and get {-3, -5} as the return value. +TORCH_API std::vector makeShapesSymbolic( + std::shared_ptr& graph, + const std::vector& sizes); + +// Inspect the graph and report whether it can be converted to TE IR. +// TODO: add error reporting for graphs that can't be converted. +TORCH_API bool isGraphCompilable(const std::shared_ptr& graph); + +// Examine the graph and (hackily) fill in missing tensor type info, such as +// scalar type, device, and strides. Ideally, this should be done by a proper +// dtype/device/shape propagation passes, but until they are ready we can use +// this, not always correct, workaround pass. +TORCH_API void fixupMissingShapeInfo(const std::shared_ptr& graph); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/half_support.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/half_support.h new file mode 100644 index 0000000000000000000000000000000000000000..82f7e0ff7c9a224c86100ede78e38abc7975aa8b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/half_support.h @@ -0,0 +1,212 @@ +#pragma once + +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +// Walk the Statement looking for Half size loads/stores. +class HalfChecker : public IRVisitor { + public: + HalfChecker(const std::vector& args) { + for (const auto& BA : args) { + hasHalf_ |= BA.dtype().scalar_type() == ScalarType::Half; + } + } + + bool hasHalf() const { + return hasHalf_; + } + + bool hasBFloat16() const { + return hasBFloat16_; + } + + void visit(const LoadPtr& v) override { + hasHalf_ |= v->dtype().scalar_type() == ScalarType::Half; + hasBFloat16_ |= v->dtype().scalar_type() == ScalarType::BFloat16; + IRVisitor::visit(v); + } + + void visit(const StorePtr& v) override { + hasHalf_ |= v->buf()->dtype().scalar_type() == ScalarType::Half; + hasBFloat16_ |= v->buf()->dtype().scalar_type() == ScalarType::BFloat16; + IRVisitor::visit(v); + } + + void visit(const HalfImmPtr& v) override { + hasHalf_ = true; + } + + void visit(const BFloat16ImmPtr& v) override { + hasBFloat16_ = true; + } + + void visit(const CastPtr& v) override { + hasHalf_ |= v->dtype().scalar_type() == ScalarType::Half; + hasBFloat16_ |= v->dtype().scalar_type() == ScalarType::BFloat16; + IRVisitor::visit(v); + } + + private: + bool hasHalf_{false}; + bool hasBFloat16_{false}; +}; + +class HalfRewriter : public IRMutator { + ExprPtr mutate(const LoadPtr& v) override { + ExprPtr child = IRMutator::mutate(v); + if (!isHalf(child)) { + return child; + } + + ExprPtr ret = alloc( + child->dtype().cloneWithScalarType(ScalarType::Float), child); + + inserted_half_casts_.insert(ret); + return ret; + } + + StmtPtr mutate(const StorePtr& v) override { + // Since mutation changes the `value()` expression in-place, we need to + // get the dtype of the `value()` before that is mutated. + auto newType = v->value()->dtype(); + ExprPtr new_val = v->value()->accept_mutator(this); + auto bufType = v->buf()->dtype(); + + if (isHalf(newType.scalar_type())) { + new_val = alloc(newType, new_val); + inserted_half_casts_.insert(new_val); + } + + // The scalar_type of value is not Half while the buf is Half + if (!isHalf(newType.scalar_type()) && isHalf(bufType.scalar_type())) { + new_val = alloc( + newType.cloneWithScalarType(bufType.scalar_type()), new_val); + inserted_half_casts_.insert(new_val); + } + + v->set_value(new_val); + return v; + } + + ExprPtr mutate(const HalfImmPtr& v) override { + return alloc(kFloat, v); + } + + ExprPtr mutate(const BFloat16ImmPtr& v) override { + return alloc(kFloat, v); + } + + ExprPtr mutate(const CastPtr& v) override { + ExprPtr child = v->src_value()->accept_mutator(this); + + // just don't allow half casts we didn't insert. + if (isHalf(v)) { + if (inserted_half_casts_.count(v) < 1) { + v->set_src_value(child); + v->set_dtype(v->dtype().cloneWithScalarType(c10::kFloat)); + return v; + } + } + + // Remove Half(Float()) and friends. + CastPtr cast_child = to(child); + if (cast_child) { + auto cast_to_double = v->dtype().scalar_type() == ScalarType::Double; + auto from_half = isHalf(cast_child->src_value()); + // Cannot simplify the double(float(half)) to double(half) as NNC does + // not support cast BF16 to double directly. + auto not_cast_half_to_doulbe = !(cast_to_double && from_half); + if (v->dtype().is_floating_point() && + cast_child->dtype().is_floating_point() && not_cast_half_to_doulbe) { + return alloc(v->dtype(), cast_child->src_value()); + } + } + + if (child == v->src_value()) { + return v; + } + + return alloc(v->dtype(), child); + } + + StmtPtr mutate(const LetPtr& v) override { + if (isHalf(v->var()->dtype().scalar_type())) { + VarPtr load_new_var = alloc(v->var()->name_hint(), kFloat); + ExprPtr new_value = alloc( + v->var()->dtype().cloneWithScalarType(ScalarType::Float), + v->value()->accept_mutator(this)); + var_map[v->var()] = load_new_var; + + return alloc(load_new_var, new_value); + } + + return IRMutator::mutate(v); + } + + ExprPtr mutate(const VarPtr& v) override { + auto it = var_map.find(v); + if (it != var_map.end()) { + return it->second; + } + + return v; + } + + template + ExprPtr mutateArithmetic(T v) { + IRMutator::mutate(v); + if (isHalf(v)) { + v->set_dtype(v->dtype().cloneWithScalarType(c10::kFloat)); + } + return v; + } + + ExprPtr mutate(const AddPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const SubPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const MulPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const DivPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const MaxPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const MinPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const CompareSelectPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const BroadcastPtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const IfThenElsePtr& v) override { + return mutateArithmetic(v); + } + ExprPtr mutate(const IntrinsicsPtr& v) override { + return mutateArithmetic(v); + } + + private: + static bool isHalf(ScalarType st) { + return st == ScalarType::Half || st == ScalarType::BFloat16; + } + + static bool isHalf(const ExprPtr& v) { + return isHalf(v->dtype().scalar_type()); + } + + std::unordered_set inserted_half_casts_; + std::unordered_map var_map; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/hash_provider.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/hash_provider.h new file mode 100644 index 0000000000000000000000000000000000000000..57a64c569aa95af10ae85c51e1e0a37ec6f87d62 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/hash_provider.h @@ -0,0 +1,281 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +struct TORCH_API SimplifierHashType { + SimplifierHashType() = default; + explicit SimplifierHashType(size_t s) : _h(s) {} + + bool operator==(const SimplifierHashType& other) const; + bool operator!=(const SimplifierHashType& other) const; + bool operator<(const SimplifierHashType& other) const; + bool operator==(const size_t other) const; + bool operator!=(const size_t other) const; + + size_t _h{0}; +}; + +} // namespace torch::jit::tensorexpr + +namespace std { +template <> +struct hash { + size_t operator()(const torch::jit::tensorexpr::SimplifierHashType& k) const { + return k._h; + } +}; + +} // namespace std + +namespace torch::jit::tensorexpr { + +#define CACHE_GUARD() \ + if (cachedHash(v)) { \ + return; \ + } + +class Term; +class Polynomial; + +/* Expression hasher providing comparable values representing sub-exprs. + * Uses memoization to avoid excessive recursion. */ +class TORCH_API HashProvider : public IRVisitor { + public: + template + SimplifierHashType hash(T e) { + e->accept(this); + return hashOf(e); + } + + bool cachedHash(const ExprPtr& e) { + return exprToHash_.find(e) != exprToHash_.end(); + } + bool cachedHash(const StmtPtr& s) { + return stmtToHash_.find(s) != stmtToHash_.end(); + } + + void clearCache() { + exprToHash_.clear(); + stmtToHash_.clear(); + } + + void visit(const AddPtr& v) override; + void visit(const SubPtr& v) override; + void visit(const MulPtr& v) override; + void visit(const DivPtr& v) override; + void visit(const ModPtr& v) override; + void visit(const RoundOffPtr& v) override; + void visit(const MaxPtr& v) override; + void visit(const MinPtr& v) override; + void visit(const AndPtr& v) override; + void visit(const OrPtr& v) override; + void visit(const XorPtr& v) override; + void visit(const LshiftPtr& v) override; + void visit(const RshiftPtr& v) override; + void visit(const CompareSelectPtr& v) override; + +#define IMM_VISIT(Type, Name) \ + void visit(const Name##ImmPtr& v) override { \ + CACHE_GUARD(); \ + putHash(v, hash_combine(#Name, v->value())); \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_VISIT) +#undef IMM_VISIT + + void visit(const CastPtr& v) override; + void visit(const VarPtr& v) override; + void visit(const RampPtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const StorePtr& v) override; + void visit(const BlockPtr& v) override; + void visit(const ForPtr& v) override; + void visit(const BroadcastPtr& v) override; + void visit(const IfThenElsePtr& v) override; + void visit(const IntrinsicsPtr& v) override; + void visit(const AllocatePtr& v) override; + void visit(const FreePtr& v) override; + void visit(const CondPtr& v) override; + void visit(const TermPtr& v) override; + void visit(const PolynomialPtr& v) override; + void visit(const MaxTermPtr& v) override; + void visit(const MinTermPtr& v) override; + + template + SimplifierHashType hash_combine(const Types&... args) { + SimplifierHashType seed; + _hash_combine(seed, args...); + return seed; + } + + private: + SimplifierHashType hashOf(const ExprPtr& e) { + auto it = exprToHash_.find(e); + if (it != exprToHash_.end()) { + return it->second; + } + + // As a failsafe fall back to IRPrinter. + std::stringstream ss; + IRPrinter printer(ss); + e->accept(&printer); + SimplifierHashType hash = SimplifierHashType(te_hash(ss.str())); + putHash(e, hash); + + return hash; + } + + SimplifierHashType hashOf(const StmtPtr& s) { + auto it = stmtToHash_.find(s); + if (it != stmtToHash_.end()) { + return it->second; + } + + // As a failsafe fall back to IRPrinter. + std::stringstream ss; + IRPrinter printer(ss); + s->accept(&printer); + SimplifierHashType hash = SimplifierHashType(te_hash(ss.str())); + putHash(s, hash); + + return hash; + } + + // Hash funcs for various types, numbers are random. + template + void _hash_combine(SimplifierHashType& seed, const T& val) { + seed._h ^= te_hash(val) + 0x1f752c19 + (seed._h << 7) + (seed._h >> 4); + } + + void _hash_combine(SimplifierHashType& seed, const char* val) { + seed._h ^= te_hash(val) + 0x1f752c19 + (seed._h << 7) + (seed._h >> 4); + } + + // at:::Half doesn't have a prime_number_hash, so cast to short. + void _hash_combine(SimplifierHashType& seed, const at::Half& val) { + seed._h ^= + te_hash((uint16_t)val) + 0x1f752c19 + (seed._h << 7) + (seed._h >> 4); + } + + void _hash_combine(SimplifierHashType& seed, const Dtype& val) { + seed._h ^= te_hash(val.ToCppString()) + 0x1f752c19 + (seed._h << 7) + + (seed._h >> 4); + } + + void _hash_combine(SimplifierHashType& seed, ExprPtr e) { + _hash_combine(seed, hash(std::move(e))); + } + + template + void _hash_combine( + SimplifierHashType& seed, + const T& val, + const Types&... args) { + _hash_combine(seed, val); + _hash_combine(seed, args...); + } + + void putHash(const ExprPtr& e, SimplifierHashType h) { + auto res = exprToHash_.emplace(e, h); + if (res.second == false) { + // This is always a logic bug since we should check the cache first. + throw std::runtime_error("hash collision"); + } + } + void putHash(const StmtPtr& s, SimplifierHashType h) { + auto res = stmtToHash_.emplace(s, h); + if (res.second == false) { + // This is always a logic bug since we should check the cache first. + throw std::runtime_error("hash collision"); + } + } + + std::unordered_map exprToHash_; + std::unordered_map stmtToHash_; + UniqueNameManager name_manager_; + + size_t te_hash(SimplifierHashType val) { + return val._h; + } + + size_t te_hash(int64_t val) { + // put the thing down. + size_t h = val ^ 0x647AA4D20C0B; + // bit flip it. + size_t h2 = ~h; + // and reverse byte order. + size_t h3 = 0; + for (unsigned int i = 0; i < 64; i += 8) { + h3 |= ((h2 >> i) & 0xFF) << (64 - i - 8); + } + return h3; + } + + size_t te_hash(int32_t val) { + int64_t v2 = val; + return te_hash(v2); + } + + size_t te_hash(uint32_t val) { + int64_t v2 = val; + return te_hash(v2); + } + + size_t te_hash(uint64_t val) { + int64_t v2 = val; + return te_hash(v2); + } + + size_t te_hash(int16_t val) { + int64_t v2 = val; + return te_hash(v2); + } + + size_t te_hash(std::string val) { + size_t hash{0}; + int64_t intval{0}; + int64_t s = val.size() - 1; + while (s >= 0) { + for (unsigned int i = 0; i < 8; ++i) { + if (s < 0) + break; + int64_t c = val[s]; + intval |= (c << (i * 8)); + + s--; + } + hash ^= te_hash(intval); + intval = 0; + } + + return hash; + } + + size_t te_hash(double d) { + int64_t* n = reinterpret_cast(&d); + return te_hash(*n); + } + + size_t te_hash(float d) { + int32_t* n = reinterpret_cast(&d); + return te_hash(*n); + } + + size_t te_hash(at::Half d) { + int16_t* n = reinterpret_cast(&d); + return te_hash(*n); + } + + size_t te_hash(at::BFloat16 d) { + int16_t* n = reinterpret_cast(&d); + return te_hash(*n); + } +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/intrinsic_symbols.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/intrinsic_symbols.h new file mode 100644 index 0000000000000000000000000000000000000000..7508090f93060865cbd9cb5b48c0cfa34bc1b72a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/intrinsic_symbols.h @@ -0,0 +1,22 @@ +#pragma once + +#ifdef TORCH_ENABLE_LLVM +#include + +namespace torch { +namespace jit { +namespace tensorexpr { + +struct SymbolAddress { + const char* symbol; + void* address; + + SymbolAddress(const char* sym, void* addr) : symbol(sym), address(addr) {} +}; + +c10::ArrayRef getIntrinsicSymbols(); + +} // namespace tensorexpr +} // namespace jit +} // namespace torch +#endif // TORCH_ENABLE_LLVM diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir.h new file mode 100644 index 0000000000000000000000000000000000000000..a8ceabe701e7d773c48a5a967559847b6ac1204f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir.h @@ -0,0 +1,916 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +enum CompareSelectOperation { + kEQ = 0, + kGT, + kGE, + kLT, + kLE, + kNE, +}; + +enum CompareSelectBias { + kUnbiased, + kLikely, + kUnlikely, +}; + +inline int getPrecedence(IRNodeType ty) { + // Match C++ operator precedence rules, since some pretty-print expressions to + // C++. SEE: https://en.cppreference.com/w/cpp/language/operator_precedence + switch (ty) { + case kPrimitive: + return 0; + case kCast: + case kBitCast: + return 2; + case kAdd: + case kSub: + return 6; + case kMul: + case kDiv: + case kMod: + return 5; + case kMax: + case kMin: + return 99; + case kAnd: + return 11; + case kOr: + return 13; + case kLshift: + case kRshift: + return 7; + case kXor: + return 12; + case kCompareSelect: + return 16; + default: + return 99; + } +} + +class TORCH_API Cast : public ExprNode { + public: + ExprPtr src_value() const { + return src_value_; + } + + void set_src_value(ExprPtr src_value) { + src_value_ = std::move(src_value); + } + + static ExprHandle make(Dtype dtype, const ExprHandle& src_value) { + return ExprHandle(alloc(dtype, src_value.node())); + } + Cast(Dtype dtype, ExprPtr src_value) + : ExprNodeBase(dtype, kCast), src_value_(std::move(src_value)) {} + + bool isConstant() const override { + return src_value_->isConstant(); + } + + private: + ExprPtr src_value_; +}; + +template +ExprHandle cast(const ExprHandle& src_value) { + return Cast::make(Dtype(ToDtype(), src_value.dtype().lanes()), src_value); +} + +// This is a bitwise cast, akin to bitcast in LLVM +class TORCH_API BitCast : public ExprNode { + public: + ExprPtr src_value() const { + return src_value_; + } + + void set_src_value(ExprPtr src_value) { + src_value_ = std::move(src_value); + } + + static ExprHandle make(Dtype dtype, const ExprHandle& src_value) { + return ExprHandle(alloc(dtype, src_value.node())); + } + BitCast(Dtype dtype, ExprPtr src_value) + : ExprNodeBase(dtype, kBitCast), src_value_(std::move(src_value)) { + TORCH_CHECK(src_value_->dtype().byte_size() == dtype.byte_size()); + } + + bool isConstant() const override { + return src_value_->isConstant(); + } + + private: + ExprPtr src_value_; +}; + +template +ExprHandle bitcast(const ExprHandle& src_value) { + return BitCast::make( + Dtype(ToDtype(), src_value.dtype().lanes()), src_value); +} + +// Represent the expression node for binary operators. +// A CRTP pattern to share common code among the operators. +template +class BinaryOpNode : public ExprNode { + public: + ExprPtr lhs() const { + return this->lhs_; + } + ExprPtr rhs() const { + return this->rhs_; + } + + void set_lhs(ExprPtr lhs) { + lhs_ = std::move(lhs); + } + + void set_rhs(ExprPtr rhs) { + rhs_ = std::move(rhs); + } + + static ExprHandle make(const ExprHandle& lhs, const ExprHandle& rhs) { + return ExprHandle(alloc(lhs.node(), rhs.node())); + } + + BinaryOpNode( + ExprPtr lhs_v, + ExprPtr rhs_v, + IRNodeType expr_type, + ScalarType ret_type = ScalarType::Undefined) + : ExprNode( + BinaryOpDtype(lhs_v->dtype(), rhs_v->dtype(), ret_type), + expr_type), + lhs_(CastIfNeeded(std::move(lhs_v), ExprNode::dtype())), + rhs_(CastIfNeeded(std::move(rhs_v), ExprNode::dtype())) {} + + private: + static ExprPtr CastIfNeeded(ExprPtr expr, Dtype dst_dtype) { + if (expr->dtype() == dst_dtype) { + return expr; + } + return Cast::make(dst_dtype, ExprHandle(std::move(expr))).node(); + } + + ExprPtr lhs_; + ExprPtr rhs_; +}; + +namespace detail { +template +void bin_op_deducer(BinaryOpNode); +bool bin_op_deducer(...); +} // namespace detail + +class TORCH_API Add : public BinaryOpNode { + public: + Add(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kAdd) {} +}; + +class TORCH_API Sub : public BinaryOpNode { + public: + Sub(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kSub) {} +}; + +class TORCH_API Mul : public BinaryOpNode { + public: + Mul(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kMul) {} +}; + +class TORCH_API Div : public BinaryOpNode
{ + public: + Div(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kDiv) {} +}; + +class TORCH_API Mod : public BinaryOpNode { + public: + Mod(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kMod) {} +}; + +template +class BitwiseOpNode : public BinaryOpNode { + public: + BitwiseOpNode(ExprPtr lhs, ExprPtr rhs, IRNodeType type) + : BinaryOpNode(std::move(lhs), std::move(rhs), type) {} + + static ExprHandle make(const ExprHandle& lhs, const ExprHandle& rhs) { + if (!lhs.dtype().is_integral()) { + throw unsupported_dtype(); + } + if (lhs.dtype() != rhs.dtype()) { + throw malformed_input("lhs/rhs dtype mismatch"); + } + return BinaryOpNode::make(lhs, rhs); + } +}; + +class TORCH_API And : public BitwiseOpNode { + public: + And(ExprPtr lhs, ExprPtr rhs) + : BitwiseOpNode(std::move(lhs), std::move(rhs), IRNodeType::kAnd) {} +}; + +class TORCH_API Or : public BitwiseOpNode { + public: + Or(ExprPtr lhs, ExprPtr rhs) + : BitwiseOpNode(std::move(lhs), std::move(rhs), IRNodeType::kOr) {} +}; + +class TORCH_API Xor : public BitwiseOpNode { + public: + Xor(ExprPtr lhs, ExprPtr rhs) + : BitwiseOpNode(std::move(lhs), std::move(rhs), IRNodeType::kXor) {} +}; + +class TORCH_API Lshift : public BitwiseOpNode { + public: + Lshift(ExprPtr lhs, ExprPtr rhs) + : BitwiseOpNode(std::move(lhs), std::move(rhs), IRNodeType::kLshift) {} +}; + +class TORCH_API Rshift : public BitwiseOpNode { + public: + Rshift(ExprPtr lhs, ExprPtr rhs) + : BitwiseOpNode(std::move(lhs), std::move(rhs), IRNodeType::kRshift) {} +}; + +// TODO: add TORCH_API +// Currently adding it results in a compilation error on Windows +class Max : public BinaryOpNode { + private: + bool propagate_nans_; + + public: + Max(ExprPtr lhs, ExprPtr rhs, bool propagate_nans) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kMax), + propagate_nans_(propagate_nans) {} + + bool propagate_nans() const { + return propagate_nans_; + } + + static ExprHandle make(const ExprHandle& lhs, const ExprHandle& rhs) = delete; + static ExprHandle make( + const ExprHandle& lhs, + const ExprHandle& rhs, + bool propagate_nans) { + return ExprHandle(alloc(lhs.node(), rhs.node(), propagate_nans)); + } +}; + +// TODO: add TORCH_API +// Currently adding it results in a compilation error on Windows +class Min : public BinaryOpNode { + private: + bool propagate_nans_; + + public: + Min(ExprPtr lhs, ExprPtr rhs, bool propagate_nans) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kMin), + propagate_nans_(propagate_nans) {} + + bool propagate_nans() const { + return propagate_nans_; + } + + static ExprHandle make(const ExprHandle& lhs, const ExprHandle& rhs) = delete; + static ExprHandle make( + const ExprHandle& lhs, + const ExprHandle& rhs, + bool propagate_nans) { + return ExprHandle(alloc(lhs.node(), rhs.node(), propagate_nans)); + } +}; + +// Encode typed immediate values e.g. IntImm, FloatImm. +#define IMM_DECLARE(Type, Name) \ + class TORCH_API Name##Imm : public ExprNode { \ + public: \ + Name##Imm(Type value) \ + : ExprNodeBase(k##Name, kPrimitive), value_(value) {} \ + bool isConstant() const override { \ + return true; \ + } \ + Type value() const { \ + return value_; \ + } \ + static ExprHandle make(Type value) { \ + return ExprHandle(alloc(value)); \ + } \ + \ + private: \ + Type value_; \ + }; +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_DECLARE) +#undef IMM_DECLARE + +// Get immediate by ScalarType. +template +ExprPtr getImmediateByType(ScalarType immType, T initialVal) { + switch (immType) { +#define TYPE_CASE(Type, Name) \ + case ScalarType::Name: \ + return alloc(Type(initialVal)); + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TYPE_CASE) +#undef TYPE_CASE + default: + throw unsupported_dtype(); + } + return nullptr; +} + +template +ExprPtr getImmediateByType(Dtype dtype, T initialVal) { + return getImmediateByType(dtype.scalar_type(), initialVal); +} + +template +ExprPtr immLike(const ExprPtr& e, T v) { + return getImmediateByType(e->dtype(), v); +} + +template +ExprPtr immLike(const ExprHandle& e, T v) { + return immLike(e.node(), v); +} + +inline std::optional intValue(const ExprPtr& e) { +#define TYPE_CASE(Type, Name) \ + if (auto v = to(e)) { \ + return v->value(); \ + } + AT_FORALL_INT_TYPES(TYPE_CASE); +#undef TYPE_CASE + return std::nullopt; +} + +inline std::optional intValue(const ExprHandle& e) { + return intValue(e.node()); +} + +template +T immediateAs(const ExprPtr& e) { +#define TYPE_CASE(Type, Name) \ + if (Name##ImmPtr imm = to(e)) { \ + return imm->value(); \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TYPE_CASE) +#undef TYPE_CASE + throw unsupported_dtype(); + return 0; +} + +template +T immediateAs(const ExprHandle& e) { + return immediateAs(e.node()); +} + +template +bool immediateEquals(const ExprPtr& e, T val) { +#define TYPE_CASE(Type, Name) \ + if (Name##ImmPtr imm = to(e)) { \ + return imm->value() == val; \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TYPE_CASE) +#undef TYPE_CASE + throw unsupported_dtype(); + return false; +} + +TORCH_API bool immediateIsNegative(const ExprPtr& e); + +TORCH_API bool immediateIsPositive(const ExprPtr& e); + +TORCH_API bool immediateIsZero(const ExprPtr& e); + +// Represents a ramp vector node: +// [base, base + 1 * stride, ... , base + (lanes - 1) * stride] +class TORCH_API Ramp : public ExprNode { + public: + ExprPtr base() const { + return base_; + } + ExprPtr stride() const { + return stride_; + } + + void set_base(ExprPtr base) { + base_ = std::move(base); + } + + void set_stride(ExprPtr stride) { + stride_ = std::move(stride); + } + + static ExprHandle make( + const ExprHandle& base, + const ExprHandle& stride, + int64_t lanes) { + if (stride.dtype() != base.dtype()) { + throw malformed_input("Bad stride in Ramp"); + } + return ExprHandle(alloc(base.node(), stride.node(), lanes)); + } + int64_t lanes() const { + return lanes_; + } + + Ramp(ExprPtr base, ExprPtr stride, int64_t lanes) + : ExprNodeBase(Dtype(base->dtype(), lanes)), + base_(std::move(base)), + stride_(std::move(stride)), + lanes_(lanes) {} + + private: + ExprPtr base_; + ExprPtr stride_; + int64_t lanes_; +}; + +class TORCH_API Load : public ExprNode { + public: + VarPtr base_handle() const { + return buf_->base_handle(); + } + std::vector indices() const { + return indices_; + } + ExprPtr flat_index() const { + TORCH_CHECK(indices_.size() == 1, "Indices haven't been flattened."); + return indices_[0]; + } + BufPtr buf() const { + return buf_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + void set_indices(std::vector indices) { + indices_ = std::move(indices); + } + + static ExprHandle make( + Dtype dtype, + const BufHandle& buf, + const std::vector& indices); + static ExprHandle make( + const BufHandle& buf, + const std::vector& indices); + + Load(Dtype dtype, BufPtr base_handle, std::vector indices); + Load(const BufPtr& base_handle, const std::vector& indices); + + private: + BufPtr buf_; + std::vector indices_; +}; + +class TORCH_API Broadcast : public ExprNode { + public: + ExprPtr value() const { + return value_; + } + + void set_value(ExprPtr value) { + value_ = std::move(value); + } + + int64_t lanes() const { + return lanes_; + } + static ExprHandle make(const ExprHandle& value, int64_t lanes) { + return ExprHandle(alloc(value.node(), lanes)); + } + Broadcast(ExprPtr value, int64_t lanes) + : ExprNodeBase(Dtype(value->dtype(), lanes)), + value_(std::move(value)), + lanes_(lanes) {} + + private: + ExprPtr value_; + int64_t lanes_; +}; + +class TORCH_API IfThenElse : public ExprNode { + public: + ExprPtr condition() const { + return condition_; + } + + // Lazily evaluated only if condition is true + ExprPtr true_value() const { + return true_; + } + + // Lazily evaluated only if condition is false + ExprPtr false_value() const { + return false_; + } + + void set_condition(ExprPtr condition) { + condition_ = std::move(condition); + } + + void set_true_value(ExprPtr true_value) { + true_ = std::move(true_value); + } + + void set_false_value(ExprPtr false_value) { + false_ = std::move(false_value); + } + + static ExprHandle make( + const ExprHandle& c, + const ExprHandle& t, + const ExprHandle& f) { + if (!c.dtype().is_integral()) { + throw unsupported_dtype(); + } + if (c.dtype().lanes() != 1) { + throw unsupported_dtype(); + } + if (t.dtype() != f.dtype()) { + throw malformed_input("Bad dtype in IfThenElse"); + } + return ExprHandle(alloc(c.node(), t.node(), f.node())); + } + + IfThenElse(ExprPtr c, ExprPtr t, ExprPtr f) + : ExprNodeBase(t->dtype()), + condition_(std::move(c)), + true_(std::move(t)), + false_(std::move(f)) {} + + private: + ExprPtr condition_; + ExprPtr true_; + ExprPtr false_; +}; + +class TORCH_API CompareSelect : public ExprNode { + public: + CompareSelectOperation compare_select_op() const { + return compare_op_; + } + ExprPtr lhs() const { + return this->lhs_; + } + ExprPtr rhs() const { + return this->rhs_; + } + ExprPtr ret_val1() const { + return this->ret_val1_; + } + ExprPtr ret_val2() const { + return this->ret_val2_; + } + + void set_lhs(ExprPtr lhs) { + lhs_ = std::move(lhs); + } + + void set_rhs(ExprPtr rhs) { + rhs_ = std::move(rhs); + } + + void set_ret_val1(ExprPtr ret_val1) { + ret_val1_ = std::move(ret_val1); + } + + void set_ret_val2(ExprPtr ret_val2) { + ret_val2_ = std::move(ret_val2); + } + + CompareSelectBias bias() const { + return bias_; + } + + static ExprHandle make( + const ExprHandle& lhs, + const ExprHandle& rhs, + CompareSelectOperation cmp_op, + CompareSelectBias bias = kUnbiased) { + if (lhs.dtype() != rhs.dtype()) { + throw malformed_input("bad dtype in CompareSelect"); + } + return ExprHandle(alloc( + lhs.node(), + rhs.node(), + IntImm::make(1).node(), + IntImm::make(0).node(), + cmp_op, + bias)); + } + + static ExprHandle make( + const ExprHandle& lhs, + const ExprHandle& rhs, + const ExprHandle& ret_val1, + const ExprHandle& ret_val2, + CompareSelectOperation cmp_op, + CompareSelectBias bias = kUnbiased) { + if (lhs.dtype() != rhs.dtype() || ret_val1.dtype() != ret_val2.dtype()) { + throw malformed_input("bad dtype in CompareSelect"); + } + return ExprHandle(alloc( + lhs.node(), + rhs.node(), + ret_val1.node(), + ret_val2.node(), + cmp_op, + bias)); + } + + CompareSelect( + ExprPtr lhs, + ExprPtr rhs, + ExprPtr ret_val1, + ExprPtr ret_val2, + CompareSelectOperation cmp_op, + CompareSelectBias bias = kUnbiased) + : ExprNodeBase(ret_val1->dtype()), + lhs_(std::move(lhs)), + rhs_(std::move(rhs)), + ret_val1_(std::move(ret_val1)), + ret_val2_(std::move(ret_val2)), + compare_op_(cmp_op), + bias_(bias) {} + + CompareSelect( + ExprPtr lhs, + ExprPtr rhs, + CompareSelectOperation cmp_op, + CompareSelectBias bias = kUnbiased) + : ExprNodeBase(kInt), + lhs_(std::move(lhs)), + rhs_(std::move(rhs)), + ret_val1_(alloc(1)), + ret_val2_(alloc(0)), + compare_op_(cmp_op), + bias_(bias) {} + + private: + ExprPtr lhs_; + ExprPtr rhs_; + ExprPtr ret_val1_; + ExprPtr ret_val2_; + CompareSelectOperation compare_op_; + CompareSelectBias bias_; +}; + +enum IntrinsicsOp { + kSin, + kCos, + kTan, + kAsin, + kAcos, + kAtan, + kAtan2, + kSinh, + kCosh, + kTanh, + kSigmoid, + kExp, + kExpm1, + kAbs, + kLog, + kLog2, + kLog10, + kLog1p, + kErf, + kErfc, + kSqrt, + kRsqrt, + kPow, + kCeil, + kFloor, + kRound, + kTrunc, + kFmod, + kRemainder, + kLgamma, + kFrac, + kIsNan, + kRand, // We need more discussions on this. Should we consider stateful? + kMaxIntrinsicsOp, +}; + +class TORCH_API Intrinsics : public ExprNode { + public: + static ExprHandle make(IntrinsicsOp op_type, const ExprHandle& v1) { + return ExprHandle(alloc(op_type, v1.node())); + } + + static ExprHandle make( + IntrinsicsOp op_type, + const ExprHandle& v1, + const ExprHandle& v2) { + return ExprHandle(alloc(op_type, v1.node(), v2.node())); + } + + static ExprHandle make( + IntrinsicsOp op_type, + const std::vector& params) { + std::vector params_nodes(params.size()); + for (size_t i = 0; i < params.size(); i++) { + params_nodes[i] = params[i].node(); + } + return ExprHandle(alloc(op_type, params_nodes)); + } + + static ExprHandle make(IntrinsicsOp op_type, Dtype dtype) { + return ExprHandle(alloc(op_type, dtype)); + } + + IntrinsicsOp op_type() const { + return op_type_; + } + + std::string func_name() const { + switch (op_type()) { + case kSin: + return "sin"; + case kCos: + return "cos"; + case kTan: + return "tan"; + case kAsin: + return "asin"; + case kAcos: + return "acos"; + case kAtan: + return "atan"; + case kAtan2: + return "atan2"; + case kSinh: + return "sinh"; + case kCosh: + return "cosh"; + case kTanh: + return "tanh"; + case kSigmoid: + return "sigmoid"; + case kExp: + return "exp"; + case kAbs: + return "abs"; + case kLog: + return "log"; + case kLog2: + return "log2"; + case kLog10: + return "log10"; + case kLog1p: + return "log1p"; + case kErf: + return "erf"; + case kSqrt: + return "sqrt"; + case kRsqrt: + return "rsqrt"; + case kPow: + return "pow"; + case kCeil: + return "ceil"; + case kFloor: + return "floor"; + case kRound: + return "round"; + case kTrunc: + return "trunc"; + case kRand: + return "rand"; + case kFmod: + return "fmod"; + case kRemainder: + return "remainder"; + case kLgamma: + return "lgamma"; + case kExpm1: + return "expm1"; + case kErfc: + return "erfc"; + case kFrac: + return "frac"; + case kIsNan: + return "isnan"; + default: + throw std::runtime_error( + "invalid op_type: " + std::to_string(op_type())); + } + } + + Intrinsics(IntrinsicsOp op_type, Dtype dtype) + : ExprNodeBase(IntrinsicsDtype(op_type, dtype)), + params_({}), + op_type_(op_type) { + if (OpArgCount(op_type) != 0) { + throw malformed_input("bad arg count in Intrinsics"); + } + } + + Intrinsics(IntrinsicsOp op_type, ExprPtr v1) + : ExprNodeBase(IntrinsicsDtype(op_type, v1->dtype())), + params_({std::move(v1)}), + op_type_(op_type) { + if (OpArgCount(op_type) != 1) { + throw malformed_input("bad arg count in Intrinsics"); + } + } + + Intrinsics(IntrinsicsOp op_type, ExprPtr v1, ExprPtr v2) + : ExprNodeBase(IntrinsicsDtype(op_type, v1->dtype(), v2->dtype())), + params_({std::move(v1), std::move(v2)}), + op_type_(op_type) { + if (OpArgCount(op_type) != 2) { + throw malformed_input("bad arg count in Intrinsics"); + } + } + + Intrinsics(IntrinsicsOp op_type, const std::vector& params) + : ExprNodeBase(IntrinsicsDtype(op_type, params)), + params_(params), + op_type_(op_type) { + if (OpArgCount(op_type) != nparams()) { + throw malformed_input("bad arg count in Intrinsics"); + } + } + + Intrinsics(IntrinsicsOp op_type, Dtype dtype, std::vector params) + : ExprNodeBase(IntrinsicsDtype(op_type, dtype)), + params_(std::move(params)), + op_type_(op_type) { + if (OpArgCount(op_type) != nparams()) { + throw malformed_input("bad arg count in Intrinsics"); + } + } + + bool isPure() const { + return op_type_ != kRand; + } + + size_t nparams() const { + return params_.size(); + } + + ExprPtr param(size_t index) const { + return params_[index]; + } + const std::vector& params() const { + return params_; + } + + void set_params(std::vector params) { + params_ = std::move(params); + } + + static size_t OpArgCount(IntrinsicsOp op_type); + + private: + static Dtype IntrinsicsDtype(IntrinsicsOp op_type, Dtype dt1); + static Dtype IntrinsicsDtype(IntrinsicsOp op_type, Dtype dt1, Dtype dt2); + static Dtype IntrinsicsDtype( + IntrinsicsOp op_type, + const std::vector& params); + + std::vector params_; + IntrinsicsOp op_type_; +}; + +TORCH_API std::vector ExprHandleVectorToExprVector( + const std::vector&); +TORCH_API std::vector ExprVectorToExprHandleVector( + const std::vector&); +TORCH_API std::vector VarHandleVectorToVarVector( + const std::vector&); +TORCH_API std::vector VarVectorToVarHandleVector( + const std::vector&); +TORCH_API ExprPtr flatten_index( + const std::vector& dims, + const std::vector& indices, + const std::vector& strides); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_cloner.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_cloner.h new file mode 100644 index 0000000000000000000000000000000000000000..11a407dc715ce4861df6ee491c4aee84d4424320 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_cloner.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +class TORCH_API IRCloner : public IRMutator { + public: + ~IRCloner() override = default; + ExprPtr mutate(const AddPtr& v) override; + ExprPtr mutate(const SubPtr& v) override; + ExprPtr mutate(const MulPtr& v) override; + ExprPtr mutate(const DivPtr& v) override; + ExprPtr mutate(const ModPtr& v) override; + ExprPtr mutate(const MaxPtr& v) override; + ExprPtr mutate(const MinPtr& v) override; + ExprPtr mutate(const AndPtr& v) override; + ExprPtr mutate(const OrPtr& v) override; + ExprPtr mutate(const XorPtr& v) override; + ExprPtr mutate(const LshiftPtr& v) override; + ExprPtr mutate(const RshiftPtr& v) override; + ExprPtr mutate(const CompareSelectPtr& v) override; +#define IMM_MUTATE_DECLARE(Type, Name) \ + ExprPtr mutate(const Name##ImmPtr& v) override; + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_MUTATE_DECLARE) +#undef IMM_MUTATE_DECLARE + ExprPtr mutate(const CastPtr& v) override; + ExprPtr mutate(const BitCastPtr& v) override; + ExprPtr mutate(const VarPtr& v) override; + ExprPtr mutate(const BufPtr& v) override; + ExprPtr mutate(const RampPtr& v) override; + ExprPtr mutate(const LoadPtr& v) override; + ExprPtr mutate(const BroadcastPtr& v) override; + ExprPtr mutate(const IfThenElsePtr& v) override; + ExprPtr mutate(const IntrinsicsPtr& v) override; + + ExprPtr mutate(const TermPtr& v) override; + ExprPtr mutate(const PolynomialPtr& v) override; + ExprPtr mutate(const RoundOffPtr& v) override; + ExprPtr mutate(const MaxTermPtr& v) override; + ExprPtr mutate(const MinTermPtr& v) override; + + ExprPtr mutate(const ReduceOpPtr& v) override; + + StmtPtr mutate(const ForPtr& v) override; + StmtPtr mutate(const BlockPtr& v) override; + StmtPtr mutate(const StorePtr& v) override; + StmtPtr mutate(const AtomicAddPtr& v) override; + StmtPtr mutate(const SyncThreadsPtr& v) override; + StmtPtr mutate(const ExternalCallPtr& v) override; + StmtPtr mutate(const ExternalCallWithAllocPtr& v) override; + + StmtPtr mutate(const AllocatePtr& v) override; + StmtPtr mutate(const FreePtr& v) override; + StmtPtr mutate(const LetPtr& v) override; + StmtPtr mutate(const CondPtr& v) override; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_mutator.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_mutator.h new file mode 100644 index 0000000000000000000000000000000000000000..dc3c25f5ab7c36dd0abeb21494799751779abe86 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_mutator.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include + +namespace torch::jit::tensorexpr { + +class TORCH_API IRMutator { + public: + virtual ~IRMutator() = default; + virtual ExprPtr mutate(const AddPtr& v); + virtual ExprPtr mutate(const SubPtr& v); + virtual ExprPtr mutate(const MulPtr& v); + virtual ExprPtr mutate(const DivPtr& v); + virtual ExprPtr mutate(const ModPtr& v); + virtual ExprPtr mutate(const MaxPtr& v); + virtual ExprPtr mutate(const MinPtr& v); + virtual ExprPtr mutate(const AndPtr& v); + virtual ExprPtr mutate(const OrPtr& v); + virtual ExprPtr mutate(const XorPtr& v); + virtual ExprPtr mutate(const LshiftPtr& v); + virtual ExprPtr mutate(const RshiftPtr& v); + virtual ExprPtr mutate(const CompareSelectPtr& v); +#define IMM_MUTATE_DECLARE(Type, Name) \ + virtual ExprPtr mutate(const Name##ImmPtr& v); + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_MUTATE_DECLARE) +#undef IMM_MUTATE_DECLARE + virtual ExprPtr mutate(const CastPtr& v); + virtual ExprPtr mutate(const BitCastPtr& v); + virtual ExprPtr mutate(const VarPtr& v); + virtual ExprPtr mutate(const BufPtr& v); + virtual ExprPtr mutate(const RampPtr& v); + virtual ExprPtr mutate(const LoadPtr& v); + virtual ExprPtr mutate(const BroadcastPtr& v); + virtual ExprPtr mutate(const IfThenElsePtr& v); + virtual ExprPtr mutate(const IntrinsicsPtr& v); + + virtual ExprPtr mutate(const TermPtr& v); + virtual ExprPtr mutate(const PolynomialPtr& v); + virtual ExprPtr mutate(const RoundOffPtr& v); + virtual ExprPtr mutate(const MaxTermPtr& v); + virtual ExprPtr mutate(const MinTermPtr& v); + + virtual ExprPtr mutate(const ReduceOpPtr& v); + + virtual StmtPtr mutate(const ForPtr& v); + virtual StmtPtr mutate(const BlockPtr& v); + virtual StmtPtr mutate(const StorePtr& v); + virtual StmtPtr mutate(const AtomicAddPtr& v); + virtual StmtPtr mutate(const SyncThreadsPtr& v); + virtual StmtPtr mutate(const ExternalCallPtr& v); + virtual StmtPtr mutate(const ExternalCallWithAllocPtr& v); + + virtual StmtPtr mutate(const AllocatePtr& v); + virtual StmtPtr mutate(const FreePtr& v); + virtual StmtPtr mutate(const FreeExtPtr& v); + virtual StmtPtr mutate(const PlacementAllocatePtr& v); + virtual StmtPtr mutate(const LetPtr& v); + virtual StmtPtr mutate(const CondPtr& v); +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_printer.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_printer.h new file mode 100644 index 0000000000000000000000000000000000000000..1909a40283c714549c7cc4bbc277a95d3a1b7113 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_printer.h @@ -0,0 +1,130 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +class Tensor; + +class TORCH_API IRPrinter : public IRVisitor { + public: + explicit IRPrinter(std::ostream& os) : printer_os_(this, os) {} + + void print(ExprHandle); + void print(Expr&); + void print(Stmt&); + void visit(const AddPtr& v) override; + void visit(const SubPtr& v) override; + void visit(const MulPtr& v) override; + void visit(const DivPtr& v) override; + void visit(const ModPtr& v) override; + void visit(const MaxPtr& v) override; + void visit(const MinPtr& v) override; + void visit(const AndPtr& v) override; + void visit(const OrPtr& v) override; + void visit(const XorPtr& v) override; + void visit(const LshiftPtr& v) override; + void visit(const RshiftPtr& v) override; + void visit(const CompareSelectPtr& v) override; +#define IMM_PRINT_VISIT(Type, Name) void visit(const Name##ImmPtr& v) override; + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_PRINT_VISIT) +#undef IMM_PRINT_VISIT + void visit(const CastPtr& v) override; + void visit(const BitCastPtr& v) override; + void visit(const VarPtr& v) override; + void visit(const BufPtr& v) override; + void visit(const RampPtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const BroadcastPtr& v) override; + void visit(const IfThenElsePtr& v) override; + void visit(const IntrinsicsPtr& v) override; + void visit(const TermPtr& v) override; + void visit(const PolynomialPtr& v) override; + void visit(const RoundOffPtr& v) override; + void visit(const MaxTermPtr& v) override; + void visit(const MinTermPtr& v) override; + void visit(const ReduceOpPtr& v) override; + + void visit(const AtomicAddPtr& v) override; + void visit(const SyncThreadsPtr& v) override; + void visit(const ExternalCallPtr& v) override; + void visit(const ExternalCallWithAllocPtr& v) override; + void visit(const StorePtr& v) override; + void visit(const ForPtr& v) override; + void visit(const CondPtr& v) override; + void visit(const BlockPtr& v) override; + void visit(const AllocatePtr& v) override; + void visit(const FreePtr& v) override; + void visit(const FreeExtPtr& v) override; + void visit(const PlacementAllocatePtr& v) override; + void visit(const LetPtr& v) override; + + // A child class may have a difference rule for generating dtype + // string, e.g. CUDA needs int64_t to be generated as long long. + virtual std::string dtypeToCppString(const Dtype& dtype); + + std::ostream& os() { + return printer_os_; + } + + class PrinterStream : public std::ostream { + public: + PrinterStream(IRPrinter* printer, std::ostream& os) + : std::ostream(os.rdbuf()), printer_(printer) { + initialize_imbue(); + } + + void initialize_imbue(); + + IRPrinter* printer() { + return printer_; + } + + private: + IRPrinter* printer_ = nullptr; + }; + + protected: + std::string to_string(CompareSelectOperation op); + + UniqueNameManager* name_manager() { + return &name_manager_; + } + void emitIndent(); + + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + int indent_ = 0; + + private: + PrinterStream printer_os_; + UniqueNameManager name_manager_; +}; + +TORCH_API std::ostream& operator<<(std::ostream& stream, const Expr&); +TORCH_API std::ostream& operator<<(std::ostream& stream, const ExprHandle&); +TORCH_API std::ostream& operator<<(std::ostream& stream, const Stmt&); +TORCH_API std::ostream& operator<<(std::ostream& stream, const Tensor&); + +TORCH_API void print(const ExprPtr& expr); +TORCH_API void print(const StmtPtr& stmt); +TORCH_API void print(const Tensor& t); + +} // namespace torch::jit::tensorexpr + +namespace std { + +using torch::jit::tensorexpr::Expr; +using torch::jit::tensorexpr::ExprPtr; +using torch::jit::tensorexpr::Stmt; +using torch::jit::tensorexpr::StmtPtr; +using torch::jit::tensorexpr::Tensor; + +TORCH_API std::string to_string(const ExprPtr& expr); +TORCH_API std::string to_string(const StmtPtr& stmt); +TORCH_API std::string to_string(const Tensor& t); +} // namespace std diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_simplifier.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_simplifier.h new file mode 100644 index 0000000000000000000000000000000000000000..d9fd2b61c97b185861059622c5e9903286b9cf92 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_simplifier.h @@ -0,0 +1,546 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* IR Simplification + * + * Simplifies expressions in two stages: + * 1. Recursively traverse the map combining similar operations into Terms + * (interacted via Multiplication) and Polynomials (interacted via Addition). We + * reorder the components of each Term or Polynomial into a consistent order to + * allow combination or cancelling of like terms. + * 2. Once the format of the tree is minimal, expand each Term into a sequence + * of Muls, and each Polynomial into a sequence of Ads. + */ + +namespace torch::jit::tensorexpr { + +// A bunch of helpers for determine the Dtype of the output of a multi argument +// Term or Polynomial. +template +Dtype promoteTypesVec(const ExprPtr& s, const std::vector& v) { + Dtype t = s->dtype(); + bool first = true; + + for (const auto& e : v) { + if (first) { + t = Dtype(t.scalar_type(), e->dtype().lanes()); + first = false; + } + t = promoteTypes(t, e->dtype()); + } + return t; +} + +template +Dtype promoteTypesVec(const std::vector& v) { + if (v.empty()) { + throw malformed_input("empty list of types"); + } + + Dtype t = v[0]->dtype(); + for (const auto& e : v) { + t = promoteTypes(t, e->dtype()); + } + return t; +} + +template +Dtype promoteTypesMap( + const ExprPtr& s, + std::unordered_map& m) { + Dtype t = s->dtype(); + bool first = true; + for (auto& e : m) { + if (first) { + t = Dtype(t.scalar_type(), e.second->dtype().lanes()); + first = false; + } + t = promoteTypes(t, e.second->dtype()); + } + return t; +} + +template +Dtype promoteTypesVar(ExprType e) { + return e->dtype(); +} + +template +Dtype promoteTypesVar(ExprType e, Args... es) { + Dtype lhs = e->dtype(); + Dtype rhs = promoteTypesVar(es...); + if (e->isConstant()) { + lhs = Dtype(lhs.scalar_type(), rhs.lanes()); + } + + return promoteTypes(lhs, rhs); +} + +// Uses the evaluator to fold an Expression with constant terms. +// E.g. evaluateOp(Add(3, 4)) => 7. +// Expr v must not have any unbound Vars. +inline ExprPtr evaluateOp(const ExprPtr& v) { + ExprHandle handle(v); + ExprEval eval(handle); + + switch (v->dtype().scalar_type()) { +#define TYPE_CASE(Type, Name) \ + case ScalarType::Name: { \ + Type val = eval.value(); \ + return getImmediateByType(v->dtype().scalar_type(), val); \ + } + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TYPE_CASE) +#undef TYPE_CASE + default: + LOG(FATAL) << "Unsupported datatype: " << v->dtype(); + return nullptr; + } + return nullptr; +} + +// A Term represents a grouping of Exprs through multiplication. +// E.g. product(scalar, *variables). +class Term : public ExprNode { + public: + template + Term(HashProvider& hasher, ExprPtr s, Args... ts) + : ExprNodeBase(promoteTypesVar(s, ts...)), scalar_(s), hasher_(hasher) { + CHECK(s->isConstant()); + addComponent(ts...); + sort(); + } + + Term(HashProvider& hasher, ExprPtr s, std::vector v) + : ExprNodeBase(promoteTypesVec(s, v)), + variables_(std::move(v)), + scalar_(std::move(s)), + hasher_(hasher) { + sort(); + } + + // Convenience constructor from a map of hash -> var, used when merging Terms. + Term( + HashProvider& hasher, + const ExprPtr& s, + std::unordered_map varmap) + : ExprNodeBase(promoteTypesMap(s, varmap)), scalar_(s), hasher_(hasher) { + for (auto& p : varmap) { + addComponent(p.second); + } + sort(); + } + + ExprPtr scalar() const { + return scalar_; + } + const std::vector& variables() const { + return variables_; + } + HashProvider& hasher() const { + return hasher_; + } + + // Produce a hash of just the variable components of this term, to determine + // if it can be combined with another term. + SimplifierHashType hashVars() const; + + private: + std::vector variables_; + ExprPtr scalar_; + HashProvider& hasher_; + + void addComponent() {} + void addComponent(ExprPtr e) { + variables_.push_back(std::move(e)); + } + template + void addComponent(ExprPtr e, Es&&... es) { + addComponent(std::move(e)); + addComponent(std::forward(es)...); + } + + // Sort by hash to normalize order of components. + void sort(); +}; + +// Polynomial represents a grouping of Exprs by addition. +// E.g. sum(*variables, scalar). +// This would better be called Expression, but, naming conflict... +class Polynomial : public ExprNode { + public: + template + Polynomial(HashProvider& hasher, ExprPtr s, Args... ts) + : ExprNodeBase(promoteTypesVar(s, ts...)), scalar_(s), hasher_(hasher) { + CHECK(s->isConstant()); + addTerm(ts...); + sort(); + } + + Polynomial(HashProvider& hasher, const ExprPtr& s, std::vector v) + : ExprNodeBase(promoteTypesVec(s, v)), + variables_(std::move(v)), + scalar_(s), + hasher_(hasher) { + sort(); + } + + // Helper constructor for list of terms with no scalar component. + Polynomial(HashProvider& hasher, std::vector terms) + : ExprNodeBase(promoteTypesVec(terms)), + variables_(std::move(terms)), + scalar_(getImmediateByType(dtype(), 0)), + hasher_(hasher) { + sort(); + } + + // Convenience constructor for map of hash -> var, used when merging + // Polynomials. + Polynomial( + HashProvider& hasher, + const ExprPtr& s, + std::unordered_map varmap) + : ExprNodeBase(promoteTypesMap(s, varmap)), scalar_(s), hasher_(hasher) { + for (auto& p : varmap) { + addTerm(p.second); + } + sort(); + } + + ExprPtr scalar() const { + return scalar_; + } + const std::vector& variables() const { + return variables_; + } + HashProvider& hasher() const { + return hasher_; + } + + SimplifierHashType hashVars() const; + + private: + std::vector variables_; + ExprPtr scalar_; + HashProvider& hasher_; + + void addTerm(TermPtr t) { + variables_.push_back(std::move(t)); + } + template + void addTerm(TermPtr t, Ts&&... ts) { + addTerm(std::move(t)); + addTerm(std::forward(ts)...); + } + + // Sort by hash to normalize order of terms. + void sort(); +}; + +class RoundOff : public BinaryOpNode { + public: + RoundOff(ExprPtr lhs, ExprPtr rhs) + : BinaryOpNode(std::move(lhs), std::move(rhs), IRNodeType::kOther) {} +}; + +class MaxTerm : public ExprNode { + public: + template + MaxTerm(HashProvider& hasher, ExprPtr s, bool p, Args... ts) + : ExprNodeBase(s ? promoteTypesVar(s, ts...) : promoteTypesVar(ts...)), + scalar_(s), + hasher_(hasher), + propagate_nans_(p) { + addComponent(ts...); + uniquefy(); + } + + MaxTerm( + HashProvider& hasher, + const ExprPtr& s, + bool p, + std::vector v) + : ExprNodeBase(s ? promoteTypesVec(s, v) : promoteTypesVec(v)), + variables_(std::move(v)), + scalar_(s), + hasher_(hasher), + propagate_nans_(p) { + uniquefy(); + } + + bool propagate_nans() const { + return propagate_nans_; + } + + ExprPtr scalar() const { + return scalar_; + } + const std::vector& variables() const { + return variables_; + } + HashProvider& hasher() const { + return hasher_; + } + + private: + std::vector variables_; + ExprPtr scalar_; + HashProvider& hasher_; + bool propagate_nans_; + + void addComponent() {} + void addComponent(ExprPtr e) { + variables_.push_back(std::move(e)); + } + template + void addComponent(ExprPtr e, Es&&... es) { + addComponent(std::move(e)); + addComponent(std::forward(es)...); + } + + // Uniquefy the terms using their hash. + void uniquefy(); +}; + +class MinTerm : public ExprNode { + public: + template + MinTerm(HashProvider& hasher, ExprPtr s, bool p, Args... ts) + : ExprNodeBase(s ? promoteTypesVar(s, ts...) : promoteTypesVar(ts...)), + scalar_(s), + hasher_(hasher), + propagate_nans_(p) { + addComponent(ts...); + uniquefy(); + } + + MinTerm( + HashProvider& hasher, + const ExprPtr& s, + bool p, + std::vector v) + : ExprNodeBase(s ? promoteTypesVec(s, v) : promoteTypesVec(v)), + variables_(std::move(v)), + scalar_(s), + hasher_(hasher), + propagate_nans_(p) { + uniquefy(); + } + + bool propagate_nans() const { + return propagate_nans_; + } + + ExprPtr scalar() const { + return scalar_; + } + const std::vector& variables() const { + return variables_; + } + HashProvider& hasher() const { + return hasher_; + } + + private: + std::vector variables_; + ExprPtr scalar_; + HashProvider& hasher_; + bool propagate_nans_; + + void addComponent() {} + void addComponent(ExprPtr e) { + variables_.push_back(std::move(e)); + } + template + void addComponent(ExprPtr e, Es&&... es) { + addComponent(std::move(e)); + addComponent(std::forward(es)...); + } + + // Uniquefy the terms using their hash. + void uniquefy(); +}; + +// Context-sensitive IR simplification +using VarBoundInfo = std::unordered_map; + +class TORCH_API SimplifierUnderContext : public IRMutator { + public: + ~SimplifierUnderContext() override = default; + // Add boundary info for index variables in for-loops + StmtPtr mutate(const ForPtr& v) override; + + ExprPtr mutate(const DivPtr& v) override; + ExprPtr mutate(const ModPtr& v) override; + ExprPtr mutate(const CompareSelectPtr& v) override; + ExprPtr mutate(const IfThenElsePtr& v) override; + + protected: + bool getLoopBoundInfo(const ExprPtr& expr, analysis::Bound* loop_bound_info); + + protected: + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + HashProvider hasher_; + VarBoundInfo var_bound_info_; +}; + +// Stmt simplification should occur in both modes. +class TORCH_API PolynomialBase : public IRMutator { + public: + ~PolynomialBase() override = default; + + StmtPtr mutate(const BlockPtr& v) override; + + StmtPtr mutate(const CondPtr& v) override; + + StmtPtr mutate(const ForPtr& v) override; + + // Trivially factorize terms by GCD of scalar components. + TermPtr factorizePolynomial(const PolynomialPtr& poly); + + HashProvider& hasher() { + return hasher_; + } + + protected: + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) + HashProvider hasher_; +}; + +// Simplify the IR by combining arithmetic expressions over common terms. +class TORCH_API PolynomialTransformer : public PolynomialBase { + public: + using PolynomialBase::mutate; + // Inserts term into the provided map, in the case of a hash collision + // combines the term with the existing and updates the map. + void addOrUpdateTerm( + std::unordered_map& varmap, + const TermPtr& term); + + // Add Polynomial expressions, combining Terms representing the same + // variables. + ExprPtr addPolynomials(const PolynomialPtr& lhs, const PolynomialPtr& rhs); + + // Insert a new Term into the provided polynomial. If the new term has + // common variables to an existing term it is combined. + ExprPtr insertTerm(const PolynomialPtr& poly, const TermPtr& term); + + // Merge and simplify addition. + ExprPtr mutate(const AddPtr& v) override; + + // Subtract one term from another, cancelling if necessary. + ExprPtr subTerms(const TermPtr& lhs, TermPtr rhs, bool negated); + + // Subtract the RHS Polynomial from the LHS Polynomial, cancelling out where + // possible. + ExprPtr subPolynomials(const PolynomialPtr& lhs, const PolynomialPtr& rhs); + + // Merge and simplify subtraction. + ExprPtr mutate(const SubPtr& v) override; + + // Multiply two terms together, usually creating a new term with the variable + // lists concatenated. + TermPtr mulTerms(const TermPtr& lhs, const TermPtr& rhs); + + // Multiply a Polynomial by a Term. + ExprPtr polyByTerm(const PolynomialPtr& poly, const TermPtr& term); + + // Match a rounding pattern and create a RoundOff if found. + ExprPtr isRoundOff(const ExprPtr& lhs, const ExprPtr& rhs); + + // Inserts a new component into a term, simplifying if possible. + ExprPtr insertIntoTerm(const TermPtr& term, const ExprPtr& expr); + + // Merge and simplify multiplication. + ExprPtr mutate(const MulPtr& v) override; + + ExprPtr mutate(const DivPtr& v) override; + + ExprPtr mutate(const ModPtr& v) override; + + ExprPtr mutate(const AndPtr& v) override; + + ExprPtr mutate(const XorPtr& v) override; + + ExprPtr mutate(const LshiftPtr& v) override; + + ExprPtr mutate(const RshiftPtr& v) override; + + ExprPtr mutate(const MaxPtr& v) override; + + ExprPtr mutate(const MinPtr& v) override; + + ExprPtr mutate(const CompareSelectPtr& v) override; + + ExprPtr mutate(const IntrinsicsPtr& v) override; + + ExprPtr mutate(const CastPtr& v) override; + + ExprPtr mutate(const IfThenElsePtr& v) override; + + static ExprPtr simplify(ExprPtr e); + static ExprHandle simplify(const ExprHandle& e); + static StmtPtr simplify(StmtPtr e); +}; + +// Expands Terms and Polynomial expressions into primitive operations. +// Does some simple factorization and reordering. +class TORCH_API TermExpander : public PolynomialBase { + PolynomialTransformer* simplifier_; + std::set eliminated_allocations_; + + public: + using PolynomialBase::mutate; + TermExpander(PolynomialTransformer* simplifier) : simplifier_(simplifier) {} + bool check_safe() { + return eliminated_allocations_.empty(); + } + + // Expand Terms out to a series of Muls. + ExprPtr mutate(const TermPtr& v) override; + + // Expand Polynomials out to a series of Adds. + ExprPtr mutate(const PolynomialPtr& v) override; + + // Expand MaxTerms to a series of Max ops. + ExprPtr mutate(const MaxTermPtr& v) override; + + // Expand MinTerms to a series of Min ops. + ExprPtr mutate(const MinTermPtr& v) override; + + // Expand RoundOff to it's component: Mul(Div(lhs, rhs), rhs). + ExprPtr mutate(const RoundOffPtr& v) override; + + // Eliminate zero length allocations. + StmtPtr mutate(const AllocatePtr& v) override; + StmtPtr mutate(const FreePtr& v) override; + + // Override to enable condition fusing. + BlockPtr fuseConditions(BlockPtr v); + StmtPtr fuseSyncThreads(BlockPtr block); + StmtPtr mutate(const BlockPtr& v) override; +}; + +class TORCH_API IRSimplifier { + public: + static StmtPtr simplify(StmtPtr s); + static ExprPtr simplify(ExprPtr e); + static ExprHandle simplify(const ExprHandle& e) { + return ExprHandle(simplify(e.node())); + } +}; + +// Flattens the buf and performs the simplifier on the flattened dims. +ExprPtr buf_flat_size(const BufPtr& v); +// Returns true if expressions A and B can be simplified to an equal expression. +TORCH_API bool exprEquals(const ExprPtr& A, const ExprPtr& B); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_verifier.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_verifier.h new file mode 100644 index 0000000000000000000000000000000000000000..e8e887ac80aedbfd9f07ca840dfeee35362c9649 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_verifier.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +namespace torch::jit::tensorexpr { + +class Expr; +class ExprHandle; +class Mod; +class And; +class Or; +class Xor; +class Lshift; +class Rshift; +class CompareSelect; +class Ramp; +class Load; +class IfThenElse; +class Intrinsics; + +class Stmt; +class ExternalCall; +class Store; +class For; +class Block; + +class TORCH_API IRVerifier : public IRVisitor { + public: + IRVerifier() = default; + + void visit(const ModPtr& v) override; + void visit(const AndPtr& v) override; + void visit(const OrPtr& v) override; + void visit(const XorPtr& v) override; + void visit(const LshiftPtr& v) override; + void visit(const RshiftPtr& v) override; + void visit(const CompareSelectPtr& v) override; + void visit(const RampPtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const IfThenElsePtr& v) override; + void visit(const IntrinsicsPtr& v) override; + + void visit(const ExternalCallPtr& v) override; + void visit(const StorePtr& v) override; + void visit(const ForPtr& v) override; + void visit(const BlockPtr& v) override; +}; + +TORCH_API void verify(const StmtPtr&); +TORCH_API void verify(const ExprPtr&); +TORCH_API void verify(const ExprHandle&); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_visitor.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_visitor.h new file mode 100644 index 0000000000000000000000000000000000000000..d6e87b6cc8413dd09e395fa2ce10d1178d026f64 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/ir_visitor.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include + +namespace torch::jit::tensorexpr { + +class TORCH_API IRVisitor { + public: + virtual ~IRVisitor() = default; + virtual void visit(const AddPtr& v); + virtual void visit(const SubPtr& v); + virtual void visit(const MulPtr& v); + virtual void visit(const DivPtr& v); + virtual void visit(const ModPtr& v); + virtual void visit(const MaxPtr& v); + virtual void visit(const MinPtr& v); + virtual void visit(const AndPtr& v); + virtual void visit(const OrPtr& v); + virtual void visit(const XorPtr& v); + virtual void visit(const LshiftPtr& v); + virtual void visit(const RshiftPtr& v); + virtual void visit(const CompareSelectPtr& v); + +#define IMM_PRINT_VISIT(Type, Name) virtual void visit(const Name##ImmPtr& v); + + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, IMM_PRINT_VISIT) +#undef IMM_PRINT_VISIT + + virtual void visit(const CastPtr& v); + virtual void visit(const BitCastPtr& v); + virtual void visit(const VarPtr& v); + virtual void visit(const BufPtr& v); + virtual void visit(const RampPtr& v); + virtual void visit(const LoadPtr& v); + virtual void visit(const ForPtr& v); + virtual void visit(const BlockPtr& v); + virtual void visit(const StorePtr& v); + virtual void visit(const BroadcastPtr& v); + virtual void visit(const IfThenElsePtr& v); + virtual void visit(const IntrinsicsPtr& v); + virtual void visit(const AllocatePtr& v); + virtual void visit(const FreePtr& v); + virtual void visit(const FreeExtPtr& v); + virtual void visit(const PlacementAllocatePtr& v); + virtual void visit(const LetPtr& v); + virtual void visit(const CondPtr& v); + virtual void visit(const TermPtr& v); + virtual void visit(const PolynomialPtr& v); + virtual void visit(const RoundOffPtr& v); + virtual void visit(const MaxTermPtr& v); + virtual void visit(const MinTermPtr& v); + virtual void visit(const ReduceOpPtr& v); + virtual void visit(const AtomicAddPtr& v); + virtual void visit(const SyncThreadsPtr& v); + virtual void visit(const ExternalCallPtr& v); + virtual void visit(const ExternalCallWithAllocPtr& v); +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/kernel.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..629b461c0785655e5572cfb68ef23909e614c863 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/kernel.h @@ -0,0 +1,378 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +struct SmallSizeTPairHash { + public: + std::size_t operator()(const std::pair& x) const { + // hashing input index and then dim index + return x.first * 128 + x.second; + } +}; + +// Returns true if the TE fuser supports this conv2d. +bool conv2dIsSupportedJit(const Node* node); +// Returns true if the TE fuser supports this conv2d with mkldnn prepacked conv. +bool mkldnnPrepackedConvIsSupportedJit(const Node* node); +// Returns true if the TE _convolution node is Conv2d. +bool isConv2d(const Node* node); +// Returns true if the TE fuser supports this matmul. +bool matmulIsSupported(const Node* node); +template +inline std::vector bufferSizes(const T& t) { + std::vector sizes; + for (size_t i = 0; i < t->ndim(); i++) { + sizes.push_back(*intValue(t->dim(i))); + } + return sizes; +} + +// Get the dimensions of a value. +std::vector valueShape(const ArgValue& v); + +// If v is a tensor, broadcast it to match the shape of axes, or return +// directly if v is a constant. +ExprHandle tensorOrConstant( + const ArgValue& v, + const std::vector& axes); + +int64_t normalizeAndCheckIndex(int64_t idx, int64_t list_size); + +ExprHandle broadcast(const BufHandle& b, const std::vector& axes); + +ExprHandle constant(const ArgValue& v); + +std::vector computeIndicesToBroadcast( + const std::vector& outputAxes, + const std::vector& inputSizes); + +inline std::string getArgValueName(const ArgValue& a) { + if (std::holds_alternative(a)) { + return "BufHandle"; + } else if (std::holds_alternative(a)) { + return "VarHandle"; + } else if (std::holds_alternative(a)) { + return "double"; + } else if (std::holds_alternative(a)) { + return "int64_t"; + } else if (std::holds_alternative(a)) { + return "bool"; + } else if (std::holds_alternative(a)) { + return "BufList"; + } else if (std::holds_alternative(a)) { + return "DoubleList"; + } else if (std::holds_alternative(a)) { + return "IntList"; + } else if (std::holds_alternative(a)) { + return "None"; + } else { + throw std::runtime_error("ArgValue type not handled in string conversion"); + } +} + +template +std::vector convertVecArgValue(const std::vector& v) { + std::vector res; + for (auto& x : v) { + auto val = std::get_if(&x); + if (val) { + res.push_back(*val); + } else { + throw std::runtime_error( + "vector type not homogeneous - found " + getArgValueName(x) + + ", expected " + getArgValueName(v[0])); + } + } + return res; +} + +class TORCH_API TensorExprKernel { + struct ConstantDescr { + BufPtr buf; + // Only one of ptr and node is used at a time + // 1) ptr for the constant tensors + // 2) node for the constant custom class objects + void* ptr = nullptr; + Node* node = nullptr; + }; + + public: + // Constructor Params: + // * subgraph + // - the graph that needs to be compiled. + // * kernel_func_name + // - the name that should be used for the generated kernel. + // * custom_lowerings + // - map that represents custom lowering definitions for a set of ops. + // * symbolic_shape_inputs + // - a list of symbolic graph inputs that represent the symbolic dims of + // the input tensors. + // * pre_alloc + // - a flag to control pre-allocation of buffers. + explicit TensorExprKernel( + const std::shared_ptr& subgraph, + std::string kernel_func_name, + std::unordered_map custom_lowerings = + {}, + std::vector symbolic_shape_inputs = {}, + bool pre_alloc = false, + std::unordered_map< + const torch::jit::Value*, + std::vector> symbolic_strides = {}); + + explicit TensorExprKernel( + const std::shared_ptr& subgraph, + std::unordered_map custom_lowerings = + {}, + std::vector symbolic_shape_inputs = {}, + bool pre_alloc = false, + std::unordered_map< + const torch::jit::Value*, + std::vector> symbolic_strides = {}) + : TensorExprKernel( + subgraph, + SubgraphUtils::generateNameForGraph(subgraph), + std::move(custom_lowerings), + std::move(symbolic_shape_inputs), + pre_alloc, + std::move(symbolic_strides)) {} + + void run(Stack& stack) const; + void runFast( + const std::vector& inputs, + const std::vector& outputs) const; + // Expected format of stack: + // ... + // i.e., output IValues must be below the input IValues in the stack. + void runWithAllocatedOutputs(Stack& stack) const; + + void fallback(Stack& stack) const { + InterpreterState(code_).run(stack); + } + void recompile(); + + StmtPtr getCodeGenStmt(); + + std::string getCodeText(const std::string& attr = "") { + return codegen_->getCodeText(attr); + } + + const std::shared_ptr graph() { + return graph_; + } + + const std::vector& getConstantDescriptors() const { + return constants_; + } + + const std::vector& getBufferArgs() const { + return bufferArgs_; + } + + const std::string& getKernelName() const { + return (codegen_ ? codegen_->kernel_func_name() : kernel_func_name_); + } + + const std::vector& getSymbolicShapeInputs() const { + return symbolic_shape_inputs_; + } + + private: + enum BackendType { + kUninitialized, + kSimpleIREval, + kLLVMCodeGen, + kCudaCodeGen, + kBlockCodeGen, + }; + + enum MemoryLayoutPolicy { + kContiguous, + kChannelsLastNdContiguous, + }; + + void compile(); + void genInputDebugNames(); + void runKernel(Stack& stack) const; + + std::vector sizesForValue(const torch::jit::Value* v); + + // These functions broadcast shape and also store a `hasBroadcast_` variable. + std::vector broadcastShapesMut( + const std::vector& a, + const std::vector& b); + std::vector broadcastShapesMut( + std::vector> shapes); + + ArgValue toArg(const torch::jit::Value* v) const; + ExprHandle constant(const torch::jit::Value* v); + + Tensor computeValue(const torch::jit::Value* v); + + void bindConstant(const torch::jit::Value* v); + + StmtPtr transformLoops(BackendType backendType, StmtPtr st); + + std::string getCodeGenName(BackendType backendType); + + void getStaticOutputSizesAndStrides( + const at::ArrayRef& inputs, + std::vector>* static_sizes, + std::vector>* static_strides) const; + + std::vector prepareRunArgs( + const at::ArrayRef& inputs, + std::vector& outputs) const; + BackendType inferBackendTypeFromDevice(at::Device device); + + Tensor bindInput(const torch::jit::Value* input); + BlockPtr bindAllInputs(); + + // Deduce the memory layout policy to be propagated within + // NNC fusion group. The memory layout policy could be `kContiguous` + // or `kChannelsLastNdContiguous`. + // `kContiguous`: Always convert the non-contiguous input tensors and + // internal buffers to contiguous. + // `kChannelsLastNdContiguous`: Always convert the input tensors and + // internal buffers to channels-last contiguous. + // Currently, the rule is simple. + // If all the input and out tensors of NNC fusion group are channels-last + // contiguous, the policy is `kChannelsLastNdContiguous`. Otherwise, it + // is always `kContiguous`. + void deduceMemoryLayoutPolicy(); + + Tensor convertSymbolicOutputToCorrectStrides(torch::jit::Value* v); + Tensor convertStaticShapeOutputToCorrectStrides(torch::jit::Value* v); + Tensor convertSymbolicOutputToCorrectStrides( + const std::vector& sizes, + const std::vector& sorted_stride_indices_descending, + const std::vector& strides, + BufPtr& buf); + + NNCLoweringFunction getCustomLoweringFor(c10::Symbol op) const; + std::unordered_map getCustomLowerings() + const { + return custom_lowerings_; + } + + // Allocate memory for intermediate buffers at compile time. + // Specifically, we pre-allocate memory for intermediate buffers with static + // size and manage these buffers in the way we manage JIT constant tensors: + // push the buf args into the stack so NNC IR can access them at runtime. + std::vector preAllocIntermediateBufs( + const std::vector& interm_bufs); + + struct UnpackedTensorOptions { + std::optional dtype; + std::optional layout; + std::optional device; + std::optional pinned_memory; + + UnpackedTensorOptions(const c10::TensorOptions& opts) + : dtype(c10::optTypeMetaToScalarType(opts.dtype_opt())), + layout(opts.layout_opt()), + device(opts.device_opt()), + pinned_memory(opts.pinned_memory_opt()) {} + }; + + ExprHandle getVarForShape(const c10::ShapeSymbol& ss); + std::vector computeInputTensorDims( + const torch::jit::Value* input); + ExprHandle getStrideArg(size_t tensor_input, size_t stride_index); + std::vector sizesFromSymbolicShape( + const c10::SymbolicShape& shape); + std::vector getInputStrides( + const torch::jit::Value* input, + const std::vector& inputTensorDims); + std::vector& getSymbolicStrideDesc( + const torch::jit::Value* value); + + // Apply the optimizations to the graph owned by the current fusion group, + // like concatenation optimization, post-op fusion, and some other graph-level + // optimizations. + void optimizeOwningGraph(); + + int64_t nInputs_ = 0; + int64_t nOutputs_ = 0; + std::vector bufferArgs_; + std::vector> tensorOutputSizes_; + std::vector> tensorOutputStrides_; + std::vector tensorOutputStrideDesc_; + std::vector isOutputScalar_; + std::vector tensorOutputTensorOptions_; + std::unordered_set bufOutputs_; + std::unordered_set bufsToBeParallelized_; + std::unordered_map bufs_; + std::unordered_map scalars_; + std::unordered_map input_name_map_; + std::unique_ptr codegen_; + at::Device device_ = at::kCPU; + std::shared_ptr graph_; + Code code_; + bool allow_fallback_{false}; + bool use_fallback_{false}; + bool hasRandom_{false}; + bool hasBroadcast_{false}; + std::unordered_map> + known_sizes_; + + std::vector> tensorOutputSymbolicSizes_; + // A map from ShapeSymbol.value() to the corresponding Var. + std::unordered_map shapeSymbolToVar_; + std::unordered_map shapeSymbolInputPos_; + // List of values corresponding to the ShapeSymbols that are inputs to + // kernel being compiled. The order of these values correspond to the order + // of the symbolic inputs at the end of the list of inputs to the kernel. + std::vector symbolic_shape_inputs_; + bool has_symbolic_shapes_{false}; + + std::vector unpacked_constant_tensors_; + std::vector constants_; + + std::unordered_map custom_lowerings_; + StmtPtr stmt_ = nullptr; + bool pre_alloc_{false}; + std::string kernel_func_name_; + + // index of stack, stride index of tensor that will be appended as a codegen + // arg + std::vector> input_stride_args_; + // map from to stride as arg VarHandle + std::unordered_map, VarHandle, SmallSizeTPairHash> + strideArgToVar_; + std::unordered_map< + const torch::jit::Value*, + std::vector> + symbolic_strides_; + + // Memory layout to be propagated with fusion group + MemoryLayoutPolicy memory_layout_policy_ = MemoryLayoutPolicy::kContiguous; +}; + +TORCH_API int& getTECudaPointwiseLoopLevels(); +TORCH_API int& getTECudaPointwiseBlockCount(); +TORCH_API int& getTECudaPointwiseBlockSize(); +TORCH_API bool& getTEGenerateBlockCode(); +TORCH_API bool& getTEMustUseLLVMOnCPU(); +TORCH_API bool fallbackAllowed(); +TORCH_API bool setFallbackAllowed(bool value); +TORCH_API bool& getCatWoConditionals(); +TORCH_API bool& getOptConditionals(); + +TORCH_API std::optional pickDeviceType( + const at::ArrayRef& inputs); + +bool isContiguous( + const torch::jit::Value* v, + at::MemoryFormat memory_format = at::MemoryFormat::Contiguous); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/llvm_jit.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/llvm_jit.h new file mode 100644 index 0000000000000000000000000000000000000000..beadbdd5e537e7f054a70200af16a2f7a67c21fa --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/llvm_jit.h @@ -0,0 +1,77 @@ +#pragma once + +#ifdef TORCH_ENABLE_LLVM +#include +#include +#include +#include + +C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED("-Wsuggest-override") +#include +C10_DIAGNOSTIC_POP() +#include +#include +#include + +#include +#include + +namespace torch { +namespace jit { +namespace tensorexpr { + +inline std::string formatError(llvm::Error&& err, const char* msg) { + static constexpr const char* defaultErrorMsg = + "Unexpected failure in LLVM JIT"; + std::string errorMsg(msg ? msg : defaultErrorMsg); + llvm::raw_string_ostream ss(errorMsg); + ss << ": " << err; + return ss.str(); +} + +template +T assertSuccess(llvm::Expected valOrErr, const char* msg = nullptr) { + TORCH_INTERNAL_ASSERT(valOrErr, formatError(valOrErr.takeError(), msg)); + return std::move(*valOrErr); +} + +inline void assertSuccess(llvm::Error err, const char* msg = nullptr) { + TORCH_INTERNAL_ASSERT(!err, formatError(std::move(err), msg)); +} + +} // namespace tensorexpr +} // namespace jit +} // namespace torch + +namespace llvm { +namespace orc { + +class PytorchLLVMJITImpl; + +class TORCH_API PytorchLLVMJIT { + public: + PytorchLLVMJIT( + std::optional triple, + std::optional cpu, + std::optional attrs); + ~PytorchLLVMJIT(); + + void addModule(std::unique_ptr M, std::unique_ptr C); + + JITSymbol findSymbol(const std::string Name); + + bool hasSymbol(const std::string& Name); + + TargetMachine& getTargetMachine(); + + const DataLayout& getDataLayout(); + + private: + // Use the PImpl idiom here to hide the no-rtti parts of the JIT structure. + std::unique_ptr impl_; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // ENABLE LLVM diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest.h new file mode 100644 index 0000000000000000000000000000000000000000..20614fea0bad9dafaf9190b1a931a8e24ed2b030 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest.h @@ -0,0 +1,616 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace torch::jit::tensorexpr { + +class Expr; +class Var; +class Buf; +class Tensor; +class Function; +class Stmt; +class For; +class Block; +class Store; +class Dtype; + +class TORCH_API LoopNest { + public: + // A constructor for building a LoopNest from a list of Tensors + LoopNest( + const std::vector& output_tensors, + const std::vector& tensors_to_compute); + + // A convenience constructor for the case when all tensors are output tensors + LoopNest(const std::vector& output_tensors); + + // A constructor for building a LoopNest from an Stmt and a list of output + // buffers. + LoopNest(StmtPtr stmt, std::unordered_set output_bufs); + + // A constructor for building a LoopNest from another loopnest. It clones the + // other loopnest's stmt. + LoopNest(const LoopNest& other); + + StmtPtr root_stmt() const { + return root_stmt_; + } + + std::vector getLoopStmtsFor(const Tensor&) const; + std::vector getLoopStmtsFor(const BufPtr&) const; + std::vector getLoopStmtsFor(StmtPtr) const; + StmtPtr getLoopBodyFor(const Tensor&) const; + StmtPtr getLoopBodyFor(BufPtr) const; + + // Returns the For stmt indexed by 'indices' in the 'root' For stmt. + //'indices' indicates the path to the returned loop from 'root' in AST, e.g., + // + // root: for(int i...){ + // j_loop: for (int j...){ + // k1_loop: for (int k1...){ + // A[i, j, k1] = .... + // } + // B[i, j] = ... + // k2_loop: for (int k2...){ + // A[i, j, k2] = ... + // } + // } + // } + // + // the path from 'root' to 'j_loop' is [0] + // the path from 'root' to 'k1_loop' is [0, 0] + // the path from 'root' to 'k2_loop' is [0, 2] + ForPtr getLoopAt(ForPtr root, const std::vector& indices) const; + + // Returns the For stmt that is immediately enclosing the given stmt. + static ForPtr getParentLoop(const StmtPtr& st); + + // Returns the list of For stmts corresponding to the loopnest that is + // enclosing the given stmt. + static std::vector getEnclosingLoopNest(const StmtPtr& st); + + // Returns a list of all Stmts that write to the given buf. + std::vector getAllWritesToBuf(BufPtr) const; + + // The following methods return the For loops that contain writes to + // the given buf. + // + // For example, consider the following code: + // for i1 + // for j1 + // a[i1,j1] = + // for i2 + // for j2 + // for k2 + // a[i2,j2] = + // for j3 + // a[i2,j3] = + + // Returns a list of For loops which directly contain a Stmt that writes + // to buf. + // For the above example: + // getAllInnermostLoopsWritingToBuf(a) => {j1, k2, j3} + std::vector getAllInnermostLoopsWritingToBuf(BufPtr) const; + + // Returns a list of For loopnests which contain a Stmt that writes to + // the given buf. Each loopnest here is a vector For loops. + // For the above example: + // getAllLoopNestsWritingToBuf(a) => {{i1,j1}, {i2,j2,k2}, {i2,j3}} + std::vector> getAllLoopNestsWritingToBuf(BufPtr) const; + + StmtPtr simplify(); + + // Sanitize variables and buffer names. + // The pass assigns predefined names for loop index variables + // (i,j,k,l,m,n,o,p,i1,j1,k1,...) and ensures these names are not conflicting + // anywhere. It also removes duplicates from other Buf nad Var names as well + // as replaces illegal characters in them with underscores. + // + // Note: since it's currently technically possible to use the same variable + // as index in two different loops, this transformation finds such cases and + // introduces new variables to avoid duplication. + static StmtPtr sanitizeNames(StmtPtr s); + + bool computeInline(const StmtPtr& s); + bool computeInline(const BufPtr& b); + void inlineIntermediateBufs(bool allow_duplicated_work); + + // Optimizes conditionals. + // + // Currently, only the following pattern of conditionals is optimized. + // This corresponds to the conditional format that is generated to handle + // `aten::cat` op. + // + // for (int i = 0; i < 20; i++) { + // A[i] = IfThenElse(i<5 ? 1 : 0, B[i], C[i-5]) + // } + // + // Constraints that must be satisfied for this optimization: + // * All conditions should be of the form "var < expr". + // * All conditions should have the same variable, say v. + // * The condition variable found should be the same as the inner-most + // loop variable. TODO: Remove this constraint. + // * If there are multiple stores that contain conditionals using the same + // loop variable, only the first conditional will be optimized. + // TODO: Remove this constraint. + bool optimizeConditionals(); + + // Splits the given loop into 2 nested loops with the given factor as the + // inner loop bound. If the factor does not evenly divide the loop bound, + // then the remaining iterations are extracted into a tail loop that is + // added after the given loop. + // + // For example, consider the following code: + // for (int i = 0; i < 100; ++i) { + // A[i] = + // } + // + // splitWithTail(i, 8, ...) will result in: + // for (int i_outer = 0; i_outer < 12; ++i_outer) { + // for (int i_inner = 0; i_inner < 8; ++i_inner) { + // A[i_outer * 8 + i_inner] = + // } + // } + // for (int i_tail = 0; i_tail < 4; ++i_tail) { + // A[i_tail + 96] = + // } + // + // The given loop will be transformed to the outer loop after splitting. + // So, the pointer to the input loop should be valid after splitting and + // will point to the outer loop. The `inner` and `tail` parameters will be + // set to point to the inner and tail loops that are generated. + static void splitWithTail( + const ForPtr& f, + int factor, + ForPtr* inner, + ForPtr* tail); + // A convenience wrapper when the caller does not need to access the + // split loops. + static void splitWithTail(const ForPtr& f, int factor); + + // Splits the given loop into 2 nested loops with the given factor as the + // inner loop bound. If the factor does not evenly divide the loop bound, + // then a conditional is inserted into the body to handle the remaining + // iterations appropriately. + // + // For example, consider the following code: + // for (int i = 0; i < 100; ++i) { + // A[i] = + // } + // + // splitWithMask(i, 8, ...) will result in: + // for (int i_outer = 0; i_outer < 13; ++i_outer) { + // for (int i_inner = 0; i_inner < 8; ++i_inner) { + // if (i_outer * 8 + i_inner < 100) { + // A[i_outer * 8 + i_inner] = + // } + // } + // } + // + // The given loop will be transformed to the outer loop after splitting. + // So, the pointer to the input loop should be valid after splitting and + // will point to the outer loop. The `inner` parameter will be set to point + // to the inner loop that is generated. + static void splitWithMask(const ForPtr& f, int factor, ForPtr* inner); + // A convenience wrapper when the caller does not need to access the + // split loops. + static void splitWithMask(const ForPtr& f, int factor); + + // The following methods support loop distribution. + // For example, consider the following code. This will be used to + // demonstrate the methods below. + // + // S0: for m + // S1: for i + // S2: A[i] = 0 + // S3: for j + // S4: A[i] = A[i] + + // S5: B[i] = A[i] + // S6: for k + // S7: B[i] = B[i] + + + // This method distributes the given loop over its body by splitting + // after every given pivot stmt. + // + // NOTE: Pivot stmts that are not in the given loop's body will be ignored. + // + // For the above example: + // distributeLoop(S1, {S3, S5}) + // will result in: + // S0: for m + // S1: for i + // S2: A[i] = 0 + // S3: for j + // S4: A[i] = A[i] + + // : for i + // S5: B[i] = A[i] + // : for i + // S6: for k + // S7: B[i] = B[i] + + static std::vector distributeLoop( + const ForPtr& loop, + const std::unordered_set& pivots); + + // This method distributes the given loop over every stmt in its body. + // + // For the above example: + // distributeLoop(S1) + // will result in: + // S0: for m + // S1: for i + // S2: A[i] = 0 + // : for i + // S3: for j + // S4: A[i] = A[i] + + // : for i + // S5: B[i] = A[i] + // : for i + // S6: for k + // S7: B[i] = B[i] + + static std::vector distributeLoop(const ForPtr& loop); + // Same as above, but also distribute parent loops. + // Returns the result of distributing the outermost loop. + // + // For the above example: + // distributeLoopAndParents(S1) will result in: + // S0: for m + // S1: for i + // S2: A[i] = 0 + // : for m + // : for i + // S3: for j + // S4: A[i] = A[i] + + // : for m + // : for i + // S5: B[i] = A[i] + // : for m + // : for i + // S6: for k + // S7: B[i] = B[i] + + static std::vector distributeLoopAndParents(const ForPtr& loop); + + // This method distributes the given loop over its body by splitting + // after every For stmt in its body. + // + // For the above example: + // distributeLoopOverInnerLoops(S1) + // will result in: + // S0: for m + // S1: for i + // S2: A[i] = 0 + // S3: for j + // S4: A[i] = A[i] + + // : for i + // S5: B[i] = A[i] + // S6: for k + // S7: B[i] = B[i] + + static std::vector distributeLoopOverInnerLoops(const ForPtr& loop); + // Same as above, but also distribute parent loops. + // Returns the result of distributing the outermost loop. + // + // For the above example: + // distributeLoopAndParentsOverInnerLoops(S1) + // will result in: + // S0: for m + // S1: for i + // S2: A[i] = 0 + // S3: for j + // S4: A[i] = A[i] + + // : for m + // : for i + // S5: B[i] = A[i] + // S6: for k + // S7: B[i] = B[i] + + static std::vector distributeLoopAndParentsOverInnerLoops( + const ForPtr& loop); + + // This method performs loop fusion. + // For example, consider the following code. + // + // S1: for m + // S2: A[m] = 0 + // S3: for j + // S4: A[m] = A[m] + + // S5: for n + // S5: B[n] = A[n] + // S6: for k + // S7: B[n] = B[n] + + // + // fuseLoops({S1, S5}), will return the following loop: + // S1: for m + // S2: A[m] = 0 + // S3: for j + // S4: A[m] = A[m] + + // S5: B[m] = A[m] + // S6: for k + // S7: B[m] = B[m] + + // + // This transformation is unsafe as it simply add all loops into the body of + // the first loop for fusion without correctness checks. + // + // Below are the two requirements to apply unsafeFuseLoops: + // * All the loops have the same parent. + // * There are no statements between these loops in their parent body. + static bool unsafeFuseLoops(const std::vector& loops, ForPtr* fused); + + // Loop fusion is done only when all the conditions below are satisfied. + // * All the loops have the same parent. + // * There are no statements between these loops in their parent body. + // * The start bounds are the same for all loops. + // * The stop bounds are the same for all loops. + // * Fusing the loops does not violate or add any dependencies. + static bool fuseLoops(const std::vector& loops, ForPtr* fused); + + static void reorderAxis(const ForPtr& a, const ForPtr& b); + + // Reorder the given list of loops according to the permutation specified. + // Here `permutation[i]` represents the position of the loop in the input + // which will end up at position `i` after the reorder. + // + // For example, consider the following code: + // for p + // for q + // for r + // for s + // A[p,q,r,s] = + // + // reorder({p, q, r, s}, {2, 3, 0, 1}) will return the list of loops in the + // following form: + // for r + // for s + // for p + // for q + // A[p,q,r,s] = + static std::vector reorder( + const std::vector& loops, + const std::vector& permutation); + + // Tile takes a 2d domain (x, y) and splits it into small rectangular blocks + // each with shape (x_factor, y_factor). The traversal over the domain turns + // into an outer iteration over the blocks and an inner traversal over all + // points in the block. + // Note that if x dim % x_factor or y dim % y_factor does not equal to 0, the + // loop body will generate corresponding tailing loops. + // The transformation is in-place and returns 'xtail'. + // + // For example, consider the following code: + // for i: [0, 64) + // for j: [0, 64) + // for k: [0, 32) + // A[i, j] = B[i, k] + C[j, k] + // + // tile(i, j, 4, 8) will transform "i" for-stmt into the following nested + // loop: + // for i_outer: [0, 16) + // for j_outer: [0, 8) + // for i_inner: [0, 4) + // for j_inner: [0, 8) + // for k: [0, 32) + // A[i_outer * 4 + i_inner, j_outer * 8 + j_inner] = + // B[i_outer * 4 + i_inner, k] + C[j_outer * 8 + j_inner, k] + // + // tile(i, j, 4, 9) will transform "i" for-stmt into the following nested + // loop: + // for i_outer: [0, 16) + // for j_outer: [0, 7) + // for i_inner: [0, 4) + // for j_inner: [0, 9) + // for k: (0, 32) + // A[i_outer * 4 + i_inner, j_outer * 9 + j_inner] = + // B[i_outer * 4 + i_inner, k] + C[j_outer * 9 + j_inner, k] + // for j_tail: [0, 1) + // for i_inner: [0, 4) + // for k: (0, 32) + // A[i_outer * 4 + i_inner, 7 * 9 + j_tail] = + // B[i_outer * 4 + i_inner, k] + C[7 * 9 + j_tail, k] + ForPtr tile(const ForPtr& x, const ForPtr& y, int x_factor, int y_factor); + + // Returns true if the given loops are perfectly nested, i.e., every loop + // (except the innermost) should have exactly one statement in its body + // and that statement must be the next inner loop. + static bool areLoopsPerfectlyNested(const std::vector& loops); + + // Returns true if the given loop has a loop-carried dependence. + static bool hasLoopCarriedDependence(const ForPtr& loop); + + // Unrolls all the iterations of the given loop. + // Requires that the loop bounds are constant. + static void fullUnroll(const ForPtr& f, StmtPtr* unrolled); + static void fullUnroll(const ForPtr& f); + + // Unrolls the given loop for the specified factor. + // This does not require constant bounds for the loop being unrolled. + static void unroll(const ForPtr& f, int factor, ForPtr* tail); + static void unroll(const ForPtr& f, int factor); + + static bool normalize(const ForPtr& f); + static bool isNormalized(const ForPtr& f); + + static bool flatten(const std::vector& f, ForPtr* flattened); + static bool flatten(const std::vector& f); + + // Compresses the given buffer based on its use in the given Stmts. + // + // NOTE: This API assumes that there are no accesses to the given buffer + // outside the given statement. So, this should be called with the entire + // kernel statement to avoid incorrect buffer compressions. + // + // For example, given the input: + // + // for (int i = 0; i < 100; ++i) { + // for (int j = 0; j < 200; ++j) { + // A[i,j] = sin(i*j) + // } + // for (int j = 0; j < 199; ++j) { + // B[i,j] = A[i,j] + A[i, j+1] + // } + // } + // + // compressBuffer(A, ...) will compress buffer A from + // [100, 200] to [1, 200] and modify the code as follows: + // + // for (int i = 0; i < 100; ++i) { + // for (int j = 0; j < 200; ++j) { + // A[0,j] = sin(i*j) + // } + // for (int j = 0; j < 199; ++j) { + // B[i,j] = A[0,j] + A[0, j+1] + // } + // } + static void compressBuffer(const BufPtr& buf, const StmtPtr& stmt); + + // Compresses all buffers in the given statement. + // + // NOTE: This API assumes that there are no accesses to buffers outside + // the given statement. So, this should be called with the entire + // kernel statement to avoid incorrect buffer compressions. + // + // TODO: Add an IR verifier check to detect invalidly compressed buffers. + static void compressAllBuffers(const StmtPtr& stmt); + + // Get 'num' loops from the loopnest starting at 'f'. + static std::vector getLoopStmtsInLoopNest( + const ForPtr& f, + size_t num); + + // LoopOptions are propagated to tail. + static void sliceHead( + const ForPtr& f, + int factor, + ForPtr* head, + ForPtr* tail); + static void sliceHead(const ForPtr& f, int factor); + // LoopOptions are propagated to head. + static void sliceTail( + const ForPtr& f, + int factor, + ForPtr* head, + ForPtr* tail); + static void sliceTail(const ForPtr& f, int factor); + + using AccessResult = std::pair; + // Insert a cache for the consumer's usages of the buffer produced in + // consumer, and redirect reads and writes in the consumer to that cache. + // Returns a pair of the new cache buffer, and the new rewritten consumer. + static AccessResult cacheAccesses( + const BufPtr& producer, + const std::string& name, + const StmtPtr& consumer); + + // Insert a temporary computation of statement S in the scope of loop AT. + // S is assumed to be a Store or a Block containing a Store. Along with the + // computation itself, this transformation inserts Alloc/Free statements for + // the temporary buffer used in the computation. + static void computeAt(const StmtPtr& s, const ForPtr& at); + + // Rfactor a reduction axis into a normal axis. + // + // Requirements: + // * S is the reduction store + // * S is the only statement in the innermost loop + // * There is at least two reduction arguments in S + // * OUTER_REDUCTION_FOR loop corresponds to the outermost reduction variable + // used in the store and all other reduction variables are index variables of + // children loops of OUTER_REDUCTION_FOR + // * OUTER_REDUCTION_FOR is a perfect loop nest, i.e. it has only loops + // corresponding to the other reduction variables and the store, nested into + // each other + // + // What it does: + // * Introduce a new buffer with an extra dimension of a size equal to the + // span of the loop OUTER_REDUCTION_FOR (the new buffer is returned via + // RFAC_BUF_PTR) + // * Insert an initialization store for the new buffer in + // OUTER_REDUCTION_FOR before its nested loop + // * Replace the reduction store to the original buffer with the reduction + // store to the temp buffer, removing the index var of OUTER_REDUCTION_FOR + // from reduction arguments + // * Insert a final reduction store over the extra dimension of the new + // buffer to the original buffer + // * Returns TRUE if the transformation succeeded and FALSE otherwise + // + // Example: + // Original IR: + // S1: for i # normal axis + // S2: X[i] = 0 + // S3: for j # reduction axis + // S4: for k # reduction axis + // S5: X[i] = ReduceOp(X[i] + Y[i,j,k], reduce_axis={j,k}) + // + // After RFACTOR(S5, S3) + // S1: for i # normal axis + // S2: X[i] = 0 + // S3: for j # reduction axis for X, normal axis for X_rfac + // X_rfac[i,j] = 0 + // S4: for k # reduction axis + // X_rfac[i,j] = ReduceOp(X_rfac[i,j] + Y[i,j,k], reduce_axis={k}) + // X[i] = ReduceOp(X[i] + X_rfac[i,j], reduce_axis={j}) + static bool rfactor(const StmtPtr& s, const ForPtr& outer_reduction_for); + static bool rfactor( + const StmtPtr& s, + const ForPtr& outer_reduction_for, + BufPtr* rfac_buf_ptr); + + // Vectorize the given loop. This method requires that the given loop + // does not perform a reduction. + // It returns true if vectorization is successful and false otherwise. + static bool vectorize(const ForPtr&); + + // Find the inner-most loops and vectorize them. Currently, this only works + // for the LLVM backend, when no reductions are involved. + void vectorizeInnerLoops(); + + void eliminateDeadStores(); + + void prepareForCodegen(); + + const std::unordered_set getInputBufs() const; + const std::unordered_set getOutputBufs() const { + return output_bufs_; + } + std::vector getIntermediateBufs() const; + + // Finds which is the outer For between a and b for loops. If neither of the 2 + // Fors is an ancestor of the other, it returns nullptr. + static ForPtr findOuterFor(ForPtr a, ForPtr b); + + private: + void initialize( + const std::vector& output_tensors, + const std::vector& tensors_to_compute); + + StmtPtr root_stmt_; + + std::unordered_set output_bufs_; +}; + +TORCH_API StmtPtr FlattenIndexes(const StmtPtr& s); + +// TODO: Revisit this once we decide on how dependencies analysis should look +// like. Maybe we would choose to use a different API and BufUse would be +// removed, or if we decide to keep it we need to properly document its API. +struct BufLoadOrStoreUse { + StmtPtr s; + bool isStore; +}; + +/* + * Returns a map ( Buf -> uses of this Buf), uses are represented as vectors of + * BufUse elements, which are StmtPtr and a bool isStore flag. The order of uses + * in the vectors reflects the order in which the uses appear in the given + * statement. + */ +std::unordered_map> findLoadOrStoreUses( + const StmtPtr& s); + +// replaces all invalid characters with underscore +TORCH_API std::string sanitizeName(const std::string& input_name); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest_randomization.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest_randomization.h new file mode 100644 index 0000000000000000000000000000000000000000..3f5f687416cb752aa32319680d49c928e1549fe4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/loopnest_randomization.h @@ -0,0 +1,9 @@ +#pragma once + +namespace torch::jit::tensorexpr { + +// Applies a series of loop optimizations chosen randomly. This is only for +// testing purposes. This allows automatic stress testing of NNC loop +// transformations. +void loopnestRandomization(int64_t seed, LoopNest& l); +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/mem_dependency_checker.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/mem_dependency_checker.h new file mode 100644 index 0000000000000000000000000000000000000000..39202f487ad2de57b5e566bcd1e5cb9ac0fdd50e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/mem_dependency_checker.h @@ -0,0 +1,409 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr::analysis { + +enum class AccessType { + Input, + Output, + Load, + Store, + Call, + AtomicAdd, + Alloc, + Free +}; +const char* AccessToString(AccessType a); + +class AccessInfo; +using DependencySet = std::unordered_set>; + +/* AccessInfo + * + * Represents a single bounded memory access to a buffer, for instance a Load or + * a Store. Holds information relating to the specific access and links to + * connected accesses in the dependency graph. + */ +class TORCH_API AccessInfo { + public: + AccessInfo( + size_t id, + AccessType type, + StmtPtr stmt, + VarPtr var, + IndexBounds bounds) + : id_(id), + type_(type), + stmt_(std::move(stmt)), + expr_(nullptr), + var_(std::move(var)), + bounds_(std::move(bounds)) {} + + AccessInfo( + size_t id, + AccessType type, + ExprPtr expr, + StmtPtr stmt, + VarPtr var, + IndexBounds bounds) + : id_(id), + type_(type), + stmt_(std::move(stmt)), + expr_(std::move(expr)), + var_(std::move(var)), + bounds_(std::move(bounds)) {} + + // Id is a unique int representing the order this access occurred in the + // graph. + size_t id() const { + return id_; + } + + // The type of the access (Load, Store, etc). + AccessType type() const { + return type_; + } + + // The enclosing Stmt this access represents. E.g. if this is a Store then + // Stmt is the Store itself, while if the access is caused by an Expr, this is + // the most immediate parent Stmt. + StmtPtr stmt() const { + return stmt_; + } + + // If the access is represented by an Expr (such as Load or Call) then this is + // it, otherwise it's nullptr. + ExprPtr expr() const { + return expr_; + } + + // The Var representing the underlying Buffer. + VarPtr var() const { + return var_; + } + + // A vector of Bounds representing the start and end expression for each + // dimension. + IndexBounds& bounds() { + return bounds_; + } + + // Each access that this depends upon, + // eg. if this is a Load, then it contains every Store that immediately + // contributes to a load of the bounds. + // or: if this is a Store, it contains all reads on the RHS of the Store. + const std::map>& dependencies() const { + return dependencies_; + } + + // Each access that depends on this one. + // ie. this access is present in the dependencies map of all accesses that are + // dependent. + std::map> dependents() const { + std::map> res; + for (const auto& kv : dependents_) { + res.emplace(kv.first, kv.second.lock()); + } + return res; + } + + // Returns the symbolic expression of the indices of this access. + std::vector getIndices() const; + + // Establishes a dependency or dependent relationship with another access. + void addDependency(const std::shared_ptr& write); + void addDependent(const std::shared_ptr& read); + + // helper for checking dependencies. + bool hasDependency(const std::shared_ptr& info) const; + + // Returns the set of all nodes that are direct (immediate) dependencies of + // this access. + DependencySet getDirectDependencies(); + // likewise, returns all nodes that directly depend on this one. + DependencySet getDirectDependents(); + + // Returns the full list of all nodes in the graph that this access depends + // on, and all nodes they depend on, and so forth, back to the inputs. + DependencySet getIndirectDependencies(); + // likewise, returns the full list of all nodes that depend on this node, and + // all nodes that depend on those nodes and so on down to the outputs. + DependencySet getIndirectDependents(); + + // Does this access represent a read of memory (Load, ReduceOp, Call, etc). + bool isRead() const; + // Does this access represent a write of memory (Store, etc). + bool isWrite() const; + + // Helpers for dumping accesses in various formats. + void print() const; + void dumpDOT(std::ostream& os) const; + const char* AccessTypeColour() const; + + private: + size_t id_; + AccessType type_; + StmtPtr stmt_; + ExprPtr expr_; + VarPtr var_; + IndexBounds bounds_; + + // Yes these should be sorted. + std::map> dependencies_; + std::map> dependents_; +}; + +using VarBoundMap = std::unordered_map; + +/* MemDependencyChecker analyses a IR fragment and builds a dependency graph of + * accesses contained within. + * + * It's possible to retrieve the entire graph in node-object form, or can be + * used as an oracle for answering dependency questions. e.g: + * + * analyzer.hasIndirectDependency(BufA, BufB); or, + * analyzer.hasDirectDependency(LoadA, StoreB); + */ +class TORCH_API MemDependencyChecker : public IRVisitor { + struct Scope; + + public: + MemDependencyChecker(); + MemDependencyChecker( + const std::unordered_set& inputs, + const std::unordered_set& outputs); + MemDependencyChecker( + const std::vector& inputs, + const std::vector& outputs); + + ~MemDependencyChecker() override = default; + + // Whether or not to allow loop execution order to influence dependency + // calculation. If the loop may later be parallelized you don't want this. + bool allowLoopExecutionOrderAnalysis(bool allow = true); + + // Dependency Checking API. + // The goal is to have enough overloads here so you don't really have to think + // about it. + + // Returns true if any read in A has a direct dependence on a write in B. + bool dependsDirectly(const StmtPtr& A, const StmtPtr& B); + bool dependsDirectly(const ExprPtr& A, const StmtPtr& B); + + // Returns true of the output depends directly on a write contained in B. + bool dependsDirectly(const BufPtr& output, const StmtPtr& B); + + // Returns true if a read in A depends directly on the provided input. + bool dependsDirectly(const StmtPtr& A, const BufPtr& input); + bool dependsDirectly(const ExprPtr& A, const BufPtr& input); + + // Outputs/inputs cannot depend directly. + + // Returns true if the access A has B as an immediate dependency. + bool dependsDirectly( + const std::shared_ptr& A, + const std::shared_ptr& B); + + // Returns true if any read in A has an ancestor write contained in B. + bool dependsIndirectly(const StmtPtr& A, const StmtPtr& B); + bool dependsIndirectly(const ExprPtr& A, const StmtPtr& B); + + // Returns true of the output depends indirectly on a write contained in B. + bool dependsIndirectly(const BufPtr& output, const StmtPtr& B); + + // Returns true if a read in A depends indirectly on the provided input. + bool dependsIndirectly(const StmtPtr& A, const BufPtr& input); + bool dependsIndirectly(const ExprPtr& A, const BufPtr& input); + + // returns true if the output uses any load of the input. + bool dependsIndirectly(const BufPtr& output, const BufPtr& input); + + // Returns true if the access A has a dependency chain to access B. + bool dependsIndirectly( + const std::shared_ptr& A, + const std::shared_ptr& B); + + // Returns the AccessInfo + std::shared_ptr accessFor(const StmtPtr& A) const; + std::shared_ptr accessFor(const ExprPtr& A) const; + + // Returns all AccessInfos. + std::unordered_set> accessesWithin( + const StmtPtr& A) const; + // TODO: this will return only the AccessInfo for A. It's included for + // completeness but be aware it wont return accesses used in the computation + // of A. + std::unordered_set> accessesWithin( + const ExprPtr& A) const; + + // Accesses relating to input and output buffers. + std::shared_ptr input(const BufPtr& B) const; + std::shared_ptr output(const BufPtr& B) const; + + // Returns the full history of reads and writes. + const std::vector>& getHistory() const; + + // Dumps the dependency graph in DOT format. + void dumpDAG(const std::string& filename) const; + + private: + // Node visitors. + void visit(const StorePtr& v) override; + void visit(const LoadPtr& v) override; + void visit(const ForPtr& v) override; + void visit(const CondPtr& v) override; + void visit(const IfThenElsePtr& v) override; + void visit(const CompareSelectPtr& v) override; + void visit(const BlockPtr& v) override; + void visit(const LetPtr& v) override; + void visit(const AtomicAddPtr& v) override; + void visit(const AllocatePtr& v) override; + void visit(const FreePtr& v) override; + + using BoundRelationship = std::pair>; + + // An internal struct holding the accesses found within a scope Block. + struct Scope { + Scope(BlockPtr b, std::shared_ptr p) + : block(std::move(b)), parent(std::move(p)) {} + + BlockPtr block; + std::shared_ptr parent; + + std::unordered_map shadowedVarBounds; + std::unordered_set localVars; + + std::vector> accesses_; + + std::unordered_map> openWrites_; + }; + std::shared_ptr currentScope_; + + bool allowExecutionOrderAnalysis_{false}; + + std::unordered_multimap> stmtToAccess_; + std::unordered_multimap> exprToAccess_; + std::unordered_map>> + scopeToAccesses_; + + VarBoundMap knownVarBounds_; + + // Finds all accesses that are reads within the scope of v. + template + DependencySet getAllReadsWithin(const StmtOrExprPtr& v) { + DependencySet reads; + auto insertAllReads = [&](const auto& nodes) { + for (const auto& l : nodes) { + auto bound = exprToAccess_.equal_range(l); + for (auto it = bound.first; it != bound.second; ++it) { + if (it->second->isRead()) { + reads.insert(it->second); + } + } + } + }; + + // Look for and insert accesses belonging to all nodes that act like + // reads. + insertAllReads(NodeFinder::find(v)); + insertAllReads(NodeFinder::find(v)); + + return reads; + } + + // Finds all accesses that are writes within the scope of v. + // Writes cannot occur in Exprs, so this is a little simpler. + DependencySet getAllWritesWithin(const StmtPtr& v) { + DependencySet writes; + + // writes just Store currently. + auto stores = NodeFinder::find(v); + for (const auto& s : stores) { + auto bound = stmtToAccess_.equal_range(s); + for (auto it = bound.first; it != bound.second; ++it) { + if (it->second->isWrite()) { + writes.insert(it->second); + } + } + } + return writes; + } + + // Templated helpers to work on either Exprs or Stmts. + template + bool dependsDirectlyHelper(const StmtOrExprPtr& A, const StmtPtr& B) { + auto aReads = getAllReadsWithin(A); + auto bWrites = getAllWritesWithin(B); + + for (auto& read : aReads) { + for (auto& depPair : read->dependencies()) { + if (bWrites.count(depPair.second) != 0) { + return true; + } + } + } + + return false; + } + + template + bool dependsIndirectlyHelper(StmtOrExprPtr A, const StmtPtr& B) { + auto aReads = getAllReadsWithin(A); + auto bWrites = getAllWritesWithin(B); + + auto aDeps = getAllWriteDependencies(aReads); + + for (auto& dependency : aDeps) { + if (bWrites.count(dependency) != 0) { + return true; + } + } + + return false; + } + + DependencySet getAllWriteDependencies(const DependencySet& products); + + // Maps for inputs and outputs, since they aren't present directly in the IR. + std::unordered_map> inputs_; + std::unordered_map> outputs_; + std::unordered_map> intermediates_; + + // Inserts accesses for Buf's: specifically for inputs and outputs. + void insertBuffers( + std::unordered_map>& bufs, + AccessType type); + + // Update the write history with a new write, adding dependencies and closing + // any overlapped writes (if possible). + void updateWriteHistory( + std::list& writeHistory, + const std::shared_ptr& info, + size_t latestAccessToClose, + bool closeOverlapped = true, + bool insert = true); + + // Merge a child scope into a parent scope, adding dependencies for open + // writes in the parent to accesses in the child. + void mergeScope( + const std::shared_ptr& child, + const std::shared_ptr& parent, + bool closeOverlapped = true); + + // Binds symbolic vars in indices with the low and high bound for those vars. + std::vector getIndicesBounds(const std::vector& indices); + + size_t nextAccess_{0}; + StmtPtr lastStmt_{nullptr}; +}; + +} // namespace torch::jit::tensorexpr::analysis diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/reduction.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/reduction.h new file mode 100644 index 0000000000000000000000000000000000000000..c65cf43be7fba00ff9cc974c7316d468deb3ad12 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/reduction.h @@ -0,0 +1,306 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace torch::jit::tensorexpr { + +using ParameterList = const std::vector; +using ReduceInteraction = std::function; + +// A Reducer is a user interface describing a particular reduction +// operation. It has three components: An initialization value, a way of +// interacting each value with the accumulation, and a method for obtaining the +// current value to be reduced. It is materialized into a ReduceOp when loop +// variables are known. +class TORCH_API Reducer { + public: + Reducer(ExprHandle init, ReduceInteraction& interaction) + : init_(init.node()), interaction_(interaction) {} + + template + Reducer(ExprHandle init, RI interaction) + : init_(init.node()), interaction_(std::move(interaction)) {} + + ExprPtr initializer() const { + return init_; + } + + ExprHandle operator()( + const BufHandle& result_buf, + ExprHandle body, + const std::vector& output, + const std::vector& inner) const; + + ReduceOpPtr operator()( + const BufPtr& result_buf, + ExprPtr body, + const std::vector& output, + const std::vector& inner) const; + + ExprHandle operator()( + const BufHandle& result_buf, + BufHandle acc_buf, + const ExprHandle& body, + const std::vector& output, + const std::vector& inner) const; + + // Polymorphic handling of Body functions with a variety of parameters. + static ExprHandle getReduceBody( + const std::function& func, + const std::vector& vars) { + return func(vars); + } + + static ExprHandle getReduceBody( + const std::function& func, + const std::vector& vars) { + if (vars.size() != 1) { + throw malformed_input("mismatch between reduce body and arg size (1)"); + } + + return func(vars[0]); + } + + static ExprHandle getReduceBody( + const std::function& func, + const std::vector& vars) { + if (vars.size() != 2) { + throw malformed_input("mismatch between reduce body and arg size (2)"); + } + return func(vars[0], vars[1]); + } + + static ExprHandle getReduceBody( + const std::function< + ExprHandle(const VarHandle&, const VarHandle&, const VarHandle&)>& + func, + const std::vector& vars) { + if (vars.size() != 3) { + throw malformed_input("mismatch between reduce body and arg size (3)"); + } + return func(vars[0], vars[1], vars[2]); + } + + static ExprHandle getReduceBody( + const std::function& func, + const std::vector& vars) { + if (vars.size() != 4) { + throw malformed_input("mismatch between reduce body and arg size (4)"); + } + return func(vars[0], vars[1], vars[2], vars[3]); + } + + // Completes the reduction operator by applying the interaction function to + // the accumulation and the body expression. + static ExprPtr complete( + const BufPtr& accumulator, + const ReduceInteraction& interaction, + ExprHandle body, + const std::vector& output_args, + const std::vector& reduce_args) { + ExprHandle accum = + ExprHandle(alloc(body.dtype(), accumulator, output_args)); + auto e = interaction(std::move(accum), std::move(body)); + return e.node(); + } + static ExprHandle complete( + const BufHandle& accumulator, + const ReduceInteraction& interaction, + ExprHandle body, + const std::vector& output_args, + const std::vector& reduce_args) { + ExprHandle accum = Load::make(body.dtype(), accumulator, output_args); + auto e = interaction(std::move(accum), std::move(body)); + return e; + } + + private: + ExprPtr init_; + ReduceInteraction interaction_; +}; + +// An expression representing a Reduction operation (e.g. Sum, Max) broken into +// it's component parts: initialization, accumulation var, acquisition of value +// to be reduced and interaction. +// +// This is intended to be expanded in the loopnest and not make it to codegen. +class TORCH_API ReduceOp : public ExprNode { + public: + ReduceOp( + const ExprPtr& body, + std::vector reduce_args, + Reducer reducer) + : ExprNodeBase(body->dtype()), + body_(body), + reduce_args_(std::move(reduce_args)), + reducer_(std::move(reducer)) { + result_buf_ = nullptr; + acc_buf_ = nullptr; + ri_operand_ = nullptr; + } + + ReduceOp( + const ExprPtr& body, + std::vector reduce_args, + BufPtr result_buf, + BufPtr acc_buf, + ExprPtr ri_operand, + Reducer reducer) + : ExprNodeBase(body->dtype()), + body_(body), + reduce_args_(std::move(reduce_args)), + result_buf_(std::move(result_buf)), + acc_buf_(std::move(acc_buf)), + ri_operand_(std::move(ri_operand)), + reducer_(std::move(reducer)) {} + + static ExprHandle make( + ExprHandle body, + const std::vector& reduce_args, + const Reducer& reducer); + + static ExprHandle make( + ExprHandle body, + const std::vector& reduce_args, + BufHandle result_buf, + BufHandle acc_buf, + ExprHandle ri_operand, + const Reducer& reducer); + + // return the body expression which obtains the value to be reduced. + ExprPtr body() const { + return body_; + } + + // Returns the original Reducer factory that can create ReduceOps. + const Reducer& reducer() const { + return reducer_; + } + + // returns variables associated with the axes of reduction. + const std::vector& reduce_args() const { + return reduce_args_; + } + + void setAccBuf(BufHandle acc_buf) { + acc_buf_ = acc_buf.node(); + } + BufPtr getAccBuf() { + return acc_buf_; + } + + void setResultBuf(BufHandle buf) { + result_buf_ = buf.node(); + } + BufPtr getResultBuf() { + return result_buf_; + } + + void setRiOperand(ExprHandle ri_operand) { + ri_operand_ = ri_operand.node(); + } + ExprPtr getRiOperand() { + return ri_operand_; + } + + private: + // body_ = reducer_->interaction_(result_buf_, ri_operand_) + ExprPtr body_; + std::vector reduce_args_; + + BufPtr result_buf_; + BufPtr acc_buf_; + ExprPtr ri_operand_; + + const Reducer reducer_; +}; + +class Sum : public Reducer { + public: + Sum() + : Reducer(ExprHandle(0), [](const ExprHandle& a, const ExprHandle& b) { + return a + b; + }) {} +}; + +inline ExprHandle maximumVal(ScalarType type) { + switch (type) { +#define MAX_BY_TYPE_CASE(Type, Name) \ + case ScalarType::Name: \ + return ExprHandle(std::numeric_limits::max()); + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, MAX_BY_TYPE_CASE) +#undef MAX_BY_TYPE_CASE + default: + throw unsupported_dtype(); + } + return ExprHandle(); +} + +inline ExprHandle minimumVal(ScalarType type) { + switch (type) { +#define MAX_BY_TYPE_CASE(Type, Name) \ + case ScalarType::Name: \ + return ExprHandle(std::numeric_limits::min()); + AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, MAX_BY_TYPE_CASE) +#undef MAX_BY_TYPE_CASE + default: + throw unsupported_dtype(); + } +} + +class Maximum : public Reducer { + public: + // TODO possible to remove this arg by deferring the init value until we + // know the dtype of the body. + Maximum(Dtype dtype) + : Reducer( + minimumVal(dtype.scalar_type()), + [](const ExprHandle& a, const ExprHandle& b) { + return Max::make(a, b, true); + }) {} + Maximum(ExprHandle initializer) + : Reducer( + std::move(initializer), + [](const ExprHandle& a, const ExprHandle& b) { + return Max::make(a, b, true); + }) {} +}; + +class Minimum : public Reducer { + public: + Minimum(Dtype dtype) + : Reducer( + maximumVal(dtype.scalar_type()), + [](const ExprHandle& a, const ExprHandle& b) { + return Min::make(a, b, true); + }) {} + Minimum(const ExprHandle& initializer) + : Reducer(initializer, [](const ExprHandle& a, const ExprHandle& b) { + return Min::make(a, b, true); + }) {} +}; + +class ReductionExpander : public IRMutator { + public: + StmtPtr expand(const StmtPtr& s) { + return s->accept_mutator(this); + } + + ExprPtr mutate(const ReduceOpPtr& v) override { + return v->body(); + } +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/registerizer.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/registerizer.h new file mode 100644 index 0000000000000000000000000000000000000000..752537bb089953df9abcbb86568ee8b2a69b8f57 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/registerizer.h @@ -0,0 +1,426 @@ +#pragma once +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace torch::jit::tensorexpr { +namespace registerizer { + +/* The Registerizer performs scalar replacement by looking for common Stores and +Loads to a single item in a buffer and replacing them with a local temporary +scalar which is cheaper to write. + +For example it can replace: + +{ + A[0] = 0; + for(const auto x : c10::irange(10)) { + A[0] = (A[0]) + x; + } +} + +with: + +{ + int A_ = 0; + for(const auto x : c10::irange(10)) { + A_ = x + A_; + } + A[0] = A_; +} + +This is particularly useful on GPUs when parallelizing, since after replacing +loops with metavars we have a lot of accesses like this. */ + +class Scope; + +/* Holds analysis information about accesses to a specific range of a + buffer, including the number of loads and stores and the lowest common parent + Block. + */ +class AccessInfo { + public: + AccessInfo() = default; + AccessInfo( + SimplifierHashType h, + BufPtr b, + std::vector i, + size_t accessOrder) + : hash_(h), + buf_(std::move(b)), + indices_(std::move(i)), + store_cost_(alloc(0)), + load_cost_(alloc(0)), + accessOrder_(accessOrder) {} + + // Adds a Store to this access, which is in the provided scope. + void addStore(const StorePtr& store, const std::shared_ptr& scope); + + // Adds a Load to this access, which occurs in the usage Stmt in the provided + // scope. + void addLoad( + const LoadPtr& load, + const std::shared_ptr& scope, + const StmtPtr& usage); + + // Merge another AccessInfo into this one. + void merge(const std::shared_ptr& other); + + // Returns true if the other AccessInfo's bounds may overlap this one. + bool overlaps(const std::shared_ptr& other); + + // Returns true if the indices of this access depend on the provided Var. + bool dependsOnVar(const VarPtr& v); + + // Clone this AccessInfo, and set this as the new accesses' hiddenAccess. + static std::shared_ptr cloneWithHiddenInfo( + const std::shared_ptr& orig); + + // print for debugging. + void print() const; + + SimplifierHashType hash() const { + return hash_; + } + + BufPtr buf() const { + return buf_; + } + + const std::vector& indices() const { + return indices_; + } + + BlockPtr block() const { + return block_; + } + + void setEnclosingBlock(BlockPtr b) { + block_ = std::move(b); + } + + StmtPtr first_usage() const { + return first_usage_; + } + StmtPtr last_usage() const { + return last_usage_; + } + + void setUsageMarks(StmtPtr first, StmtPtr last) { + first_usage_ = std::move(first); + last_usage_ = std::move(last); + } + + bool firstUsageOverlapped() const { + return firstUsageOverlapped_; + } + + ExprPtr store_cost() const { + return store_cost_; + } + + ExprPtr load_cost() const { + return load_cost_; + } + + const std::vector& stores() const { + return stores_; + } + + const std::vector& loads() const { + return loads_; + } + + void hoistCosts(const ExprPtr& extent) { + store_cost_ = IRSimplifier::simplify(alloc(store_cost_, extent)); + load_cost_ = IRSimplifier::simplify(alloc(load_cost_, extent)); + } + + size_t conditionId() const { + return conditionId_; + } + + void setConditionId(size_t c) { + conditionId_ = c; + } + + size_t accessOrder() const { + return accessOrder_; + } + + std::shared_ptr hiddenAccess() const { + return hiddenAccess_; + } + + // Holds state relating to the scalar variable we will insert to replace some + // number of loads and stores. + struct ScalarReplacement { + VarPtr var{nullptr}; + BufPtr var_wrapper{nullptr}; + LetPtr initializer{nullptr}; + }; + + ScalarReplacement& replacement() { + return replacement_; + } + + private: + SimplifierHashType hash_; + BufPtr buf_; + std::vector indices_; + BlockPtr block_{nullptr}; + + StmtPtr first_usage_{nullptr}; + StmtPtr last_usage_{nullptr}; + + // Whether or not this access is overlapped in the first Stmt it appears. This + // means we cannot use it's first Store as the initializer. + bool firstUsageOverlapped_{false}; + + // The cost in real ops that this access represents, to enable + // filtering accesses that wont save any loads or stores. + ExprPtr store_cost_; + ExprPtr load_cost_; + + // The actual Stores and Loads which represent this access. + // Be careful with these, any mutator will invalidate these pointers. + std::vector stores_; + std::vector loads_; + + // An identifier representing the conditional block, if any, this access + // depends on. + size_t conditionId_{0}; + + // An identifier representing the order this access was first encountered, for + // sorting returned results. + size_t accessOrder_{0}; + + // Sometimes when traversing the tree we need to record what would happen if + // we hoisted an access, but sometimes it doesn't work out. This lets us + // "undo" some mutation and return to the internal hidden AccessInfo. + // It will be removed after any further additions to this AccessInfo. + std::shared_ptr hiddenAccess_; + + ScalarReplacement replacement_; +}; + +using AccessHashMap = + std::unordered_map>; + +// Represents a scope block and holds all accesses contained within it. +class Scope { + public: + Scope(BlockPtr b, std::shared_ptr parent, size_t conditionId = 0) + : block_(std::move(b)), + parent_(std::move(parent)), + conditionId_(conditionId) {} + + AccessHashMap& getAccessMapByBuf(const BufPtr& b); + + std::unordered_map& openAccesses() { + return openAccesses_; + } + + std::vector>& closedAccesses() { + return closedAccesses_; + } + + BlockPtr block() const { + return block_; + } + + std::shared_ptr parent() const { + return parent_; + } + + size_t conditionId() const { + return conditionId_; + } + + const std::unordered_set& localVars() const { + return localVars_; + } + void addLocalVar(VarPtr v) { + localVars_.insert(std::move(v)); + } + + void closeAccess(const std::shared_ptr& info); + + void filterClosed(); + + private: + // Map of map to access, narrowing by Buf then by hash(Buf+Indices). + // This allows us to find a candidate access easily, and also check for + // overlap with other accesses to the same buf. Buf -> + // Hash -> + // Access + std::unordered_map openAccesses_; + std::vector> closedAccesses_; + + // The Block object this scope represents. + BlockPtr block_; + + // The enclosing scope object. + std::shared_ptr parent_; + + // An identifier representing the condition block this scope depends on. + size_t conditionId_; + + // A set of variables local to this scope (e.g. loop vars). + std::unordered_set localVars_; +}; + +/* Analyzes the graph and collects accesses to the same symbolic tensor element + * which can be replaced by a single local scalar. + * + * This works by recursively walking the tree in postfix order, building sets of + * accesses to the same symbolic element by scope and then merging lower scopes + * into their enclosing scope. + * + * It is safe to move two accesses of the same Tensor element to a local scalar + * Var if between all usages of the element there are no other Loads or Stores + * that may refer to it. In the comments I refer to this as overlapping the + * access, or "cutting" the existing AccessInfo. In the case where a candidate + * for registerization is cut, it may be possible to finalize the access early + * by writing it back to the Tensor and then create a new scalar variable after + * the overlapping access is complete. We will attempt to do this when it saves + * memory accesses. + * + * There are a few cases that make this more challenging: + * + * - For: Loops change the number of real usages of a buffer by the loop + * extent, but only if we can pull the definition and finalization of the scalar + * variable out of the loop block. + * + * - Cond: Conditions complicate lifting scalars out of internal scopes. + * Generally we cannot lift an access outside of a conditional scope unless + * there is already a reference to that same access at the higher scope, since + * we don't know if the condition was guarding an array access not safe at the + * higher scope. In the comments I refer to this as the condition "hiding" the + * access, and the outer access "unhiding" it. + * + * - IfThenElse: Same situation as Cond, except since IfThenElse is an Expr + * rather than a Stmt we cannot insert the scalar definition or finalizer + * within the conditional scope. Accesses inside an IfThenElse can be safely + * combined with external accesses but cannot exist completely within. + * + * - Let: Accesses dependent on local variables via Let Stmts, or loop vars, + * cannot be raised outside of the scope of the dependent var. + */ +class TORCH_API RegisterizerAnalysis : public IRVisitor { + public: + RegisterizerAnalysis() + : currentScope_(std::make_shared(nullptr, nullptr, 0)) {} + ~RegisterizerAnalysis() override = default; + + void visit(const ForPtr& v) override; + + void visit(const CondPtr& v) override; + + void visit(const BlockPtr& v) override; + + void visit(const StorePtr& v) override; + + void visit(const LoadPtr& v) override; + + void visit(const IfThenElsePtr& v) override; + + void visit(const LetPtr& v) override; + +#define STMT_ON_STACK(Op) \ + void visit(const Op##Ptr& v) override { \ + stmtStack_.push_front(v); \ + IRVisitor::visit(v); \ + stmtStack_.pop_front(); \ + } + + STMT_ON_STACK(AtomicAdd) + STMT_ON_STACK(Allocate) + STMT_ON_STACK(Free) + +#undef STMT_ON_STACK + + std::vector> getCandidates(); + + private: + void mergeCurrentScopeIntoParent(); + void mergeHiddenScope(bool allowClosed); + void closeAccessIntoScope( + const std::shared_ptr& info, + const std::shared_ptr& scope); + + std::unordered_set exprConditionals_; + + // A stack of enclosing Stmts for tracking the usage Stmt of Loads. + std::deque stmtStack_; + + // The current scope being analyzed. + std::shared_ptr currentScope_; + + HashProvider hasher_; + + size_t conditionId_{0}; + size_t accessOrder_{0}; +}; + +/* Replaces each registerizable access with a Scalar variable, including + * definition, initializer and finalizer. + */ +class TORCH_API RegisterizerReplacer : public IRMutator { + public: + RegisterizerReplacer(std::vector>& vec) + : infoSet_(vec) { + buildReplacements(); + } + + ExprPtr mutate(const LoadPtr& v) override; + + StmtPtr mutate(const StorePtr& v) override; + + StmtPtr mutate(const BlockPtr& v) override; + + private: + struct ReplacerScope { + std::unordered_map>> + initializerPoints_; + std::unordered_map>> + finalizePoints_; + }; + + // Creates the various ReplacerScope objects and builds internal maps. + void buildReplacements(); + + // State relating to the accesses yet to be replaced. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + std::vector>& infoSet_; + std::unordered_map> storeToAccess_; + std::unordered_map> loadToAccess_; + std::unordered_map parentToAccesses_; + + // Holds the set of Stores that should be pulled into an initializer, so they + // can be eliminated. + std::set eliminatedIntializers_; + + // Tracks the number of times we've seen each buffer, so we can name the + // scalar Vars appropriately. + std::unordered_map bufferAccessCounts_; + unsigned int getBufferAccessCount(const BufPtr& b) { + return ++bufferAccessCounts_[b]; + } +}; +} // namespace registerizer + +// Apply scalar replacement to all accesses in s. +// To produce safe code, this must occur after handling parallelized axes and +// atomics. +TORCH_API StmtPtr registerize(StmtPtr s); + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/stmt.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/stmt.h new file mode 100644 index 0000000000000000000000000000000000000000..62bde5c4c33ea1622caa1ab82443a4990a08c1e1 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/stmt.h @@ -0,0 +1,1009 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +// The common base between all statement node. +class TORCH_API Stmt : public std::enable_shared_from_this { + public: + Stmt() = default; + virtual ~Stmt() = default; + virtual void accept(IRVisitor* visitor) = 0; + virtual StmtPtr accept_mutator(IRMutator* mutator) = 0; + + StmtPtr get_parent() const { + return parent_ ? parent_->getptr() : nullptr; + } + + /* + * Make a deep copy of the given statement. + * + * All statements and expressions used in children of the statement are + * cloned. Note that the variables are not deep-copied since they are + * immutable. + */ + static StmtPtr clone(const StmtPtr& s); + + protected: + static void set_parent(const StmtPtr& s, Stmt* new_parent) { + s->parent_ = new_parent; + } + std::shared_ptr getptr() { + return shared_from_this(); + } + + private: + Stmt* parent_ = nullptr; +}; + +template +class StmtNode : public Stmt { + public: + using StmtNodeBase = StmtNode; + void accept(IRVisitor* visitor) override { + visitor->visit(static_to(getptr())); + } + StmtPtr accept_mutator(IRMutator* mutator) override; + StmtNode() = default; +}; + +template +StmtPtr StmtNode::accept_mutator(IRMutator* mutator) { + return mutator->mutate(static_to(getptr())); +} + +// Concrete Stmt classes +class TORCH_API Block : public StmtNode { + public: + static BlockPtr make(const std::vector& stmts) { + std::vector valid_stmts; + for (auto& stmt : stmts) { + if (!stmt) { + continue; + } + valid_stmts.push_back(stmt); + } + if (valid_stmts.empty()) { + return nullptr; + } + return alloc(valid_stmts); + } + + size_t nstmts() const { + return stmts_.size(); + } + bool empty() const { + return stmts_.empty(); + } + + void prepend_stmt(const StmtPtr& s) { + if (s->get_parent()) { + throw malformed_input("Block prepend Stmt with existing parent", s); + } + + stmts_.push_front(s); + set_parent(s, this); + } + void append_stmt(const StmtPtr& s) { + if (s->get_parent()) { + throw malformed_input("Block append Stmt with existing parent", s); + } + + stmts_.push_back(s); + set_parent(s, this); + } + + void insert_stmt_before(const StmtPtr& s, const StmtPtr& before) { + if (s->get_parent()) { + throw malformed_input("Block append Stmt with existing parent", s); + } + + auto pos = std::find(stmts_.begin(), stmts_.end(), before); + if (pos == stmts_.end()) { + throw malformed_input( + "Inserting after statement that is not in block", s); + } + + stmts_.insert(pos, s); + set_parent(s, this); + } + + void insert_stmt_after(const StmtPtr& s, const StmtPtr& after) { + if (s->get_parent()) { + throw malformed_input("Block append Stmt with existing parent", s); + } + + auto pos = std::find(stmts_.begin(), stmts_.end(), after); + if (pos == stmts_.end()) { + throw malformed_input( + "Inserting after statement that is not in block", s); + } + + ++pos; + + stmts_.insert(pos, s); + set_parent(s, this); + } + + bool replace_stmt(const StmtPtr& old_stmt, const StmtPtr& new_stmt) { + if (new_stmt->get_parent()) { + throw malformed_input( + "Block replace Stmt with existing parent", new_stmt); + } + + auto pos = std::find(stmts_.begin(), stmts_.end(), old_stmt); + if (pos == stmts_.end()) { + return false; + } + stmts_.insert(pos, new_stmt); + stmts_.erase(pos); + set_parent(old_stmt, nullptr); + set_parent(new_stmt, this); + return true; + } + + // Creates a new block by cloning `this` block and replacing the given + // statement with a new statement. Note that `old_stmt` refers to a statement + // in `this` block. If the `old_stmt` is not found, it will return `nullptr`. + BlockPtr clone_and_replace(const StmtPtr& old_stmt, const StmtPtr& new_stmt) { + if (new_stmt->get_parent()) { + throw malformed_input( + "Block replace Stmt with existing parent", new_stmt); + } + + std::vector stmts(stmts_.begin(), stmts_.end()); + std::vector cloned_stmts(stmts.size()); + bool found = false; + for (int i = 0; i < static_cast(stmts.size()); ++i) { + if (stmts[i] == old_stmt) { + found = true; + cloned_stmts[i] = new_stmt; + } else { + cloned_stmts[i] = Stmt::clone(stmts[i]); + } + } + if (!found) { + return nullptr; + } + return alloc(cloned_stmts); + } + + bool remove_stmt(const StmtPtr& stmt) { + auto pos = std::find(stmts_.begin(), stmts_.end(), stmt); + if (pos == stmts_.end()) { + return false; + } + + set_parent(stmt, nullptr); + stmts_.erase(pos); + return true; + } + + std::list stmts() const { + return stmts_; + } + + void clear() { + for (const auto& s : stmts_) { + set_parent(s, nullptr); + } + stmts_.clear(); + } + + void set_stmts(const std::vector& stmts) { + clear(); + init(stmts); + } + + explicit Block(const std::vector& stmts) { + init(stmts); + } + + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; + + iterator begin() { + return stmts_.begin(); + } + + const_iterator begin() const { + return stmts_.begin(); + } + + iterator end() { + return stmts_.end(); + } + + const_iterator end() const { + return stmts_.end(); + } + + StmtPtr front() { + return stmts_.front(); + } + + StmtPtr front() const { + return stmts_.front(); + } + + StmtPtr back() { + return stmts_.back(); + } + + StmtPtr back() const { + return stmts_.back(); + } + + void splice(Block::iterator it, const BlockPtr& other) { + for (const StmtPtr& s : *other) { + set_parent(s, this); + } + + stmts_.splice(it, other->stmts_); + } + + static BlockPtr getSharedParent(StmtPtr p1, StmtPtr p2) { + std::unordered_set enclosing; + + StmtPtr p1_p = std::move(p1); + while (p1_p) { + if (BlockPtr b = to(p1_p)) { + enclosing.insert(b); + } + p1_p = p1_p->get_parent(); + } + + StmtPtr p2_p = std::move(p2); + while (p2_p) { + if (BlockPtr b = to(p2_p)) { + if (enclosing.count(b) != 0) { + return b; + } + } + p2_p = p2_p->get_parent(); + } + + return nullptr; + } + + // returns the immediate child containing statement s. + StmtPtr getEnclosedRoot(StmtPtr s) const { + while (s && s->get_parent().get() != this) { + s = s->get_parent(); + } + return s; + } + + private: + std::list stmts_; + + void init(const std::vector& stmts) { + for (const StmtPtr& s : stmts) { + if (!s) { + continue; + } + if (!s->get_parent()) { + // If we get here, it's a bug, but we cannot throw an error from a + // constructor. But IR verifier would catch this. + set_parent(s, this); + } + + stmts_.push_back(s); + } + } +}; + +class TORCH_API Store : public StmtNode { + public: + VarPtr base_handle() const { + return buf_->base_handle(); + } + std::vector indices() const { + return indices_; + } + ExprPtr flat_index() const { + TORCH_CHECK(indices_.size() == 1, "Indices haven't been flattened."); + return indices_[0]; + } + ExprPtr value() const { + return value_; + } + BufPtr buf() const { + return buf_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + void set_indices(std::vector indices) { + indices_ = std::move(indices); + } + + void set_value(ExprPtr value) { + value_ = std::move(value); + } + + static StorePtr make( + const BufHandle& buf, + const std::vector& indices, + const ExprHandle& value); + + Store(BufPtr buf, std::vector indices, ExprPtr value); + + private: + BufPtr buf_; + std::vector indices_; + ExprPtr value_; +}; + +// Allocate a buffer of given shapes and dtypes and bind it with the given +// buffer var. The life span is at most through the current program, until it is +// explicitly freed. An unfreed memory is likely considered an error. +class TORCH_API Allocate : public StmtNode { + public: + static AllocatePtr make(const BufHandle& buf_handle) { + return alloc(buf_handle.node()); + } + + VarPtr buffer_var() const { + return buf_->base_handle(); + } + + Dtype dtype() const { + return buf_->dtype(); + } + + const std::vector dims() const { + return buf_->dims(); + } + + BufPtr buf() const { + return buf_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + explicit Allocate(BufPtr buf) : buf_(std::move(buf)) {} + + private: + BufPtr buf_; + // TODO: add memory types. +}; + +// PlacementAllocate is a variation of the Allocate operator in NNC IR. It does +// not allocate memory but reuse the memory of another buffer for the given +// buffer. +class TORCH_API PlacementAllocate : public StmtNode { + public: + static PlacementAllocatePtr make( + const BufHandle& buf_handle, + const BufHandle& buf_handle_to_reuse) { + return alloc( + buf_handle.node(), buf_handle_to_reuse.node()); + } + + BufPtr buf() const { + return buf_; + } + + BufPtr buf_to_reuse() const { + return buf_to_reuse_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + void set_buf_to_reuse(BufPtr buf) { + buf_to_reuse_ = std::move(buf); + } + + explicit PlacementAllocate(BufPtr buf, BufPtr buf_to_reuse) + : buf_(std::move(buf)), buf_to_reuse_(std::move(buf_to_reuse)) {} + + private: + BufPtr buf_; + BufPtr buf_to_reuse_; +}; + +// Free the specific buffer. It is an error. +class TORCH_API Free : public StmtNode { + public: + static FreePtr make(const BufHandle& buf_handle) { + return alloc(buf_handle.node()); + } + + VarPtr buffer_var() const { + return buf_->base_handle(); + } + + BufPtr buf() const { + return buf_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + explicit Free(BufPtr buf) : buf_(std::move(buf)) {} + + private: + BufPtr buf_; +}; + +class TORCH_API FreeExt : public StmtNode { + public: + static FreeExtPtr make(const std::vector& bufs); + + std::vector bufs() const { + return bufs_; + } + + void set_bufs(std::vector bufs) { + bufs_ = std::move(bufs); + } + + explicit FreeExt(std::vector bufs) : bufs_(std::move(bufs)) {} + + private: + std::vector bufs_; +}; + +class TORCH_API Let : public StmtNode { + public: + static LetPtr make(const VarHandle& var, const ExprHandle& val) { + return alloc(var.node(), val.node()); + } + + Let(VarPtr var, ExprPtr val) : var_(std::move(var)), val_(std::move(val)) {} + + VarPtr var() const { + return var_; + } + + ExprPtr value() const { + return val_; + } + + void set_var(VarPtr var) { + var_ = std::move(var); + } + + void set_val(ExprPtr val) { + val_ = std::move(val); + } + + private: + VarPtr var_; + ExprPtr val_; +}; + +class TORCH_API Cond : public StmtNode { + public: + static CondPtr make( + const ExprHandle& condition, + const StmtPtr& true_stmt, + const StmtPtr& false_stmt) { + return alloc(condition.node(), true_stmt, false_stmt); + } + + ExprPtr condition() const { + return condition_; + } + + BlockPtr true_stmt() const { + return true_stmt_; + } + + BlockPtr false_stmt() const { + return false_stmt_; + } + + void set_condition(ExprPtr condition) { + condition_ = std::move(condition); + } + + void set_true_stmt(StmtPtr true_stmt) { + if (true_stmt) { + BlockPtr b = to(true_stmt); + if (!b) { + b = alloc(std::vector({std::move(true_stmt)})); + } + true_stmt_ = b; + set_parent(true_stmt_, this); + } + } + + void set_false_stmt(StmtPtr false_stmt) { + if (false_stmt) { + BlockPtr b = to(false_stmt); + if (!b) { + b = alloc(std::vector({std::move(false_stmt)})); + } + false_stmt_ = b; + set_parent(false_stmt_, this); + } + } + + Cond(ExprPtr condition, StmtPtr true_stmt, StmtPtr false_stmt) + : condition_(std::move(condition)) { + set_true_stmt(std::move(true_stmt)); + set_false_stmt(std::move(false_stmt)); + } + + CondPtr cloneWithNewBodies( + const StmtPtr& true_stmt, + const StmtPtr& false_stmt) { + return alloc(condition_, true_stmt, false_stmt); + } + + CondPtr cloneWithNewBody(const StmtPtr& true_stmt) { + return alloc(condition_, true_stmt, nullptr); + } + + private: + ExprPtr condition_; + BlockPtr true_stmt_ = nullptr; + BlockPtr false_stmt_ = nullptr; +}; + +class TORCH_API LoopOptions { + public: + enum { + IDX_UNSET = -1, + IDX_X = 0, + IDX_Y = 1, + IDX_Z = 2, + IDX_W = 3, + IDX_MAX = IDX_W, + }; + // GPU Block Index + bool is_gpu_block_index() const { + return gpu_block_index_ != IDX_UNSET; + } + + int gpu_block_index() const { + return gpu_block_index_; + } + + std::string gpu_block_index_str() const { + if (!is_gpu_block_index()) { + throw malformed_input("Has no GPU block index"); + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + static const char* kBlockIndexNames[] = { + "blockIdx.x", + "blockIdx.y", + "blockIdx.z", + "blockIdx.w", + }; + + if (gpu_block_index_ < IDX_X || gpu_block_index_ > IDX_MAX) { + throw malformed_input("invalid GPU block index"); + } + + return kBlockIndexNames[gpu_block_index_]; + } + + void set_gpu_block_index(int index) { + if (index == IDX_UNSET) { + gpu_block_index_ = IDX_UNSET; + } + + if (is_gpu_thread_index()) { + throw std::runtime_error("Cannot set both gpu block and thread index"); + } + if (is_gpu_block_index() && gpu_block_index() != index) { + throw std::runtime_error("Cannot set a previously set block index"); + } + gpu_block_index_ = index; + } + + // GPU Thread Index + bool is_gpu_thread_index() const { + return gpu_thread_index() != IDX_UNSET; + } + + int gpu_thread_index() const { + return gpu_thread_index_; + } + + std::string gpu_thread_index_str() const { + if (!is_gpu_thread_index()) { + throw malformed_input("has no GPU thread index"); + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + static const char* kThreadIndexNames[] = { + "threadIdx.x", "threadIdx.y", "threadIdx.z", "threadIdx.w"}; + + if (gpu_thread_index_ < IDX_X || gpu_thread_index_ > IDX_MAX) { + throw malformed_input("invalid GPU thread index"); + } + + return kThreadIndexNames[gpu_thread_index_]; + } + + void set_gpu_thread_index(int index) { + if (index == IDX_UNSET) { + gpu_thread_index_ = IDX_UNSET; + } + + if (is_gpu_block_index()) { + throw std::runtime_error("Cannot set both gpu thread and block index"); + } + if (is_gpu_thread_index() && gpu_thread_index() != index) { + throw std::runtime_error("Cannot set a previously set thread index"); + } + gpu_thread_index_ = index; + } + + void set_parallel() { + is_parallel_ = true; + } + + bool is_parallel() const { + return is_parallel_; + } + + std::string ToString() const { + if (is_gpu_block_index()) { + return gpu_block_index_str(); + } else if (is_gpu_thread_index()) { + return gpu_thread_index_str(); + } else if (is_parallel()) { + return "parallel"; + } + return ""; + } + + bool isDefault() const { + return gpu_block_index_ == IDX_UNSET && gpu_thread_index_ == IDX_UNSET && + !is_parallel_; + } + + void set_buffer_mapping(const std::unordered_map& map) { + map_input_to_tensor_bufs_ = map; + } + + std::unordered_map get_buffer_mapping() const { + return map_input_to_tensor_bufs_; + } + + private: + int gpu_block_index_{IDX_UNSET}; + int gpu_thread_index_{IDX_UNSET}; + bool is_parallel_{false}; + std::unordered_map map_input_to_tensor_bufs_; +}; + +class TORCH_API For : public StmtNode { + public: + VarPtr var() const { + return var_; + } + ExprPtr start() const { + return start_; + } + ExprPtr stop() const { + return stop_; + } + BlockPtr body() const { + return body_; + } + static ForPtr make( + const VarHandle& var, + const ExprHandle& start, + const ExprHandle& stop, + const StmtPtr& body) { + if (!body) { + return nullptr; + } + return alloc(var.node(), start.node(), stop.node(), body); + } + static ForPtr make( + const VarHandle& var, + const ExprHandle& start, + const ExprHandle& stop, + const StmtPtr& body, + const LoopOptions& loop_options) { + if (!body) { + return nullptr; + } + return alloc( + var.node(), start.node(), stop.node(), body, loop_options); + } + const LoopOptions loop_options() const { + return loop_options_; + } + + For(VarPtr var, ExprPtr start, ExprPtr stop, StmtPtr body) + : var_(std::move(var)), start_(std::move(start)), stop_(std::move(stop)) { + BlockPtr b = to(body); + if (!b) { + b = alloc(std::vector({std::move(body)})); + } + body_ = b; + set_parent(body_, this); + } + + For(VarPtr var, + ExprPtr start, + ExprPtr stop, + StmtPtr body, + LoopOptions loop_options) + : var_(std::move(var)), + start_(std::move(start)), + stop_(std::move(stop)), + loop_options_(std::move(loop_options)) { + if (!var_) { + throw malformed_input("invalid Var in For loop"); + } else if (!start_) { + throw malformed_input("invalid Start in For loop"); + } else if (!stop_) { + throw malformed_input("invalid Stop in For loop"); + } else if (!body || body->get_parent()) { + throw malformed_input("invalid Body in For loop"); + } + + BlockPtr b = to(body); + if (!b) { + b = alloc(std::vector({std::move(body)})); + } + body_ = b; + set_parent(body_, this); + } + + void set_gpu_block_index(int block_index) { + loop_options_.set_gpu_block_index(block_index); + } + + void set_gpu_thread_index(int thread_index) { + loop_options_.set_gpu_thread_index(thread_index); + } + + void set_parallel() { + loop_options_.set_parallel(); + } + + bool is_parallel() const { + return loop_options_.is_parallel(); + } + + void set_buffer_map(const std::unordered_map& map) { + loop_options_.set_buffer_mapping(map); + } + + ForPtr cloneWithNewBody(const StmtPtr& body) const { + return alloc(var_, start_, stop_, body, loop_options_); + } + + BlockPtr removeBody() { + auto res = body_; + set_parent(res, nullptr); + body_ = nullptr; + return res; + } + + void set_body(StmtPtr body) { + BlockPtr b = to(body); + if (!b) { + b = alloc(std::vector({std::move(body)})); + } + body_ = b; + set_parent(body_, this); + } + + void set_start(ExprPtr start) { + start_ = std::move(start); + } + + void set_stop(ExprPtr stop) { + stop_ = std::move(stop); + } + + void set_var(VarPtr var) { + var_ = std::move(var); + } + + private: + VarPtr var_; + ExprPtr start_; + ExprPtr stop_; + BlockPtr body_; + LoopOptions loop_options_; +}; + +// A backend specific IR Node that implements atomic-add. +// This node could only shows up as an internal with GPU backends. +// TODO: move to this an internal IR. +// TODO: make IR nodes extensible. +class TORCH_API AtomicAdd : public StmtNode { + public: + AtomicAdd(BufPtr buf, std::vector indices, ExprPtr value) + : buf_(std::move(buf)), + indices_(std::move(indices)), + value_(std::move(value)) {} + + VarPtr base_handle() const { + return buf_->base_handle(); + } + + BufPtr buf() const { + return buf_; + } + + ExprPtr flat_index() const { + TORCH_CHECK(indices_.size() == 1, "Indices haven't been flattened."); + return indices_[0]; + } + + ExprPtr value() const { + return value_; + } + + const std::vector& indices() const { + return indices_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + void set_indices(std::vector indices) { + indices_ = std::move(indices); + } + + void set_value(ExprPtr value) { + value_ = std::move(value); + } + + private: + BufPtr buf_; + std::vector indices_; + ExprPtr value_; +}; + +class TORCH_API SyncThreads : public StmtNode { + public: + SyncThreads() = default; +}; + +/* + * ExternalCall statement represents a call to an external function that would + * compute the contents of the output buffer. An ExternalCall statement consists + * of: + * 1) output buffer - the buffer that'll be initialized by the call + * 2) external function name - a key from the NNC function registry to lookup + * the actual function to call + * 3) buffer arguments - the input buffers used by the function + * 4) non-buffer arguments - scalar arguments to pass to the function + * + * An example: + * A = nnc_conv2d(buf_args={Input, Weight, Bias}, args={1}) + * Here 'A' is the output buffer, "nnc_conv2d" is the function name, the buffer + * arguments are 'Input', 'Weight', and 'Bias', and there is a single non-buffer + * argument - 1. + * + * The semantics of the scalar arguments is defined solely by the implementation + * of the external function. + */ +class TORCH_API ExternalCall : public StmtNode { + public: + static ExternalCallPtr make( + BufHandle buf, + const std::string& func_name, + const std::vector& buf_args, + const std::vector& args); + + BufPtr buf() const { + return buf_; + } + + std::string func_name() const { + return func_name_; + } + + std::vector buf_args() const { + return buf_args_; + } + + std::vector args() const { + return args_; + } + + void set_buf(BufPtr buf) { + buf_ = std::move(buf); + } + + void set_buf_args(std::vector buf_args) { + buf_args_ = std::move(buf_args); + } + + void set_args(std::vector args) { + args_ = std::move(args); + } + + ExternalCall( + BufPtr buf, + std::string func_name, + std::vector buf_args, + std::vector args) + : buf_(std::move(buf)), + func_name_(std::move(func_name)), + buf_args_(std::move(buf_args)), + args_(std::move(args)) {} + + private: + BufPtr buf_; + std::string func_name_; + std::vector buf_args_; + std::vector args_; +}; + +class TORCH_API ExternalCallWithAlloc : public StmtNode { + public: + static ExternalCallWithAllocPtr make( + const std::string& func_name, + const std::vector& buf_out_args, + const std::vector& buf_args, + const std::vector& args); + + std::vector buf_out_args() const { + return buf_out_args_; + } + + std::string func_name() const { + return func_name_; + } + + std::vector buf_args() const { + return buf_args_; + } + + std::vector args() const { + return args_; + } + + void set_buf_out_args(std::vector buf_out_args) { + buf_out_args_ = std::move(buf_out_args); + } + + void set_buf_args(std::vector buf_args) { + buf_args_ = std::move(buf_args); + } + + void set_args(std::vector args) { + args_ = std::move(args); + } + + ExternalCallWithAlloc( + std::string func_name, + std::vector buf_out_args, + std::vector buf_args, + std::vector args) + : func_name_(std::move(func_name)), + buf_out_args_(std::move(buf_out_args)), + buf_args_(std::move(buf_args)), + args_(std::move(args)) {} + + private: + std::string func_name_; + std::vector buf_out_args_; + std::vector buf_args_; + std::vector args_; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensor.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensor.h new file mode 100644 index 0000000000000000000000000000000000000000..c7b2e65b44771b5ac15d01910b9d20757d61d0e8 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensor.h @@ -0,0 +1,321 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace torch::jit::tensorexpr { + +class TORCH_API Tensor { + public: + Tensor(BufPtr buf, const std::vector& args, const ExprPtr& body) + : buf_(std::move(buf)) { + stmt_ = constructStmt(args, body, {}, {}); + } + Tensor(BufHandle buf, const std::vector& args, ExprHandle body) + : Tensor(buf.node(), VarHandleVectorToVarVector(args), body.node()) {} + + Tensor( + BufPtr buf, + const std::vector& args, + const std::vector& reduce_dims, + const std::vector& reduce_args, + const ExprPtr& body) + : buf_(std::move(buf)) { + stmt_ = constructStmt(args, body, reduce_dims, reduce_args); + } + Tensor( + BufHandle buf, + const std::vector& args, + const std::vector& reduce_dims, + const std::vector& reduce_args, + ExprHandle body) + : Tensor( + buf.node(), + VarHandleVectorToVarVector(args), + ExprHandleVectorToExprVector(reduce_dims), + VarHandleVectorToVarVector(reduce_args), + body.node()) {} + + Tensor(BufPtr buf, StmtPtr stmt) + : buf_(std::move(buf)), stmt_(std::move(stmt)) {} + + BufPtr buf() const { + return buf_; + } + + StmtPtr stmt() const { + return stmt_; + } + + template + inline ExprHandle load(const std::vector& args) const; + template + inline ExprHandle load(const Ts&... ts) const; + + private: + StmtPtr constructStmt( + const std::vector& args, + const ExprPtr& body, + const std::vector& reduce_dims, + const std::vector& reduce_args) const; + + BufPtr buf_; + StmtPtr stmt_; +}; + +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const std::function& body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::function& body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const std::function& + body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::function& + body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const std::function< + ExprHandle(const VarHandle&, const VarHandle&, const VarHandle&)>& + body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::function< + ExprHandle(const VarHandle&, const VarHandle&, const VarHandle&)>& + body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const std::function& body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::function& body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const std::function&)>& body_func); +TORCH_API Tensor Compute( + const std::string& func_name, + const std::vector& dims, + const std::function&)>& body_func); + +inline std::vector create_index_vars( + const std::vector& dims) { + std::vector vars; + vars.reserve(dims.size()); + for (const ExprHandle& dim : dims) { + vars.emplace_back(alloc( + "i", dim.dtype().scalar_type() == ScalarType::Long ? kLong : kInt)); + } + return vars; +} + +// Handle reductions over a Reducer and a body_func which produces values. +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const Reducer& reducer, + const InitFunc& init_func, + const BodyFunc& body_func, + const std::vector& reduce_dims) { + std::vector vars = create_index_vars(dims); + std::vector reduce_vars = create_index_vars(reduce_dims); + + // If reduce_vars is empty, then it's not a reduction, but rather a simple + // copy + if (reduce_vars.empty()) { + ExprHandle body = Reducer::getReduceBody(body_func, vars); + BufHandle func_result = + Buf::make(func_name, dims, body.dtype(), std::nullopt, strides); + return Tensor(std::move(func_result), vars, std::move(body)); + } + + std::vector all_vars; + all_vars.insert(all_vars.end(), vars.begin(), vars.end()); + all_vars.insert(all_vars.end(), reduce_vars.begin(), reduce_vars.end()); + + ExprHandle body = Reducer::getReduceBody(body_func, all_vars); + std::vector output_args(vars.begin(), vars.end()); + ExprHandle init_expr = Cast::make(body.dtype(), init_func(vars)); + BufHandle func_result = Buf::make(func_name, dims, body.dtype(), init_expr); + + ExprHandle reduce_op = reducer(func_result, body, output_args, reduce_vars); + if (body.dtype() == kBFloat16) { + ExprHandle init_expr_acc = Cast::make(kFloat, init_func(vars)); + BufHandle func_result_acc = + Buf::make(func_name + "_acc", dims, kFloat, init_expr_acc); + reduce_op = reducer( + func_result, + std::move(func_result_acc), + body, + output_args, + reduce_vars); + } + + Tensor t = Tensor( + std::move(func_result), + vars, + reduce_dims, + reduce_vars, + std::move(reduce_op)); + return t; +} +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const Reducer& reducer, + const InitFunc& init_func, + const BodyFunc& body_func, + const std::vector& reduce_dims) { + return Reduce( + func_name, + dims, + std::nullopt, + reducer, + init_func, + body_func, + reduce_dims); +} + +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const Reducer& reducer, + const BodyFunc& body_func, + const std::vector& reduce_dims) { + return Reduce( + func_name, + dims, + strides, + reducer, + [&](ParameterList& p [[maybe_unused]]) { + return ExprHandle(reducer.initializer()); + }, + body_func, + reduce_dims); +} +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const Reducer& reducer, + const BodyFunc& body_func, + const std::vector& reduce_dims) { + return Reduce( + func_name, dims, std::nullopt, reducer, body_func, reduce_dims); +} + +// Overload which allows inline lambda functions for the body_func. +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const Reducer& reducer, + const BodyFunc&& body_func, + const std::vector& reduce_dims) { + return Reduce(func_name, dims, strides, reducer, body_func, reduce_dims); +} +template +Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const Reducer& reducer, + const BodyFunc&& body_func, + const std::vector& reduce_dims) { + return Reduce(func_name, dims, std::nullopt, reducer, body_func, reduce_dims); +} + +TORCH_API Tensor Reduce( + const std::string& name, + const std::vector& dims, + const std::optional>& strides, + const Reducer& reducer, + const BufHandle& buffer, + const std::vector& reduce_dims); +TORCH_API Tensor Reduce( + const std::string& name, + const std::vector& dims, + const Reducer& reducer, + const BufHandle& buffer, + const std::vector& reduce_dims); + +// Overload for the common case of all dimensions of a previously Computed +// Tensor. +TORCH_API Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const std::optional>& strides, + const Reducer& reducer, + const Tensor& tensor, + const std::vector& reduce_dims); +TORCH_API Tensor Reduce( + const std::string& func_name, + const std::vector& dims, + const Reducer& reducer, + const Tensor& tensor, + const std::vector& reduce_dims); + +template +inline ExprHandle Tensor::load(const Ts&... ts) const { + std::vector params({ExprHandle(ts)...}); + return Load::make(BufHandle(this->buf()), params); +} + +template +inline ExprHandle Tensor::load(const std::vector& args) const { + std::vector params(args.begin(), args.end()); + return Load::make(BufHandle(this->buf()), params); +} + +template +inline ExprHandle BufHandle::load(const Ts&... ts) const { + std::vector params({ExprHandle(ts)...}); + return ExprHandle(alloc(node(), ExprHandleVectorToExprVector(params))); +} + +template +inline ExprHandle BufHandle::load(const std::vector& args) const { + std::vector params(args.begin(), args.end()); + return ExprHandle(alloc(node(), ExprHandleVectorToExprVector(params))); +} + +inline ExprHandle BufHandle::load(const std::vector& args) const { + return this->template load(args); +} + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensorexpr_init.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensorexpr_init.h new file mode 100644 index 0000000000000000000000000000000000000000..1dc38660615824654df2b5f5b0d6a426eefc263a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/tensorexpr_init.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace torch::jit { +// Initialize Python bindings for Tensor Expressions +void initTensorExprBindings(PyObject* module); +} // namespace torch::jit diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/types.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/types.h new file mode 100644 index 0000000000000000000000000000000000000000..cd23fdce4ae98e169607045dc217b308eea23f8a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/types.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include + +namespace torch::jit::tensorexpr { + +using int32 = std::int32_t; + +class Dtype; +TORCH_API std::ostream& operator<<(std::ostream& stream, const Dtype& dtype); + +using ScalarType = c10::ScalarType; + +enum ElementType { + kAllTypes = 0, + kIntegralTypes = 1 << 0, + kFloatingPointTypes = 1 << 1, + kBoolType = 1 << 2, + kComplexTypes = 1 << 3, + kQintTypes = 1 << 4, + kNonComplexOrQintTypes = kIntegralTypes | kBoolType | kFloatingPointTypes, +}; + +// Data types for scalar and vector elements. +class TORCH_API Dtype { + public: + explicit Dtype(int8_t type) + : scalar_type_(static_cast(type)), lanes_(1) {} + explicit Dtype(ScalarType type) : scalar_type_(type), lanes_(1) {} + Dtype(int8_t type, int64_t lanes) + : scalar_type_(static_cast(type)), lanes_(lanes) {} + Dtype(ScalarType type, int64_t lanes) : scalar_type_(type), lanes_(lanes) {} + Dtype(Dtype type, int64_t lanes) + : scalar_type_(type.scalar_type_), lanes_(lanes) { + if (type.lanes() != 1) { + throw malformed_input("dtype lanes dont match"); + } + } + int64_t lanes() const { + return lanes_; + } + ScalarType scalar_type() const { + return scalar_type_; + } + Dtype scalar_dtype() const; + bool operator==(const Dtype& other) const { + return scalar_type_ == other.scalar_type_ && lanes_ == other.lanes_; + } + bool operator!=(const Dtype& other) const { + return !(*this == other); + } + int byte_size() const; + std::string ToCppString() const; + + bool is_integral() const { + return c10::isIntegralType(scalar_type_, true); + } + bool is_floating_point() const { + return c10::isFloatingType(scalar_type_); + } + bool is_signed() const { + return c10::isSignedType(scalar_type_); + } + + Dtype cloneWithScalarType(ScalarType nt) const { + return Dtype(nt, lanes_); + } + + private: + friend TORCH_API std::ostream& operator<<( + std::ostream& stream, + const Dtype& dtype); + ScalarType scalar_type_; + int64_t lanes_; // the width of the element for a vector time +}; + +extern TORCH_API Dtype kHandle; + +#define NNC_DTYPE_DECLARATION(ctype, name) extern TORCH_API Dtype k##name; + +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, NNC_DTYPE_DECLARATION) +NNC_DTYPE_DECLARATION(c10::quint8, QUInt8) +NNC_DTYPE_DECLARATION(c10::qint8, QInt8) +#undef NNC_DTYPE_DECLARATION + +template +TORCH_API Dtype ToDtype(); + +#define NNC_TODTYPE_DECLARATION(ctype, name) \ + template <> \ + inline Dtype ToDtype() { \ + return k##name; \ + } +AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, NNC_TODTYPE_DECLARATION) +NNC_TODTYPE_DECLARATION(c10::quint8, QUInt8) +NNC_TODTYPE_DECLARATION(c10::qint8, QInt8) +#undef NNC_TODTYPE_DECLARATION + +TORCH_API Dtype ToDtype(ScalarType type); + +inline Dtype promoteTypes(Dtype a, Dtype b) { + if (a.lanes() != b.lanes()) { + throw malformed_input("promoting types with different lanes"); + } + return Dtype( + static_cast(c10::promoteTypes( + static_cast(a.scalar_type()), + static_cast(b.scalar_type()))), + a.lanes()); +} + +inline Dtype BinaryOpDtype( + Dtype op1_dtype, + Dtype op2_dtype, + ScalarType ret_type = ScalarType::Undefined) { + if (op1_dtype == op2_dtype) { + if (ret_type == ScalarType::Undefined) { + return op1_dtype; + } + + return ToDtype(ret_type); + } + + if (op1_dtype.lanes() != op2_dtype.lanes()) { + throw malformed_input("lanes dont match"); + } + int64_t lanes = op1_dtype.lanes(); + + Dtype resultType = promoteTypes(op1_dtype, op2_dtype); + if (resultType.scalar_type() == ScalarType::Undefined) { + throw malformed_input("scalar type doesn't match"); + } + + if (lanes == 1) { + // Use the fixed scalar Dtypes. + return ToDtype(resultType.scalar_type()); + } + + return resultType; +} + +} // namespace torch::jit::tensorexpr + +namespace std { + +using torch::jit::tensorexpr::Dtype; +std::string to_string(const Dtype& dtype); +using torch::jit::tensorexpr::ScalarType; +std::string to_string(const ScalarType& dtype); + +} // namespace std diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/unique_name_manager.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/unique_name_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..f5ceac667d158730af57c4ddc6192e59040e3d4b --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/unique_name_manager.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace torch::jit::tensorexpr { + +class VarHandle; +class Var; + +using VarNameMap = std::unordered_map; + +// A manager to get unique names from vars. +// It starts with the name hints of the var and append "_" + $counter until it +// hits a unique name. +class TORCH_API UniqueNameManager { + public: + const std::string& get_unique_name(const VarHandle& v); + + const std::string& get_unique_name(const VarPtr& v); + + private: + friend class ScopedVarName; + VarNameMap unique_name_mapping_; + std::unordered_map unique_name_count_; + std::unordered_set all_unique_names_; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/var_substitutor.h b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/var_substitutor.h new file mode 100644 index 0000000000000000000000000000000000000000..e3009902bc33497303942c86a8958dccde2eba1d --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/jit/tensorexpr/var_substitutor.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace torch::jit::tensorexpr { + +using VarMapping = std::vector>; + +class VarSubMutator : public IRMutator { + public: + VarSubMutator(const VarMapping& var_mapping) { + for (auto& entry : var_mapping) { + VarPtr key_var = entry.first; + ExprPtr value = entry.second; + if (!key_var) { + throw malformed_input("missing key in VarSubMutator"); + } + var_mapping_[std::move(key_var)] = std::move(value); + } + } + + ExprPtr mutate(const VarPtr& var) override { + auto iter = var_mapping_.find(var); + if (iter == var_mapping_.end()) { + return var; + } + return iter->second; + } + + ExprPtr mutate(const ReduceOpPtr& var) override { + auto body = var->body()->accept_mutator(this); + std::vector new_inner; + + for (const auto& v : var->reduce_args()) { + ExprPtr e = v->accept_mutator(this); + if (VarPtr new_var = to(e)) { + new_inner.push_back(std::move(new_var)); + } else { + VarFinder varFinder; + e->accept(&varFinder); + auto varlist = varFinder.vars(); + new_inner.insert(new_inner.end(), varlist.begin(), varlist.end()); + } + } + + return alloc(body, new_inner, var->reducer()); + } + + private: + std::unordered_map var_mapping_; +}; + +} // namespace torch::jit::tensorexpr diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/byte_order.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/byte_order.h new file mode 100644 index 0000000000000000000000000000000000000000..d586270fbfc7c267ba7d1514c32538b212f77e2f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/byte_order.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#include +#define thp_bswap16(x) bswap16(x) +#define thp_bswap32(x) bswap32(x) +#define thp_bswap64(x) bswap64(x) +#elif defined(__APPLE__) +#include +#define thp_bswap16(x) OSSwapInt16(x) +#define thp_bswap32(x) OSSwapInt32(x) +#define thp_bswap64(x) OSSwapInt64(x) +#elif defined(__GNUC__) && !defined(__MINGW32__) +#include +#define thp_bswap16(x) bswap_16(x) +#define thp_bswap32(x) bswap_32(x) +#define thp_bswap64(x) bswap_64(x) +#elif defined _WIN32 || defined _WIN64 +#define thp_bswap16(x) _byteswap_ushort(x) +#define thp_bswap32(x) _byteswap_ulong(x) +#define thp_bswap64(x) _byteswap_uint64(x) +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define to_be16(x) thp_bswap16(x) +#define from_be16(x) thp_bswap16(x) +#define to_be32(x) thp_bswap32(x) +#define from_be32(x) thp_bswap32(x) +#define to_be64(x) thp_bswap64(x) +#define from_be64(x) thp_bswap64(x) +#define to_le16(x) (x) +#define from_le16(x) (x) +#define to_le32(x) (x) +#define from_le32(x) (x) +#define to_le64(x) (x) +#define from_le64(x) (x) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define to_be16(x) (x) +#define from_be16(x) (x) +#define to_be32(x) (x) +#define from_be32(x) (x) +#define to_be64(x) (x) +#define from_be64(x) (x) +#define to_le16(x) thp_bswap16(x) +#define from_le16(x) thp_bswap16(x) +#define to_le32(x) thp_bswap32(x) +#define from_le32(x) thp_bswap32(x) +#define to_le64(x) thp_bswap64(x) +#define from_le64(x) thp_bswap64(x) +#else +#error Unexpected or undefined __BYTE_ORDER__ +#endif + +namespace torch::utils { + +enum THPByteOrder { THP_LITTLE_ENDIAN = 0, THP_BIG_ENDIAN = 1 }; + +TORCH_API THPByteOrder THP_nativeByteOrder(); + +template +TORCH_API void THP_decodeBuffer(T* dst, const uint8_t* src, U type, size_t len); + +template +TORCH_API void THP_encodeBuffer( + uint8_t* dst, + const T* src, + THPByteOrder order, + size_t len); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/cpp_stacktraces.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/cpp_stacktraces.h new file mode 100644 index 0000000000000000000000000000000000000000..8c38e972faf71cb653dee89fced30d928c26b725 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/cpp_stacktraces.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace torch { +TORCH_API bool get_cpp_stacktraces_enabled(); +TORCH_API torch::unwind::Mode get_symbolize_mode(); +} // namespace torch diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/disable_torch_function.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/disable_torch_function.h new file mode 100644 index 0000000000000000000000000000000000000000..9331c521b18362abb38e4f8da1cc763542b9443a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/disable_torch_function.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include + +namespace torch { +// Sometimes we don't want infinite recursion for subclasses, +// Or a way to achieve the old behaviour. + +// This is an internal utility, not exposed to users. +bool torch_function_enabled(); +PyObject* disabled_torch_function_impl(); +PyObject* disabled_torch_dispatch_impl(); +void set_disabled_torch_function_impl(PyObject* value); +void set_disabled_torch_dispatch_impl(PyObject* value); +// Set ignore_mode to true if you're trying to collect overloaded arguments; +// using mode here will improperly cause you to add ALL objects to the +// overloaded list even if they don't actually have __torch_function__ +bool check_has_torch_function(PyObject* obj, bool ignore_mode = false); + +struct DisableTorchDispatch { + DisableTorchDispatch() + : guard_(c10::DispatchKeySet( + {c10::DispatchKey::Python, c10::DispatchKey::PreDispatch})), + guard_tls_snapshot_(c10::DispatchKey::PythonTLSSnapshot) {} + c10::impl::ExcludeDispatchKeyGuard guard_; + c10::impl::ExcludeDispatchKeyGuard guard_tls_snapshot_; +}; + +} // namespace torch + +PyObject* THPModule_isEnabledTorchFunction(PyObject* self, PyObject* unused); +PyObject* THPModule_isAllDisabledTorchFunction( + PyObject* self, + PyObject* unused); +PyObject* THPModule_DisableTorchFunctionType(); +PyObject* THPModule_DisableTorchFunctionSubclassType(); +PyObject* THPModule_disable_torch_function(PyObject* self, PyObject* args); +PyObject* THPModule_disable_torch_dispatch(PyObject* self, PyObject* args); +PyObject* THPModule_has_torch_function(PyObject*, PyObject* arg); +PyObject* THPModule_has_torch_function_unary(PyObject*, PyObject* obj); +PyObject* THPModule_has_torch_function_variadic( + PyObject*, + PyObject* const* args, + Py_ssize_t nargs); diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/init.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/init.h new file mode 100644 index 0000000000000000000000000000000000000000..31b65470c18ee807917e4568dfc2d95ed63571e0 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/init.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::throughput_benchmark { + +void initThroughputBenchmarkBindings(PyObject* module); + +} // namespace torch::throughput_benchmark diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/invalid_arguments.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/invalid_arguments.h new file mode 100644 index 0000000000000000000000000000000000000000..fecf93225e76b9a67f4022da81708ffdbff94097 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/invalid_arguments.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace torch { + +std::string format_invalid_args( + PyObject* given_args, + PyObject* given_kwargs, + const std::string& function_name, + const std::vector& options); + +} // namespace torch diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/nested.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/nested.h new file mode 100644 index 0000000000000000000000000000000000000000..7683a2412418747ec3513525a910dba1d5606499 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/nested.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include + +namespace torch::utils { + +at::Tensor nested_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/numpy_stub.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/numpy_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..ecdfa61500ecf00f1523090a486a17c776415ff9 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/numpy_stub.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef USE_NUMPY + +#if !defined(NO_IMPORT_ARRAY) && !defined(WITH_NUMPY_IMPORT_ARRAY) +#define NO_IMPORT_ARRAY +#endif + +#ifndef PY_ARRAY_UNIQUE_SYMBOL +#define PY_ARRAY_UNIQUE_SYMBOL __numpy_array_api +#endif + +#ifndef NPY_NO_DEPRECATED_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#endif + +#include + +#endif // USE_NUMPY diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/object_ptr.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/object_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..983a7a2ae07a613c56630b84b0d150e43a689862 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/object_ptr.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +template +class TORCH_PYTHON_API THPPointer { + public: + THPPointer() : ptr(nullptr) {} + explicit THPPointer(T* ptr) noexcept : ptr(ptr) {} + THPPointer(THPPointer&& p) noexcept : ptr(std::exchange(p.ptr, nullptr)) {} + THPPointer(const THPPointer& p) = delete; + THPPointer& operator=(const THPPointer&) = delete; + + ~THPPointer() { + free(); + } + T* get() { + return ptr; + } + const T* get() const { + return ptr; + } + T* release() { + T* tmp = ptr; + ptr = nullptr; + return tmp; + } + operator T*() { + return ptr; + } + THPPointer& operator=(T* new_ptr) noexcept { + free(); + ptr = new_ptr; + return *this; + } + THPPointer& operator=(THPPointer&& p) noexcept { + free(); + ptr = p.ptr; + p.ptr = nullptr; + return *this; + } + T* operator->() { + return ptr; + } + explicit operator bool() const { + return ptr != nullptr; + } + + private: + void free(); + T* ptr = nullptr; +}; + +/** + * An RAII-style, owning pointer to a PyObject. You must protect + * destruction of this object with the GIL. + * + * WARNING: Think twice before putting this as a field in a C++ + * struct. This class does NOT take out the GIL on destruction, + * so if you will need to ensure that the destructor of your struct + * is either (a) always invoked when the GIL is taken or (b) takes + * out the GIL itself. Easiest way to avoid this problem is to + * not use THPPointer in this situation. + */ +using THPObjectPtr = THPPointer; +using THPCodeObjectPtr = THPPointer; +using THPFrameObjectPtr = THPPointer; diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/out_types.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/out_types.h new file mode 100644 index 0000000000000000000000000000000000000000..63d85dc8f5a908df9b1968f21913ab32a2f05c1f --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/out_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace torch::utils { + +TORCH_API void check_out_type_matches( + const at::Tensor& result, + std::optional scalarType, + bool scalarType_is_none, + std::optional layout, + std::optional device, + bool device_is_none); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pycfunction_helpers.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pycfunction_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..745e1842e682c8a2fb3cc9d94e77122505016571 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pycfunction_helpers.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include + +inline PyCFunction castPyCFunctionWithKeywords(PyCFunctionWithKeywords func) { + C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED("-Wcast-function-type") + C10_DIAGNOSTIC_PUSH_AND_IGNORED_IF_DEFINED("-Wcast-function-type-strict") + return reinterpret_cast(func); + C10_DIAGNOSTIC_POP() + C10_DIAGNOSTIC_POP() +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_arg_parser.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_arg_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..34246fb2f1f9358c3a6d7f6c17e060af7a8a5046 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_arg_parser.h @@ -0,0 +1,1301 @@ +#pragma once + +// Parse arguments to Python functions implemented in C++ +// This is similar to PyArg_ParseTupleAndKeywords(), but specifically handles +// the types relevant to PyTorch and distinguishes between overloaded function +// signatures. +// +// Example: +// +// static PythonArgParser parser({ +// "norm(Scalar p, int64_t dim, bool keepdim=False)", +// "norm(Scalar p=2)", +// }); +// ParsedArgs<3> parsed_args; +// auto r = parser.parse(args, kwargs, parsed_args); +// if (r.idx == 0) { +// norm(r.scalar(0), r.int64(1), r.bool(0)); +// } else { +// norm(r.scalar(0)); +// } +// +// We auto-generate most uses of PythonArgParser; the generated files +// are torch/csrc/autograd/generated/python_*.cpp +// +// Some gotchas that you should watch out for: +// +// - Note [Order of overloads matters] +// Order of overloads matters. A set of input arguments may +// bind to multiple argument specs; we will always pick the +// first one in PythonArgParser. However, when you are writing +// overloads in, e.g., native_functions.yaml, you don't have to +// worry about what order you write them, because the code +// generation logic always gives the overloads a canonical +// order, where Tensor overloads come first, before Scalar overloads. +// This logic is in sort_declarations in +// tools/autograd/gen_python_functions.py +// +// - Zero-dim tensors (e.g., torch.tensor(2)) bind to both +// Scalar and Tensor, UNLESS they require grad (in which case +// they only bind to Tensor). + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +inline bool THPUtils_checkScalar(PyObject* obj) { +#ifdef USE_NUMPY + if (torch::utils::is_numpy_scalar(obj)) { + return true; + } +#endif + return PyFloat_Check(obj) || PyLong_Check(obj) || PyComplex_Check(obj) || + torch::is_symint(py::handle(obj)) || + torch::is_symfloat(py::handle(obj)) || torch::is_symbool(py::handle(obj)); +} + +namespace torch { + +bool should_allow_numbers_as_tensors(const std::string& name); + +enum class ParameterType { + TENSOR, + SCALAR, + INT64, + SYM_INT, + DOUBLE, + COMPLEX, + TENSOR_LIST, + INT_LIST, + GENERATOR, + BOOL, + STORAGE, + PYOBJECT, + SCALARTYPE, + LAYOUT, + MEMORY_FORMAT, + DEVICE, + STREAM, + STRING, + DIMNAME, + DIMNAME_LIST, + QSCHEME, + FLOAT_LIST, + SCALAR_LIST, + SYM_INT_LIST, + DISPATCH_KEY_SET +}; + +struct FunctionParameter; +struct FunctionSignature; +struct PythonArgs; + +// Contains bound Python arguments in declaration order +template +struct ParsedArgs { + ParsedArgs() : args() {} + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + PyObject* args[N]; +}; + +// A PythonArgParser contains a list of valid signatures. Instances are +// typically global variables and should be immutable. +struct PYBIND11_EXPORT PythonArgParser { + explicit PythonArgParser( + const std::vector& fmts, + bool traceable = false); + + // meant only for `torch` functions. + template + inline PythonArgs parse( + PyObject* self, + PyObject* args, + PyObject* kwargs, + ParsedArgs& dst); + + template + inline PythonArgs parse(PyObject* args, PyObject* kwargs, ParsedArgs& dst); + + inline PythonArgs parse(PyObject* self, ParsedArgs<0>& dst); + + // Formatted strings of non-hidden signatures + std::vector get_signatures() const; + + private: + [[noreturn]] void print_error( + PyObject* self, + PyObject* args, + PyObject* kwargs, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + PyObject* parsed_args[]); + void check_deprecated(const FunctionSignature& signature); + PythonArgs raw_parse( + PyObject* self, + PyObject* args, + PyObject* kwargs, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + PyObject* parsed_args[]); + + std::vector signatures_; + std::string function_name; + size_t max_args; + bool traceable; +}; + +// FunctionSignature represents a single valid signature for a Python function. +// It is immutable once constructed. The contained data can be concurrently +// accessed by multiple calls. +struct FunctionSignature { + explicit FunctionSignature(const std::string& fmt, int index); + + bool parse( + PyObject* self, + PyObject* args, + PyObject* kwargs, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + PyObject* dst[], + std::vector& overloaded_args, + bool raise_exception); + + std::string toString() const; + + std::string name; + std::vector params; + size_t min_args; + size_t max_args; + size_t max_pos_args; + int index; + bool hidden; + bool deprecated; +}; + +// PythonArgs contains bound Python arguments for an actual invocation +// along with references to the matched signature. +struct PythonArgs { + PythonArgs( + bool traceable, + const FunctionSignature& signature, + PyObject** args, + std::vector overloaded_args) + : idx(signature.index), + traceable(traceable), + signature(signature), + args(args), + overloaded_args(std::move(overloaded_args)) {} + + int idx; + bool traceable; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + const FunctionSignature& signature; + PyObject** args; + std::vector overloaded_args; // NOTE: borrowed references + + inline bool has_torch_function(); + inline std::string get_func_name(); + inline at::Tensor tensor(int i); + inline std::optional optionalTensor(int i); + inline at::Scalar scalar(int i); + inline at::Scalar scalarWithDefault(int i, const at::Scalar& default_scalar); + inline std::vector scalarlist(int i); + inline std::vector tensorlist(int i); + inline torch::List> list_of_optional_tensors(int i); + template + inline std::array tensorlist_n(int i); + inline std::vector intlist(int i); + inline std::vector symintlist(int i); + inline c10::OptionalArray intlistOptional(int i); + inline c10::OptionalArray symintlistOptional(int i); + inline std::vector intlistWithDefault( + int i, + std::vector default_intlist); + inline std::optional generator(int i); + inline at::Storage storage(int i); + inline at::Storage storage( + int i, + at::ScalarType& storage_scalar_type, + bool& is_typed_storage); + inline c10::Stream stream(int i); + inline at::ScalarType scalartype(int i); + inline at::ScalarType scalartypeWithDefault( + int i, + at::ScalarType default_scalartype); + inline std::optional scalartypeOptional(int i); + inline std::optional scalarOptional(int i); + inline std::optional toInt64Optional(int i); + inline std::optional toSymIntOptional(int i); + inline std::optional toBoolOptional(int i); + inline std::optional toDoubleOptional(int i); + inline c10::OptionalArray doublelistOptional(int i); + inline std::vector doublelist(int i); + inline std::vector getDoublelist(int i); + inline at::Layout layout(int i); + inline at::Layout layoutWithDefault(int i, at::Layout default_layout); + inline std::optional layoutOptional(int i); + inline at::Device device(int i); + inline at::Device deviceWithDefault(int i, const at::Device& default_device); + inline std::optional deviceOptional(int i); + inline at::Dimname dimname(int i); + inline std::vector dimnamelist(int i); + inline std::optional> toDimnameListOptional(int i); + inline at::MemoryFormat memoryformat(int i); + inline std::optional memoryformatOptional(int i); + inline at::QScheme toQScheme(int i); + inline std::string string(int i); + inline std::string stringWithDefault(int i, const std::string& default_str); + inline std::optional stringOptional(int i); + inline std::string_view stringView(int i); + inline std::string_view stringViewWithDefault( + int i, + const std::string_view default_str); + inline std::optional stringViewOptional(int i); + inline PyObject* pyobject(int i); + inline int64_t toInt64(int i); + inline c10::SymInt toSymInt(int i); + inline c10::SymBool toSymBool(int i); + inline int64_t toInt64WithDefault(int i, int64_t default_int); + inline double toDouble(int i); + inline double toDoubleWithDefault(int i, double default_double); + inline c10::complex toComplex(int i); + inline c10::complex toComplexWithDefault( + int i, + c10::complex default_complex); + inline bool toBool(int i); + inline bool toBoolWithDefault(int i, bool default_bool); + inline bool isNone(int i); + inline std::optional toDispatchKeySetOptional(int i); + + private: + at::Tensor tensor_slow(int i); + at::Scalar scalar_slow(int i); + at::Scalar scalar_slow(PyObject* arg); +}; + +// FunctionParameter is a single formal parameter of a Python function. +// It is immutable once constructed. +struct FunctionParameter { + FunctionParameter(const std::string& fmt, bool keyword_only); + + bool check( + PyObject* obj, + std::vector& overloaded_args, + int argnum, + int64_t* failed_idx = nullptr); + + void set_default_str(const std::string& str); + std::string type_name() const; + + ParameterType type_; + bool optional; + bool allow_none; + bool keyword_only; + bool allow_numbers_as_tensors = false; + int size; + std::string name; + // having this as a raw PyObject * will presumably leak it, but these are only + // held by static objects anyway, and Py_Finalize can already be called when + // this is destructed. + PyObject* python_name; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + at::SmallVector numpy_python_names; + at::Scalar default_scalar; + std::vector default_intlist; + std::string default_string; + union { + bool default_bool; + int64_t default_int; + double default_double; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + double default_complex[2]; // see Scalar + at::ScalarType default_scalartype; + at::Layout default_layout; + }; + std::string default_value; +}; + +template +inline PythonArgs PythonArgParser::parse( + PyObject* self, + PyObject* args, + PyObject* kwargs, + ParsedArgs& dst) { + TORCH_CHECK_VALUE( + N >= max_args, + "PythonArgParser: dst ParsedArgs buffer does not have enough capacity, expected ", + max_args, + " (got ", + N, + ")"); + return raw_parse(self, args, kwargs, dst.args); +} + +template +inline PythonArgs PythonArgParser::parse( + PyObject* args, + PyObject* kwargs, + ParsedArgs& dst) { + return parse(nullptr, args, kwargs, dst); +} + +inline PythonArgs PythonArgParser::parse(PyObject* self, ParsedArgs<0>& dst) { + return parse(self, nullptr, nullptr, dst); +} + +inline bool PythonArgs::has_torch_function() { + return !overloaded_args.empty() || at::impl::torch_function_mode_enabled(); +} + +inline std::string PythonArgs::get_func_name() { + return signature.name; +} + +// TODO: this can return MaybeOwned +inline at::Tensor PythonArgs::tensor(int i) { + if (args[i] && THPVariable_CheckExact(args[i])) { + return THPVariable_Unpack(args[i]); + } + return tensor_slow(i); +} + +inline std::optional PythonArgs::optionalTensor(int i) { + at::Tensor t = tensor(i); + // NOLINTNEXTLINE(bugprone-branch-clone) + if (t.defined()) { + return t; + } else { + return std::nullopt; + } +} + +inline at::Scalar PythonArgs::scalar(int i) { + if (!args[i]) + return signature.params[i].default_scalar; + return scalar_slow(i); +} + +inline std::vector PythonArgs::scalarlist(int i) { + if (!args[i]) + return std::vector(); + auto tuple = six::isTuple(args[i]); + THPObjectPtr arg = six::maybeAsTuple(args[i]); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg.get()) : PyList_GET_SIZE(arg.get()); + std::vector res(size); + for (const auto idx : c10::irange(size)) { + PyObject* obj = tuple ? PyTuple_GET_ITEM(arg.get(), idx) + : PyList_GET_ITEM(arg.get(), idx); + res[idx] = scalar_slow(obj); + } + return res; +} + +inline at::Scalar PythonArgs::scalarWithDefault( + int i, + const at::Scalar& default_scalar) { + if (!args[i]) + return default_scalar; + return scalar_slow(i); +} + +inline std::optional PythonArgs::scalarOptional(int i) { + if (!args[i]) + return std::nullopt; + return scalar_slow(i); +} + +inline std::vector PythonArgs::tensorlist(int i) { + if (!args[i]) + return std::vector(); + auto tuple = six::isTuple(args[i]); + THPObjectPtr arg = six::maybeAsTuple(args[i]); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg.get()) : PyList_GET_SIZE(arg.get()); + std::vector res(size); + for (const auto idx : c10::irange(size)) { + PyObject* obj = tuple ? PyTuple_GET_ITEM(arg.get(), idx) + : PyList_GET_ITEM(arg.get(), idx); + // This is checked by the argument parser so it's safe to cast without + // checking if this is a tensor first + res[idx] = THPVariable_Unpack(obj); + } + return res; +} + +inline torch::List> PythonArgs:: + list_of_optional_tensors(int i) { + if (!args[i]) + return torch::List>(); + auto tuple = six::isTuple(args[i]); + THPObjectPtr arg = six::maybeAsTuple(args[i]); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg.get()) : PyList_GET_SIZE(arg.get()); + torch::List> res; + res.reserve(size); + for (const auto idx : c10::irange(size)) { + PyObject* obj = tuple ? PyTuple_GET_ITEM(arg.get(), idx) + : PyList_GET_ITEM(arg.get(), idx); + // This is checked by the argument parser so it's safe to cast without + // checking if this is a tensor first + res.push_back(THPVariable_Unpack(obj)); + } + return res; +} + +template +inline std::array PythonArgs::tensorlist_n(int i) { + auto res = std::array(); + if (!args[i]) + return res; + auto tuple = six::isTuple(args[i]); + THPObjectPtr arg = six::maybeAsTuple(args[i]); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg.get()) : PyList_GET_SIZE(arg.get()); + if (size != N) { + throw TypeError("expected tuple of %d elements but got %d", N, (int)size); + } + for (const auto idx : c10::irange(size)) { + PyObject* obj = tuple ? PyTuple_GET_ITEM(arg.get(), idx) + : PyList_GET_ITEM(arg.get(), idx); + // This is checked by the argument parser so it's safe to cast without + // checking if this is a tensor first + res[idx] = THPVariable_Unpack(obj); + } + return res; +} + +inline std::vector PythonArgs::intlist(int i) { + return intlistWithDefault(i, signature.params[i].default_intlist); +} + +inline PyObject* toPyObject(const c10::SymInt& symint) { + if (symint.is_symbolic()) { + auto r = py::cast(symint).release().ptr(); + TORCH_INTERNAL_ASSERT(r); + return r; + } else { + auto m = symint.maybe_as_int(); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + return THPUtils_packInt64(m.value()); + } +} + +inline void throw_intlist_exception( + const torch::PythonArgs* args, + size_t i, + PyObject* obj, + size_t idx, + const std::exception& e = python_error()) { + std::string error = strlen(e.what()) + ? e.what() + : std::string("type must be ") + args->signature.params[i].type_name() + + ",but got " + Py_TYPE(obj)->tp_name; + throw TypeError( + "%s(): argument '%s' failed to unpack the object at pos %zu with error \"%s\"", + args->signature.name.c_str(), + args->signature.params[i].name.c_str(), + idx + 1, + error.c_str()); +} + +inline std::vector PythonArgs::symintlist(int i) { + if (!args[i]) { + return c10::fmap(signature.params[i].default_intlist, [](int64_t di) { + return c10::SymInt(di); + }); + } + + const auto size1 = signature.params[i].size; + if (size1 > 0 && THPUtils_checkLong(args[i])) { + return std::vector( + size1, c10::SymInt(THPUtils_unpackLong(args[i]))); + } + + if (size1 > 0 && torch::is_symint(py::handle(args[i]))) { + auto si = py::handle(args[i]).cast(); + return std::vector(size1, si); + } + + PyObject* arg = args[i]; + auto tuple = PyTuple_Check(arg); + // NOLINTNEXTLINE(bugprone-branch-clone) + const auto size2 = tuple ? PyTuple_GET_SIZE(arg) : PyList_GET_SIZE(arg); + std::vector res; + res.reserve(size2); + for (const auto idx : c10::irange(size2)) { + PyObject* obj = + tuple ? PyTuple_GET_ITEM(arg, idx) : PyList_GET_ITEM(arg, idx); + + // Elements of torch.Size are tensors during tracing, and we need to + // record extra information before they are turned into an IntArrayRef + if (traceable && jit::tracer::isTracing() && THPVariable_Check(obj)) { + auto& var = THPVariable_Unpack(obj); + jit::tracer::ArgumentStash::stashIntArrayRefElem( + signature.params[i].name, size2, idx, var); + try { + res.emplace_back(var.item()); + continue; + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + continue; + } else { + // convert tensor to scalar outside of try / catch, + // so that Tensor subclass exceptions will not be caught. + if (THPUtils_checkLongExact(obj)) { + // Fast path for plain numbers + try { + res.emplace_back(THPUtils_unpackLong(obj)); + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + } else if (THPVariable_Check(obj)) { + auto& var = THPVariable_Unpack(obj); + if (var.numel() != 1 || + !at::isIntegralType( + var.dtype().toScalarType(), /*include_bool*/ true)) { + throw_intlist_exception(this, i, obj, idx); + } + auto scalar = var.item(); + TORCH_CHECK(scalar.isIntegral(/*include bool*/ false)); + res.push_back(scalar.toSymInt()); + } else { + try { + if (is_symint(py::handle(obj))) { + res.push_back(py::handle(obj).cast()); + } else { + res.emplace_back(THPUtils_unpackIndex(obj)); + } + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + } + } + } + + return res; +} + +inline std::vector PythonArgs::intlistWithDefault( + int i, + std::vector default_intlist) { + if (!args[i]) + return default_intlist; + PyObject* arg = args[i]; + const auto size1 = signature.params[i].size; + if (size1 > 0 && THPUtils_checkLong(arg)) { + return std::vector(size1, THPUtils_unpackLong(arg)); + } + if (size1 > 0 && torch::is_symint(py::handle(arg))) { + return std::vector( + size1, + py::handle(arg).cast().guard_int(__FILE__, __LINE__)); + } + auto tuple = PyTuple_Check(arg); + // NOLINTNEXTLINE(bugprone-branch-clone) + const auto size2 = tuple ? PyTuple_GET_SIZE(arg) : PyList_GET_SIZE(arg); + std::vector res(size2); + for (const auto idx : c10::irange(size2)) { + PyObject* obj = + tuple ? PyTuple_GET_ITEM(arg, idx) : PyList_GET_ITEM(arg, idx); + // Elements of torch.Size are tensors during tracing, and we need to + // record extra information before they are turned into an IntArrayRef + if (traceable && jit::tracer::isTracing() && THPVariable_Check(obj)) { + auto& var = THPVariable_Unpack(obj); + jit::tracer::ArgumentStash::stashIntArrayRefElem( + signature.params[i].name, size2, idx, var); + try { + res[idx] = var.item(); + continue; + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + } else { + // convert tensor to scalar outside of try / catch, + // so that Tensor subclass exceptions will not be caught. + if (THPUtils_checkLongExact(obj)) { + // Fast path for plain numbers + try { + res[idx] = THPUtils_unpackLong(obj); + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + } else if (torch::is_symint(py::handle(obj))) { + res[idx] = py::cast(py::handle(obj)) + .guard_int(__FILE__, __LINE__); + } else if (THPVariable_Check(obj)) { + auto& var = THPVariable_Unpack(obj); + if (var.numel() != 1 || + !at::isIntegralType( + var.dtype().toScalarType(), /*include_bool*/ true)) { + throw_intlist_exception(this, i, obj, idx); + } + res[idx] = var.item(); + } else { + try { + res[idx] = THPUtils_unpackIndex(obj); + } catch (std::exception& e) { + throw_intlist_exception(this, i, obj, idx, e); + } + } + } + } + return res; +} + +inline c10::OptionalArray PythonArgs::intlistOptional(int i) { + if (!args[i]) { + return {}; + } + return intlist(i); +} + +inline c10::OptionalArray PythonArgs::symintlistOptional(int i) { + if (!args[i]) { + return {}; + } + return symintlist(i); +} + +inline std::vector PythonArgs::getDoublelist(int i) { + PyObject* arg = args[i]; + auto tuple = PyTuple_Check(arg); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg) : PyList_GET_SIZE(arg); + std::vector res(size); + for (const auto idx : c10::irange(size)) { + PyObject* obj = + tuple ? PyTuple_GET_ITEM(arg, idx) : PyList_GET_ITEM(arg, idx); + try { + if (torch::is_symfloat(py::handle(obj))) { + res[idx] = py::cast(py::handle(obj)) + .guard_float(__FILE__, __LINE__); + } else { + res[idx] = THPUtils_unpackDouble(obj); + } + } catch (const std::exception&) { + throw TypeError( + "%s(): argument '%s' must be %s, but found element of type %s at pos %zu", + signature.name.c_str(), + signature.params[i].name.c_str(), + signature.params[i].type_name().c_str(), + Py_TYPE(obj)->tp_name, + idx + 1); + } + } + return res; +} + +inline c10::OptionalArray PythonArgs::doublelistOptional(int i) { + if (!args[i]) { + return {}; + } + return this->getDoublelist(i); +} + +inline std::vector PythonArgs::doublelist(int i) { + if (!args[i]) { + return {}; + } + return this->getDoublelist(i); +} + +inline std::optional PythonArgs::toDispatchKeySetOptional( + int i) { + if (!args[i]) { + return {}; + } + return py::cast(py::handle(args[i])); +} + +inline at::ScalarType PythonArgs::scalartypeWithDefault( + int i, + at::ScalarType default_scalartype) { + if (!args[i]) + return default_scalartype; + return scalartype(i); +} + +inline at::ScalarType toScalarType(PyObject* obj) { + if (obj == (PyObject*)&PyFloat_Type) { + return at::ScalarType::Double; + } + if (obj == (PyObject*)&PyBool_Type) { + return at::ScalarType::Bool; + } + if (obj == (PyObject*)&PyLong_Type) { + return at::ScalarType::Long; + } + if (obj == (PyObject*)&PyComplex_Type) { + return at::ScalarType::ComplexDouble; + } + return reinterpret_cast(obj)->scalar_type; +} + +inline at::ScalarType PythonArgs::scalartype(int i) { + if (!args[i]) { + auto scalartype = signature.params[i].default_scalartype; + return (scalartype == at::ScalarType::Undefined) + ? torch::tensors::get_default_scalar_type() + : scalartype; + } + PyObject* obj = args[i]; + return toScalarType(obj); +} + +inline std::optional PythonArgs::scalartypeOptional(int i) { + if (!args[i]) + return std::nullopt; + return scalartype(i); +} + +inline at::Layout toLayout(PyObject* obj) { + const auto layout = reinterpret_cast(obj); + return layout->layout; +} + +inline at::Layout PythonArgs::layout(int i) { + if (!args[i]) + return signature.params[i].default_layout; + return toLayout(args[i]); +} + +inline at::Layout PythonArgs::layoutWithDefault( + int i, + at::Layout default_layout) { + if (!args[i]) + return default_layout; + return layout(i); +} + +inline std::optional PythonArgs::layoutOptional(int i) { + if (!args[i]) + return std::nullopt; + return layout(i); +} + +inline at::Device deviceFromLong(int64_t device_index) { + TORCH_CHECK(device_index >= 0, "Device index must not be negative"); + return at::Device( + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + at::getAccelerator(true).value(), + static_cast(device_index)); +} + +inline at::Device toDevice(PyObject* obj) { + if (THPDevice_Check(obj)) { + const auto device = reinterpret_cast(obj); + return device->device; + } + if (THPUtils_checkLong(obj)) { + return deviceFromLong(THPUtils_unpackLong(obj)); + } + if (torch::is_symint(py::handle(obj))) { + auto device_index = + py::cast(py::handle(obj)).guard_int(__FILE__, __LINE__); + return deviceFromLong(device_index); + } + const std::string& device_str = THPUtils_unpackString(obj); + return at::Device(device_str); +} + +inline at::Device PythonArgs::device(int i) { + if (!args[i]) { + return torch::tensors::get_default_device(); + } + return toDevice(args[i]); +} + +inline at::Device PythonArgs::deviceWithDefault( + int i, + const at::Device& default_device) { + if (!args[i]) + return default_device; + return device(i); +} + +inline std::optional PythonArgs::deviceOptional(int i) { + if (!args[i]) + return std::nullopt; + return device(i); +} + +inline at::Dimname PythonArgs::dimname(int i) { + TORCH_INTERNAL_ASSERT(args[i] != nullptr); + return THPDimname_parse(args[i]); +} + +inline std::vector parseDimnameList(PyObject* arg) { + auto tuple = PyTuple_Check(arg); + // NOLINTNEXTLINE(bugprone-branch-clone) + auto size = tuple ? PyTuple_GET_SIZE(arg) : PyList_GET_SIZE(arg); + std::vector res; + res.reserve(size); + for (const auto idx : c10::irange(size)) { + PyObject* obj = + tuple ? PyTuple_GET_ITEM(arg, idx) : PyList_GET_ITEM(arg, idx); + res.push_back(THPDimname_parse(obj)); + } + return res; +} + +inline std::optional> PythonArgs:: + toDimnameListOptional(int i) { + if (!args[i]) + return std::nullopt; + return parseDimnameList(args[i]); +} + +inline std::vector PythonArgs::dimnamelist(int i) { + TORCH_INTERNAL_ASSERT(args[i]); + PyObject* arg = args[i]; + auto size = signature.params[i].size; + TORCH_INTERNAL_ASSERT(size == 0 || size == 1); + if (size == 1 && THPUtils_checkDimname(arg)) { + return {THPDimname_parse(arg)}; + } + return parseDimnameList(arg); +} + +inline at::MemoryFormat PythonArgs::memoryformat(int i) { + if (!args[i]) + return at::MemoryFormat::Contiguous; + TORCH_CHECK( + THPMemoryFormat_Check(args[i]), + "memory_format arg must be an instance of the torch.memory_format"); + const auto memory_format = reinterpret_cast(args[i]); + return memory_format->memory_format; +} + +inline std::optional PythonArgs::memoryformatOptional(int i) { + if (!args[i]) + return std::nullopt; + return memoryformat(i); +} + +inline at::QScheme PythonArgs::toQScheme(int i) { + if (!args[i]) + return at::kPerTensorAffine; + TORCH_CHECK( + THPQScheme_Check(args[i]), + "qscheme arg must be an instance of the torch.qscheme"); + const auto qscheme = reinterpret_cast(args[i]); + return qscheme->qscheme; +} + +inline std::string PythonArgs::string(int i) { + return stringWithDefault(i, signature.params[i].default_string); +} + +inline std::string PythonArgs::stringWithDefault( + int i, + const std::string& default_str) { + if (!args[i]) + return default_str; + return THPUtils_unpackString(args[i]); +} + +inline std::optional PythonArgs::stringOptional(int i) { + if (!args[i]) + return std::nullopt; + return THPUtils_unpackString(args[i]); +} + +inline std::string_view PythonArgs::stringView(int i) { + return stringViewWithDefault(i, signature.params[i].default_string); +} + +inline std::string_view PythonArgs::stringViewWithDefault( + int i, + const std::string_view default_str) { + if (!args[i]) + return default_str; + return THPUtils_unpackStringView(args[i]); +} + +inline std::optional PythonArgs::stringViewOptional(int i) { + if (!args[i]) + return std::nullopt; + return THPUtils_unpackStringView(args[i]); +} + +inline int64_t PythonArgs::toInt64(int i) { + if (!args[i]) + return signature.params[i].default_int; + if (traceable && jit::tracer::isTracing() && THPVariable_Check(args[i])) { + auto& var = THPVariable_Unpack(args[i]); + jit::tracer::ArgumentStash::stashValue( + signature.params[i].name, idx, var, c10::IntType::get()); + } + if (torch::is_symint(py::handle(args[i]))) { + return py::cast(py::handle(args[i])) + .guard_int(__FILE__, __LINE__); + } + return THPUtils_unpackLong(args[i]); +} + +inline c10::SymInt PythonArgs::toSymInt(int i) { + if (!args[i]) { + return c10::SymInt(signature.params[i].default_int); + } + + if (traceable && jit::tracer::isTracing() && THPVariable_Check(args[i])) { + auto& var = THPVariable_Unpack(args[i]); + jit::tracer::ArgumentStash::stashValue( + signature.params[i].name, idx, var, c10::IntType::get()); + } + + return py::cast(py::handle(args[i])); +} + +inline c10::SymBool PythonArgs::toSymBool(int i) { + if (!args[i]) { + return c10::SymBool(signature.params[i].default_bool); + } + if (traceable && jit::tracer::isTracing() && THPVariable_Check(args[i])) { + auto& var = THPVariable_Unpack(args[i]); + jit::tracer::ArgumentStash::stashValue( + signature.params[i].name, idx, var, c10::BoolType::get()); + } + + return py::cast(py::handle(args[i])); +} + +inline int64_t PythonArgs::toInt64WithDefault(int i, int64_t default_int) { + if (!args[i]) + return default_int; + return toInt64(i); +} + +inline std::optional PythonArgs::toInt64Optional(int i) { + if (!args[i]) + return std::nullopt; + return toInt64(i); +} + +inline std::optional PythonArgs::toSymIntOptional(int i) { + if (!args[i]) + return std::nullopt; + return toSymInt(i); +} + +inline std::optional PythonArgs::toBoolOptional(int i) { + if (!args[i]) { + return std::nullopt; + } + return toBool(i); +} + +inline std::optional PythonArgs::toDoubleOptional(int i) { + if (!args[i]) { + return std::nullopt; + } + return toDouble(i); +} + +inline double PythonArgs::toDouble(int i) { + if (!args[i]) + return signature.params[i].default_double; + if (torch::is_symfloat(py::handle(args[i]))) { + return py::cast(py::handle(args[i])) + .guard_float(__FILE__, __LINE__); + } + if (torch::is_symint(py::handle(args[i]))) { + return static_cast(py::cast(py::handle(args[i])) + .guard_int(__FILE__, __LINE__)); + } + return THPUtils_unpackDouble(args[i]); +} + +inline bool PythonArgs::toBool(int i) { + if (!args[i]) + return signature.params[i].default_bool; + if (torch::is_symbool(py::handle(args[i]))) { + return py::cast(py::handle(args[i])) + .guard_bool(__FILE__, __LINE__); + } + return args[i] == Py_True; +} + +inline double PythonArgs::toDoubleWithDefault(int i, double default_double) { + if (!args[i]) + return default_double; + return toDouble(i); +} + +inline c10::complex PythonArgs::toComplex(int i) { + if (!args[i]) + return *(reinterpret_cast*>( + signature.params[i].default_complex)); + return THPUtils_unpackComplexDouble(args[i]); +} + +inline c10::complex PythonArgs::toComplexWithDefault( + int i, + c10::complex default_value) { + if (!args[i]) + return default_value; + return toComplex(i); +} + +inline bool PythonArgs::toBoolWithDefault(int i, bool default_bool) { + if (!args[i]) + return default_bool; + return toBool(i); +} + +inline bool PythonArgs::isNone(int i) { + return args[i] == nullptr; +} + +inline std::optional PythonArgs::generator(int i) { + if (!args[i]) + return std::nullopt; + return reinterpret_cast(args[i])->cdata; +} + +inline at::Storage PythonArgs::storage(int i) { + if (!args[i]) + return at::Storage(); + return createStorage(args[i]); +} + +inline at::Storage PythonArgs::storage( + int i, + at::ScalarType& storage_scalar_type, + bool& is_typed_storage) { + at::Storage storage; + if (!args[i]) { + storage = at::Storage(); + is_typed_storage = false; + storage_scalar_type = at::ScalarType::Undefined; + } else { + std::tie(storage, storage_scalar_type, is_typed_storage) = + createStorageGetType(args[i]); + } + return storage; +} + +inline c10::Stream PythonArgs::stream(int i) { + if (!args[i]) + return c10::Stream( + c10::Stream::Default::DEFAULT, c10::Device(c10::DeviceType::CPU, -1)); + if (!THPStream_Check(args[i])) { + throw TypeError( + "expected Stream object. Got '%s'", Py_TYPE(args[i])->tp_name); + } + return c10::Stream::unpack3( + ((THPStream*)args[i])->stream_id, + static_cast(((THPStream*)args[i])->device_index), + static_cast(((THPStream*)args[i])->device_type)); +} + +inline PyObject* PythonArgs::pyobject(int i) { + if (!args[i]) + return Py_None; + return args[i]; +} + +/* + * + * Handle __torch_function__ overrides if we know that there are overloaded + * arguments. All objects stored in r.overloaded_args must have a + * __torch_function__ implementation and the arguments must be ordered in order + * of precedence. Precedence goes from left to right in the order of the + * signature of the function the overloaded arguments were passed to, except + * subclasses are always considered before superclasses. + * + * If the result of calling __torch_function__ is NotImplemented, the + * next implementation in the precedence order is called. If all + * arguments return NotImplemented from their __torch_function__ + * implementation, a TypeError is raised in Python. + * + * Assumes overloaded_args has at least one entry. All entries must have + * a __torch_function__ attribute that resolves to a callable that + * accepts a torch API function, a tuple of arguments, and a dict of + * keyword arguments for the torch API function. + * + * It is sufficient to call PythonArgs::has_torch_function before + * calling this function to verify that there are valid arguments + * present. If that is not done then special care must be taken to + * ensure there are arguments that are overloaded with + * __torch_function__. + * + * See torch._overrides.handle_torch_function for the equivalent + * code in the pure-python implementation. + * + * 'r' is a parsed PythonArgs instance, returned from + * PythonArgParser::parse. + * + * 'args' is a reference to the python tuple of arguments to the torch + * API function. + * + * 'kwargs' is a reference to the python dict of keyword arguments to + * the torch API function. + * + * 'torch_api' is a reference to a python torch API namespace. + * + * 'torch_api_function' is the reference to the original torch method, usually, + * we can use torch_api and func_name to get torch_api_function. In some cases, + * e.g., torch custom op, we create the function in C++, if we still use + * torch_api and func_name to fetch original api, a cyclic call will happen. + * + * 'overloaded_args' is the args which have overloaded __torch_function__. + * + * 'func_name' is the named of the original torch method. + * + * TODO: we could use different names for the following 'handle_torch_function' + * instead of overloading. + * + */ +// Used for Tensor methods with arguments. +auto handle_torch_function( + PythonArgs& r, + PyObject* self, + PyObject* args, + PyObject* kwargs, + PyObject* torch_api, + const char* module_name, + const char* func_name_override = nullptr) -> PyObject*; + +// Used for functions which needs to parse python args. +auto handle_torch_function( + PythonArgs& r, + PyObject* args, + PyObject* kwargs, + PyObject* torch_api, + const char* module_name, + const char* func_name_override = nullptr) -> PyObject*; + +// Used for functions that have no argument parsing. +auto handle_torch_function( + PyObject* self, + const std::string& func_name, + PyObject* args = nullptr, + PyObject* kwargs = nullptr, + PyObject* torch_api = THPVariableClass, + const std::string& module_name = "torch.Tensor") -> PyObject*; + +// Used for functions created in C++, e.g., C++ custom op, which doesn't use +// PythonArgParser to get overloaded_args. +enum class TorchFunctionName { TorchFunction, TorchDispatch }; + +auto TORCH_PYTHON_API handle_torch_function_no_python_arg_parser( + at::ArrayRef overloaded_args, + PyObject* args, + PyObject* kwargs, + const char* func_name, + PyObject* torch_api_function, + const char* module_name, + TorchFunctionName torch_function_name = TorchFunctionName::TorchFunction) + -> PyObject*; + +// Used for getters of Tensor properties +auto handle_torch_function_getter( + THPVariable* self, + const std::string& property_name) -> PyObject*; + +// Used for setters of Tensor properties. +auto handle_torch_function_setter( + THPVariable* self, + const std::string& property_name, + PyObject* value) -> int; + +// Used for __getitem__ and __setitem__ +auto handle_torch_function_indexing( + PyObject* self, + PyObject* index, + PyObject* val = nullptr) -> PyObject*; + +/* + * Check if the input obj is Tensor type, including its subclass, or overloaded + * type. If the type defines __torch_function__, it also returns true. + * Otherwise returns flase. If the class is not torch.Tensor, and it defines + * __torch_function__, we append obj to overloaded_args. + * + * 'obj': the input argument to be checked + * 'overloaded_args': the vector to append the overloaded args. + */ +bool is_tensor_and_append_overloaded( + PyObject* obj, + std::vector* overloaded_args); + +/* + * Check if the input obj is Tensor List or Tensor Tuple type. First check + * whether obj is Tuple or List type, if true, iterate over each element and + * check whether it is Tensor type, including its subclass or overloaded type. + * At the same time, the overloaded arg is appended to the overloaded_args. + * + * 'obj': the input argument to be checked + * 'overloaded_args': the vector to append the overloaded args. + * 'argnum': the number of total arguments of the function being checked. + * 'throw_error': whether throw error if any element in the list or tuple is + * not tensor type or overloaded. + */ +bool is_tensor_list_and_append_overloaded( + PyObject* obj, + std::vector* overloaded_args, + size_t argnum, + bool throw_error); + +/* Given an argument that is definitely a tensor and is definitely overloaded, + * append it to the overloaded arguments list. Use this instead of + * is_tensor_and_append_overloaded in situations where you have a PyObject + * and you know it definitely is a Tensor and it is definitely overloaded. + * + * 'overloaded_args': the vector to append the overloaded args + * 'obj': the input tensor that is overloaded + */ +void append_overloaded_tensor( + std::vector* overloaded_args, + PyObject* obj); + +/* Given an argument that is definitely a type and is definitely overloaded, + * append it to the overloaded arguments list. Use this only with + * __torch_dispatch__, where we operate on classes that have a + * __torch_dispatch__ classmethod. + * + * 'overloaded_args': the vector to append the overloaded type + * 'obj': the input class that has a __torch_dispatch__ classmethod. + */ +void append_overloaded_type( + std::vector* overloaded_args, + PyObject* obj); + +} // namespace torch diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_compat.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_compat.h new file mode 100644 index 0000000000000000000000000000000000000000..ef3c7ca1f22d7c6a46fcf204283e7d5ac6d70051 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_compat.h @@ -0,0 +1,45 @@ +#ifndef PYTHON_COMPAT +#define PYTHON_COMPAT + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// PyTorch-only compat functions + +#define IS_PYTHON_3_11_PLUS PY_VERSION_HEX >= 0x030B00C1 +#define IS_PYTHON_3_12_PLUS PY_VERSION_HEX >= 0x030C0000 +#define IS_PYTHON_3_13_PLUS PY_VERSION_HEX >= 0x030D0000 +#define IS_PYTHON_3_14_PLUS PY_VERSION_HEX >= 0x030E0000 + +static inline int PyCode_GetNCellvars(PyCodeObject* code) { +// gh-26364 added co_ncellvars to Python 3.11.0rc1 +#if IS_PYTHON_3_11_PLUS + return code->co_ncellvars; +#else + return PyTuple_GET_SIZE(code->co_cellvars); +#endif +} + +static inline int PyCode_GetNFreevars(PyCodeObject* code) { +// gh-26364 added co_nfreevars to Python 3.11.0rc1 +#if IS_PYTHON_3_11_PLUS + return code->co_nfreevars; +#else + return PyTuple_GET_SIZE(code->co_freevars); +#endif +} + +// Provided by CPython but getting the header for them is very hard +#if IS_PYTHON_3_11_PLUS +PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference* self); +#else +extern void _PyWeakref_ClearRef(PyWeakReference* self); +#endif + +#ifdef __cplusplus +} +#endif +#endif // PYTHON_COMPAT diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_dispatch.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..aeb20a33859218cd40c2bbbb1ba5175a8e809d60 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_dispatch.h @@ -0,0 +1,16 @@ +#include +#include + +namespace torch::impl::dispatch { + +void initDispatchBindings(PyObject* module); + +void python_op_registration_trampoline_impl( + const c10::OperatorHandle& op, + c10::DispatchKey key, + c10::DispatchKeySet keyset, + torch::jit::Stack* stack, + bool with_keyset, + bool with_op); + +} // namespace torch::impl::dispatch diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_scalars.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_scalars.h new file mode 100644 index 0000000000000000000000000000000000000000..eeeebb709c93c75ae96e72008d65727882f7bb04 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_scalars.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace torch::utils { + +template +inline T unpackIntegral(PyObject* obj, const char* type) { +#if PY_VERSION_HEX >= 0x030a00f0 + // In Python-3.10 floats can no longer be silently converted to integers + // Keep backward compatible behavior for now + if (PyFloat_Check(obj)) { + return c10::checked_convert(THPUtils_unpackDouble(obj), type); + } + return c10::checked_convert(THPUtils_unpackLong(obj), type); +#else + return static_cast(THPUtils_unpackLong(obj)); +#endif +} + +inline void store_scalar(void* data, at::ScalarType scalarType, PyObject* obj) { + switch (scalarType) { + case at::kByte: + *(uint8_t*)data = unpackIntegral(obj, "uint8"); + break; + case at::kUInt16: + *(uint16_t*)data = unpackIntegral(obj, "uint16"); + break; + case at::kUInt32: + *(uint32_t*)data = unpackIntegral(obj, "uint32"); + break; + case at::kUInt64: + // NB: This doesn't allow implicit conversion of float to int + *(uint64_t*)data = THPUtils_unpackUInt64(obj); + break; + case at::kChar: + *(int8_t*)data = unpackIntegral(obj, "int8"); + break; + case at::kShort: + *(int16_t*)data = unpackIntegral(obj, "int16"); + break; + case at::kInt: + *(int32_t*)data = unpackIntegral(obj, "int32"); + break; + case at::kLong: + *(int64_t*)data = unpackIntegral(obj, "int64"); + break; + case at::kHalf: + *(at::Half*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + case at::kFloat: + *(float*)data = (float)THPUtils_unpackDouble(obj); + break; + case at::kDouble: + *(double*)data = THPUtils_unpackDouble(obj); + break; + case at::kComplexHalf: + *(c10::complex*)data = + (c10::complex)static_cast>( + THPUtils_unpackComplexDouble(obj)); + break; + case at::kComplexFloat: + *(c10::complex*)data = + (c10::complex)THPUtils_unpackComplexDouble(obj); + break; + case at::kComplexDouble: + *(c10::complex*)data = THPUtils_unpackComplexDouble(obj); + break; + case at::kBool: + *(bool*)data = THPUtils_unpackNumberAsBool(obj); + break; + case at::kBFloat16: + *(at::BFloat16*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + case at::kFloat8_e5m2: + *(at::Float8_e5m2*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + case at::kFloat8_e5m2fnuz: + *(at::Float8_e5m2fnuz*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + case at::kFloat8_e4m3fn: + *(at::Float8_e4m3fn*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + case at::kFloat8_e4m3fnuz: + *(at::Float8_e4m3fnuz*)data = + at::convert(THPUtils_unpackDouble(obj)); + break; + default: + throw std::runtime_error("invalid type"); + } +} + +inline PyObject* load_scalar(const void* data, at::ScalarType scalarType) { + switch (scalarType) { + case at::kByte: + return THPUtils_packInt64(*(uint8_t*)data); + case at::kUInt16: + return THPUtils_packInt64(*(uint16_t*)data); + case at::kUInt32: + return THPUtils_packUInt32(*(uint32_t*)data); + case at::kUInt64: + return THPUtils_packUInt64(*(uint64_t*)data); + case at::kChar: + return THPUtils_packInt64(*(int8_t*)data); + case at::kShort: + return THPUtils_packInt64(*(int16_t*)data); + case at::kInt: + return THPUtils_packInt64(*(int32_t*)data); + case at::kLong: + return THPUtils_packInt64(*(int64_t*)data); + case at::kHalf: + return PyFloat_FromDouble( + at::convert(*(at::Half*)data)); + case at::kFloat: + return PyFloat_FromDouble(*(float*)data); + case at::kDouble: + return PyFloat_FromDouble(*(double*)data); + case at::kComplexHalf: { + auto data_ = reinterpret_cast*>(data); + return PyComplex_FromDoubles(data_->real(), data_->imag()); + } + case at::kComplexFloat: { + auto data_ = reinterpret_cast*>(data); + return PyComplex_FromDoubles(data_->real(), data_->imag()); + } + case at::kComplexDouble: + return PyComplex_FromCComplex( + *reinterpret_cast((c10::complex*)data)); + case at::kBool: + // Don't use bool*, since it may take out-of-range byte as bool. + // Instead, we cast explicitly to avoid ASAN error. + return PyBool_FromLong(static_cast(*(uint8_t*)data)); + case at::kBFloat16: + return PyFloat_FromDouble( + at::convert(*(at::BFloat16*)data)); + case at::kFloat8_e5m2: + return PyFloat_FromDouble( + at::convert(*(at::Float8_e5m2*)data)); + case at::kFloat8_e4m3fn: + return PyFloat_FromDouble( + at::convert(*(at::Float8_e4m3fn*)data)); + case at::kFloat8_e5m2fnuz: + return PyFloat_FromDouble(at::convert( + *(at::Float8_e5m2fnuz*)data)); + case at::kFloat8_e4m3fnuz: + return PyFloat_FromDouble(at::convert( + *(at::Float8_e4m3fnuz*)data)); + default: + throw std::runtime_error("invalid type"); + } +} + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_strings.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_strings.h new file mode 100644 index 0000000000000000000000000000000000000000..a6cb8d5c30b502b80e71d9c6c205f32e0c4c1446 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_strings.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include +#include + +// Utilities for handling Python strings. Note that PyString, when defined, is +// the same as PyBytes. + +// Returns true if obj is a bytes/str or unicode object +// As of Python 3.6, this does not require the GIL +inline bool THPUtils_checkString(PyObject* obj) { + return PyBytes_Check(obj) || PyUnicode_Check(obj); +} + +// Unpacks PyBytes (PyString) or PyUnicode as std::string +// PyBytes are unpacked as-is. PyUnicode is unpacked as UTF-8. +// NOTE: this method requires the GIL +inline std::string THPUtils_unpackString(PyObject* obj) { + if (PyBytes_Check(obj)) { + size_t size = PyBytes_GET_SIZE(obj); + return std::string(PyBytes_AS_STRING(obj), size); + } + if (PyUnicode_Check(obj)) { + Py_ssize_t size = 0; + const char* data = PyUnicode_AsUTF8AndSize(obj, &size); + if (!data) { + throw std::runtime_error("error unpacking string as utf-8"); + } + return std::string(data, (size_t)size); + } + throw std::runtime_error("unpackString: expected bytes or unicode object"); +} + +// Unpacks PyBytes (PyString) or PyUnicode as std::string_view +// PyBytes are unpacked as-is. PyUnicode is unpacked as UTF-8. +// NOTE: If `obj` is destroyed, then the non-owning std::string_view will +// become invalid. If the string needs to be accessed at any point after +// `obj` is destroyed, then the std::string_view should be copied into +// a std::string, or another owning object, and kept alive. For an example, +// look at how IValue and autograd nodes handle std::string_view arguments. +// NOTE: this method requires the GIL +inline std::string_view THPUtils_unpackStringView(PyObject* obj) { + if (PyBytes_Check(obj)) { + size_t size = PyBytes_GET_SIZE(obj); + return std::string_view(PyBytes_AS_STRING(obj), size); + } + if (PyUnicode_Check(obj)) { + Py_ssize_t size = 0; + const char* data = PyUnicode_AsUTF8AndSize(obj, &size); + if (!data) { + throw std::runtime_error("error unpacking string as utf-8"); + } + return std::string_view(data, (size_t)size); + } + throw std::runtime_error("unpackString: expected bytes or unicode object"); +} + +inline PyObject* THPUtils_packString(const char* str) { + return PyUnicode_FromString(str); +} + +inline PyObject* THPUtils_packString(const std::string& str) { + return PyUnicode_FromStringAndSize( + str.c_str(), static_cast(str.size())); +} + +inline PyObject* THPUtils_internString(const std::string& str) { + return PyUnicode_InternFromString(str.c_str()); +} + +// Precondition: THPUtils_checkString(obj) must be true +inline bool THPUtils_isInterned(PyObject* obj) { + return PyUnicode_CHECK_INTERNED(obj); +} + +// Precondition: THPUtils_checkString(obj) must be true +inline void THPUtils_internStringInPlace(PyObject** obj) { + PyUnicode_InternInPlace(obj); +} + +/* + * Reference: + * https://github.com/numpy/numpy/blob/f4c497c768e0646df740b647782df463825bfd27/numpy/core/src/common/get_attr_string.h#L42 + * + * Stripped down version of PyObject_GetAttrString, + * avoids lookups for None, tuple, and List objects, + * and doesn't create a PyErr since this code ignores it. + * + * This can be much faster then PyObject_GetAttrString where + * exceptions are not used by caller. + * + * 'obj' is the object to search for attribute. + * + * 'name' is the attribute to search for. + * + * Returns a py::object wrapping the return value. If the attribute lookup + * failed the value will be NULL. + * + */ + +inline py::object PyObject_FastGetAttrString(PyObject* obj, const char* name) { + PyTypeObject* tp = Py_TYPE(obj); + PyObject* res = (PyObject*)nullptr; + + /* Attribute referenced by (char *)name */ + if (tp->tp_getattr != nullptr) { + // This is OK per https://bugs.python.org/issue39620 + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + res = (*tp->tp_getattr)(obj, const_cast(name)); + if (res == nullptr) { + PyErr_Clear(); + } + } + /* Attribute referenced by (PyObject *)name */ + else if (tp->tp_getattro != nullptr) { + auto w = py::reinterpret_steal(THPUtils_internString(name)); + if (w.ptr() == nullptr) { + return py::object(); + } + res = (*tp->tp_getattro)(obj, w.ptr()); + if (res == nullptr) { + PyErr_Clear(); + } + } + return py::reinterpret_steal(res); +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_stub.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..336c530d2b1faa35d7c87399846bb834a84ad569 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_stub.h @@ -0,0 +1,4 @@ +#pragma once + +struct _object; +using PyObject = _object; diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_torch_function_mode.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_torch_function_mode.h new file mode 100644 index 0000000000000000000000000000000000000000..56d6329378734128806e054dfe45947e505b7f4e --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_torch_function_mode.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace torch::overrides { + +struct StashTorchFunctionModeGuard { + StashTorchFunctionModeGuard() { + cur_mode_ = at::impl::PythonTorchFunctionTLS::pop_stack(); + } + ~StashTorchFunctionModeGuard() { + at::impl::PythonTorchFunctionTLS::push_onto_stack(cur_mode_); + } + StashTorchFunctionModeGuard(const StashTorchFunctionModeGuard&) = delete; + StashTorchFunctionModeGuard(StashTorchFunctionModeGuard&&) = delete; + StashTorchFunctionModeGuard& operator=(const StashTorchFunctionModeGuard&) = + delete; + StashTorchFunctionModeGuard& operator=(StashTorchFunctionModeGuard&&) = + delete; + + const std::shared_ptr& get_cur_mode() { + return cur_mode_; + } + + private: + std::shared_ptr cur_mode_; +}; + +} // namespace torch::overrides diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_tuples.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_tuples.h new file mode 100644 index 0000000000000000000000000000000000000000..598e69a5dd50e98b62020dda5bdd626e530108e5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/python_tuples.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +inline void THPUtils_packInt64Array( + PyObject* tuple, + size_t size, + const int64_t* sizes) { + for (size_t i = 0; i != size; ++i) { + PyObject* i64 = THPUtils_packInt64(sizes[i]); + if (!i64) { + throw python_error(); + } + PyTuple_SET_ITEM(tuple, i, i64); + } +} + +inline PyObject* THPUtils_packInt64Array(size_t size, const int64_t* sizes) { + THPObjectPtr tuple(PyTuple_New(static_cast(size))); + if (!tuple) + throw python_error(); + THPUtils_packInt64Array(tuple.get(), size, sizes); + return tuple.release(); +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pythoncapi_compat.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pythoncapi_compat.h new file mode 100644 index 0000000000000000000000000000000000000000..c0feaa20904ddd66c4beb498d53dae4a52b7b2a0 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/pythoncapi_compat.h @@ -0,0 +1,1520 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// Python 3.11.0b4 added PyFrame_Back() to Python.h +#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) +# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +#endif + + +#ifndef _Py_CAST +# define _Py_CAST(type, expr) ((type)(expr)) +#endif + +// Static inline functions should use _Py_NULL rather than using directly NULL +// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, +// _Py_NULL is defined as nullptr. +#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +#else +# define _Py_NULL NULL +#endif + +// Cast argument to PyObject* type. +#ifndef _PyObject_CAST +# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#endif + + +// bpo-42262 added Py_NewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +static inline PyObject* _Py_NewRef(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} +#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +static inline PyObject* _Py_XNewRef(PyObject *obj) +{ + Py_XINCREF(obj); + return obj; +} +#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +{ + ob->ob_refcnt = refcnt; +} +#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) +#endif + + +// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. +// It is excluded from the limited C API. +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ + } while (0) + +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ + } while (0) +#endif + + +// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() +// to Python 3.10.0b1. +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) +# define Py_Is(x, y) ((x) == (y)) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) +# define Py_IsNone(x) Py_Is(x, Py_None) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) +# define Py_IsTrue(x) Py_Is(x, Py_True) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) +# define Py_IsFalse(x) Py_Is(x, Py_False) +#endif + + +// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ + ob->ob_type = type; +} +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +{ + ob->ob_size = size; +} +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) +#endif + + +// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + assert(frame->f_code != _Py_NULL); + return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); +} +#endif + +static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) +{ + PyCodeObject *code = PyFrame_GetCode(frame); + Py_DECREF(code); + return code; +} + + +// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) +{ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(back); + return back; +} +#endif + + +// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } +#else + PyFrame_FastToLocals(frame); +#endif + return Py_NewRef(frame->f_locals); +} +#endif + + +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_globals); +} +#endif + + +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_builtins); +} +#endif + + +// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline int PyFrame_GetLasti(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030A00A7 + // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, + // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) + // instructions. + if (frame->f_lasti < 0) { + return -1; + } + return frame->f_lasti * 2; +#else + return frame->f_lasti; +#endif +} +#endif + + +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); +#else + value = _PyDict_GetItemWithError(locals, name); +#endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); +#else + PyErr_SetString(PyExc_NameError, "variable does not exist"); +#endif + return NULL; + } + return Py_NewRef(value); +} +#endif + + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* +PyFrame_GetVarString(PyFrameObject *frame, const char *name) +{ + PyObject *name_obj, *value; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(name); +#else + name_obj = PyString_FromString(name); +#endif + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + + +// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState * +PyThreadState_GetInterpreter(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->interp; +} +#endif + + +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* +_PyThreadState_GetFrameBorrow(PyThreadState *tstate) +{ + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + Py_XDECREF(frame); + return frame; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState* PyInterpreterState_Get(void) +{ + PyThreadState *tstate; + PyInterpreterState *interp; + + tstate = PyThreadState_GET(); + if (tstate == _Py_NULL) { + Py_FatalError("GIL released (tstate is NULL)"); + } + interp = tstate->interp; + if (interp == _Py_NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->id; +} +#endif + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; +#else + tstate->use_tracing = 0; +#endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + int use_tracing = (tstate->c_tracefunc != _Py_NULL + || tstate->c_profilefunc != _Py_NULL); + tstate->tracing--; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; +#else + tstate->use_tracing = use_tracing; +#endif +} +#endif + + +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +static inline PyObject* PyObject_CallNoArgs(PyObject *func) +{ + return PyObject_CallFunctionObjArgs(func, NULL); +} +#endif + + +// bpo-39245 made PyObject_CallOneArg() public (previously called +// _PyObject_CallOneArg) in Python 3.9.0a4 +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + return PyObject_CallFunctionObjArgs(func, arg, NULL); +} +#endif + + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +static inline int +PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) +{ + int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + + +// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + const char *name, *dot; + + if (PyType_Ready(type) < 0) { + return -1; + } + + // inline _PyType_Name() + name = type->tp_name; + assert(name != _Py_NULL); + dot = strrchr(name, '.'); + if (dot != _Py_NULL) { + name = dot + 1; + } + + return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +} +#endif + + +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. +#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsTracked(PyObject* obj) +{ + return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); +} +#endif + +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsFinalized(PyObject *obj) +{ + PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; + return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); +} +#endif + + +// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { + return Py_TYPE(ob) == type; +} +#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack2(double x, char *p, int le) +{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack2(const char *p, int le) +{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +#endif + + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack4(double x, char *p, int le) +{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } + +static inline int PyFloat_Pack8(double x, char *p, int le) +{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack4(const char *p, int le) +{ return _PyFloat_Unpack4((const unsigned char *)p, le); } + +static inline double PyFloat_Unpack8(const char *p, int le) +{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +#endif + + +// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCode(PyCodeObject *code) +{ + return Py_NewRef(code->co_code); +} +#endif + + +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + + +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif +#endif + + +// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A0 +static inline PyObject* PyImport_AddModuleRef(const char *name) +{ + return Py_XNewRef(PyImport_AddModule(name)); +} +#endif + + +// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D0000 +static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +{ + PyObject *obj; + if (ref != NULL && !PyWeakref_Check(ref)) { + *pobj = NULL; + PyErr_SetString(PyExc_TypeError, "expected a weakref"); + return -1; + } + obj = PyWeakref_GetObject(ref); + if (obj == NULL) { + // SystemError if ref is NULL + *pobj = NULL; + return -1; + } + if (obj == Py_None) { + *pobj = NULL; + return 0; + } + *pobj = Py_NewRef(obj); + return (*pobj != NULL); +} +#endif + + +// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 +#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET +# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#endif + +// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 +#if PY_VERSION_HEX < 0x030800B1 +static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} +#endif + + +// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 +static inline PyObject* +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ +#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 + return _PyObject_Vectorcall(callable, args, nargsf, kwnames); +#else + PyObject *posargs = NULL, *kwargs = NULL; + PyObject *res; + Py_ssize_t nposargs, nkwargs, i; + + if (nargsf != 0 && args == NULL) { + PyErr_BadInternalCall(); + goto error; + } + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + goto error; + } + + nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); + if (kwnames) { + nkwargs = PyTuple_GET_SIZE(kwnames); + } + else { + nkwargs = 0; + } + + posargs = PyTuple_New(nposargs); + if (posargs == NULL) { + goto error; + } + if (nposargs) { + for (i=0; i < nposargs; i++) { + PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); + args++; + } + } + + if (nkwargs) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + goto error; + } + + for (i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = *args; + args++; + if (PyDict_SetItem(kwargs, key, value) < 0) { + goto error; + } + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(callable, posargs, kwargs); + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return res; + +error: + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return NULL; +#endif +} +#endif + + +// gh-106521 added PyObject_GetOptionalAttr() and +// PyObject_GetOptionalAttrString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) +{ + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 +#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + return _PyObject_LookupAttr(obj, attr_name, result); +#else + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; +#endif +} + +static inline int +PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +{ + PyObject *name_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(attr_name); +#else + name_obj = PyString_FromString(attr_name); +#endif + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} +#endif + + +// gh-106307 added PyObject_GetOptionalAttr() and +// PyMapping_GetOptionalItemString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) +{ + *result = PyObject_GetItem(obj, key); + if (*result) { + return 1; + } + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; +} + +static inline int +PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) +{ + PyObject *key_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + key_obj = PyUnicode_FromString(key); +#else + key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyMapping_GetOptionalItem(obj, key_obj, result); + Py_DECREF(key_obj); + return rc; +} +#endif + +// gh-108511 added PyMapping_HasKeyWithError() and +// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItem(obj, key, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItemString(obj, key, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-108511 added PyObject_HasAttrWithError() and +// PyObject_HasAttrStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttr(obj, attr, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttrString(obj, attr, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) +{ + int res; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); +#else + PyObject *key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + + +// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// bpo-1856 added _Py_Finalizing to Python 3.2.1b1. +// _Py_IsFinalizing() was added to PyPy 7.3.0. +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ + && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int Py_IsFinalizing(void) +{ +#if PY_VERSION_HEX >= 0x030700A1 + // _Py_IsFinalizing() was added to Python 3.7.0a1. + return _Py_IsFinalizing(); +#else + return (_Py_Finalizing != NULL); +#endif +} +#endif + + +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ +#ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; +#else + return _PyLong_AsInt(obj); +#endif +} +#endif + + +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return -1; + } + Py_VISIT(*dict); + return 0; +} + +static inline void +PyObject_ClearManagedDict(PyObject *obj) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return; + } + Py_CLEAR(*dict); +} +#endif + +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// Python 3.5.2 added _PyThreadState_UncheckedGet(). +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +static inline PyThreadState* +PyThreadState_GetUnchecked(void) +{ + return _PyThreadState_UncheckedGet(); +} +#endif + +// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) +{ + Py_ssize_t len; + const void *utf8; + PyObject *exc_type, *exc_value, *exc_tb; + int res; + + // API cannot report errors so save/restore the exception + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() +#if PY_VERSION_HEX >= 0x030300A1 + if (PyUnicode_IS_ASCII(unicode)) { + utf8 = PyUnicode_DATA(unicode); + len = PyUnicode_GET_LENGTH(unicode); + } + else { + utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); + if (utf8 == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + } + + if (len != str_len) { + res = 0; + goto done; + } + res = (memcmp(utf8, str, (size_t)len) == 0); +#else + PyObject *bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + +#if PY_VERSION_HEX >= 0x03000000 + len = PyBytes_GET_SIZE(bytes); + utf8 = PyBytes_AS_STRING(bytes); +#else + len = PyString_GET_SIZE(bytes); + utf8 = PyString_AS_STRING(bytes); +#endif + if (len != str_len) { + Py_DECREF(bytes); + res = 0; + goto done; + } + + res = (memcmp(utf8, str, (size_t)len) == 0); + Py_DECREF(bytes); +#endif + +done: + PyErr_Restore(exc_type, exc_value, exc_tb); + return res; +} + +static inline int +PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) +{ + return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); +} +#endif + + +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyList_Extend(PyObject *list, PyObject *iterable) +{ + return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); +} + +static inline int +PyList_Clear(PyObject *list) +{ + return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); +} +#endif + +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) +{ + PyObject *value; + + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + if (result) { + *result = NULL; + } + return -1; + } + + // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. + // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13.0a1 removed _PyDict_Pop(). +#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 + value = PyObject_CallMethod(dict, "pop", "O", key); +#elif PY_VERSION_HEX < 0x030600b3 + value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); +#else + value = _PyDict_Pop(dict, key, NULL); +#endif + if (value == NULL) { + if (result) { + *result = NULL; + } + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; + } + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; +} + +static inline int +PyDict_PopString(PyObject *dict, const char *key, PyObject **result) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + if (result != NULL) { + *result = NULL; + } + return -1; + } + + int res = PyDict_Pop(dict, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +#if PY_VERSION_HEX < 0x030200A4 +// Python 3.2.0a4 added Py_hash_t type +typedef Py_ssize_t Py_hash_t; +#endif + + +// gh-111545 added Py_HashPointer() to Python 3.13.0a3 +#if PY_VERSION_HEX < 0x030D00A3 +static inline Py_hash_t Py_HashPointer(const void *ptr) +{ +#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) + return _Py_HashPointer(ptr); +#else + return _Py_HashPointer(_Py_CAST(void*, ptr)); +#endif +} +#endif + + +// Python 3.13a4 added a PyTime API. +// Use the private API added to Python 3.5. +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +typedef _PyTime_t PyTime_t; +#define PyTime_MIN _PyTime_MIN +#define PyTime_MAX _PyTime_MAX + +static inline double PyTime_AsSecondsDouble(PyTime_t t) +{ return _PyTime_AsSecondsDouble(t); } + +static inline int PyTime_Monotonic(PyTime_t *result) +{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); } + +static inline int PyTime_Time(PyTime_t *result) +{ return _PyTime_GetSystemClockWithInfo(result, NULL); } + +static inline int PyTime_PerfCounter(PyTime_t *result) +{ +#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) + return _PyTime_GetPerfCounterWithInfo(result, NULL); +#elif PY_VERSION_HEX >= 0x03070000 + // Call time.perf_counter_ns() and convert Python int object to PyTime_t. + // Cache time.perf_counter_ns() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter_ns"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + long long value = PyLong_AsLongLong(res); + Py_DECREF(res); + + if (value == -1 && PyErr_Occurred()) { + return -1; + } + + Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); + *result = (PyTime_t)value; + return 0; +#else + // Call time.perf_counter() and convert C double to PyTime_t. + // Cache time.perf_counter() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + double d = PyFloat_AsDouble(res); + Py_DECREF(res); + + if (d == -1.0 && PyErr_Occurred()) { + return -1; + } + + // Avoid floor() to avoid having to link to libm + *result = (PyTime_t)(d * 1e9); + return 0; +#endif +} + +#endif + +// gh-111389 added hash constants to Python 3.13.0a5. These constants were +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +#if (!defined(PyHASH_BITS) \ + && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ + && PYPY_VERSION_NUM >= 0x07090000))) +# define PyHASH_BITS _PyHASH_BITS +# define PyHASH_MODULUS _PyHASH_MODULUS +# define PyHASH_INF _PyHASH_INF +# define PyHASH_IMAG _PyHASH_IMAG +#endif + + +// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() +// to Python 3.13.0a6 +#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) + +#define Py_CONSTANT_NONE 0 +#define Py_CONSTANT_FALSE 1 +#define Py_CONSTANT_TRUE 2 +#define Py_CONSTANT_ELLIPSIS 3 +#define Py_CONSTANT_NOT_IMPLEMENTED 4 +#define Py_CONSTANT_ZERO 5 +#define Py_CONSTANT_ONE 6 +#define Py_CONSTANT_EMPTY_STR 7 +#define Py_CONSTANT_EMPTY_BYTES 8 +#define Py_CONSTANT_EMPTY_TUPLE 9 + +static inline PyObject* Py_GetConstant(unsigned int constant_id) +{ + static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + + if (constants[Py_CONSTANT_NONE] == NULL) { + constants[Py_CONSTANT_NONE] = Py_None; + constants[Py_CONSTANT_FALSE] = Py_False; + constants[Py_CONSTANT_TRUE] = Py_True; + constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; + constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; + + constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); + if (constants[Py_CONSTANT_ZERO] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); + if (constants[Py_CONSTANT_ONE] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); + if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { + goto fatal_error; + } + // goto dance to avoid compiler warnings about Py_FatalError() + goto init_done; + +fatal_error: + // This case should never happen + Py_FatalError("Py_GetConstant() failed to get constants"); + } + +init_done: + if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { + return Py_NewRef(constants[constant_id]); + } + else { + PyErr_BadInternalCall(); + return NULL; + } +} + +static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) +{ + PyObject *obj = Py_GetConstant(constant_id); + Py_XDECREF(obj); + return obj; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline PyObject * +PyList_GetItemRef(PyObject *op, Py_ssize_t index) +{ + PyObject *item = PyList_GetItem(op, index); + Py_XINCREF(item); + return item; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline int +PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, + PyObject **result) +{ + PyObject *value; + if (PyDict_GetItemRef(d, key, &value) < 0) { + // get error + if (result) { + *result = NULL; + } + return -1; + } + if (value != NULL) { + // present + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; + } + + // missing: set the item + if (PyDict_SetItem(d, key, default_value) < 0) { + // set error + if (result) { + *result = NULL; + } + return -1; + } + if (result) { + *result = Py_NewRef(default_value); + } + return 0; +} +#endif + +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + +static inline int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + +static inline int +PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 + +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyLong_GetSign(PyObject *obj, int *sign) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + + *sign = _PyLong_Sign(obj); + return 0; +} +#endif + + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/structseq.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/structseq.h new file mode 100644 index 0000000000000000000000000000000000000000..60e3429b50cd58d2e879b32894000463418b4cda --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/structseq.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace torch::utils { + +PyObject* returned_structseq_repr(PyStructSequence* obj); + +} diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_dtypes.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_dtypes.h new file mode 100644 index 0000000000000000000000000000000000000000..9a947b380e92dbcd3a71f661b6dc3b2ac622cfdd --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_dtypes.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +namespace torch::utils { + +std::pair getDtypeNames(at::ScalarType scalarType); + +void initializeDtypes(); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_memoryformats.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_memoryformats.h new file mode 100644 index 0000000000000000000000000000000000000000..b9268070e34cdd9872c81990807fd70a8bab52f5 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_memoryformats.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace torch::utils { + +void initializeMemoryFormats(); + +// This methods returns a borrowed reference! +TORCH_PYTHON_API PyObject* getTHPMemoryFormat(c10::MemoryFormat); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_new.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_new.h new file mode 100644 index 0000000000000000000000000000000000000000..088f8d1927c4732d8543ca82a39c08247257066a --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_new.h @@ -0,0 +1,136 @@ +#pragma once + +#include +#include + +#include + +namespace torch::utils { + +// NOTE: [torch.tensor, lift_fresh, and device movement] +// +// The `only_lift_cpu_tensors` flag controls what happens on torch.tensor([1, 2, +// 3], device="cuda") (or any non-CPU devices). +// +// If false (default): +// - the data gets moved into a CPU Tensor +// - then, it gets moved to cuda (via .to) +// - finally, we call lift_fresh() on it. +// Steps 1 and 2 happen with all modes disabled. +// +// If true: +// - the data gets moved into a CPU Tensor (with correct dtype) +// - we call lift_fresh() on it +// - finally, we move it to cuda (via .to) +// Step 1 happens with all modes disabled. +// +// `only_lift_cpu_tensors=true` is useful to prevent CUDA initialization under +// FakeTensorMode because it avoids moving concrete data to CUDA. +TORCH_API bool only_lift_cpu_tensors(); +TORCH_API void set_only_lift_cpu_tensors(bool value); + +at::Tensor base_tensor_ctor(PyObject* args, PyObject* kwargs); +at::Tensor legacy_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +at::Tensor legacy_tensor_new( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +at::Tensor indexing_tensor_from_data( + c10::TensorOptions options, + at::ScalarType scalar_type, + std::optional device, + PyObject* data); +at::Tensor sparse_coo_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +void _validate_sparse_coo_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); + +at::Tensor sparse_compressed_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor sparse_csr_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor sparse_csc_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor sparse_bsr_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor sparse_bsc_tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); + +void _validate_sparse_compressed_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +void _validate_sparse_csr_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +void _validate_sparse_csc_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +void _validate_sparse_bsr_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +void _validate_sparse_bsc_tensor_args( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); + +at::Tensor tensor_ctor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor as_tensor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PythonArgs& r); +at::Tensor new_tensor( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +at::Tensor new_ones( + c10::DispatchKey dispatch_key, + at::ScalarType scalar_type, + PyObject* args, + PyObject* kwargs); +at::Tensor tensor_frombuffer( + PyObject* buffer, + at::ScalarType dtype, + int64_t count, + int64_t offset, + bool requires_grad); +at::Tensor tensor_fromDLPack(PyObject* data); +at::Tensor asarray( + PyObject* obj, + std::optional dtype, + std::optional device, + std::optional copy, + bool requires_grad); +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_numpy.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_numpy.h new file mode 100644 index 0000000000000000000000000000000000000000..a067af44a45271cc047ec4b2f471640f2727d3f3 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_numpy.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace torch::utils { + +PyObject* tensor_to_numpy(const at::Tensor& tensor, bool force = false); +at::Tensor tensor_from_numpy(PyObject* obj, bool warn_if_not_writeable = true); + +int aten_to_numpy_dtype(const at::ScalarType scalar_type); +at::ScalarType numpy_dtype_to_aten(int dtype); + +bool is_numpy_available(); +bool is_numpy_int(PyObject* obj); +bool is_numpy_bool(PyObject* obj); +bool is_numpy_scalar(PyObject* obj); + +void warn_numpy_not_writeable(); +at::Tensor tensor_from_cuda_array_interface(PyObject* obj); + +void validate_numpy_for_dlpack_deleter_bug(); +bool is_numpy_dlpack_deleter_bugged(); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_qschemes.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_qschemes.h new file mode 100644 index 0000000000000000000000000000000000000000..dc982efd1ff9b8f397221e5e016dbf70db9c3330 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/tensor_qschemes.h @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace torch::utils { + +PyObject* getTHPQScheme(at::QScheme qscheme); +void initializeQSchemes(); + +} // namespace torch::utils diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/torch_dispatch_mode.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/torch_dispatch_mode.h new file mode 100644 index 0000000000000000000000000000000000000000..45e567b9ee7c0dd5dce17a070283197d50b3eb02 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/torch_dispatch_mode.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +namespace torch::torch_dispatch_mode { + +struct StashTorchDispatchModeGuard { + public: + StashTorchDispatchModeGuard() { + if (c10::impl::TorchDispatchModeTLS::any_modes_set( + /*skip_infra_modes=*/true)) { + saved_mode_ = c10::impl::TorchDispatchModeTLS::pop_stack(); + } else { + auto mode_and_key = + c10::impl::TorchDispatchModeTLS::pop_highest_infra_mode(); + saved_mode_ = std::move(std::get<0>(mode_and_key)); + saved_mode_key_ = std::get<1>(mode_and_key); + } + } + + ~StashTorchDispatchModeGuard() { + if (saved_mode_key_.has_value()) { + c10::impl::TorchDispatchModeTLS::set_mode( + saved_mode_, saved_mode_key_.value()); + } else { + c10::impl::TorchDispatchModeTLS::push_non_infra_mode_onto_stack( + std::move(saved_mode_)); + } + } + StashTorchDispatchModeGuard(const StashTorchDispatchModeGuard&) = delete; + StashTorchDispatchModeGuard(StashTorchDispatchModeGuard&&) = delete; + StashTorchDispatchModeGuard& operator=(const StashTorchDispatchModeGuard&) = + delete; + StashTorchDispatchModeGuard& operator=(StashTorchDispatchModeGuard&&) = + delete; + + const std::shared_ptr& get_cur_mode() { + return saved_mode_; + } + + private: + std::shared_ptr saved_mode_; + std::optional saved_mode_key_; +}; + +struct StashTorchDispatchStackGuard { + public: + StashTorchDispatchStackGuard() { + auto old = c10::impl::TorchDispatchModeTLS::get_state(); + c10::impl::TorchDispatchModeTLS::set_state(std::move(saved_state_)); + saved_state_ = std::move(old); + } + StashTorchDispatchStackGuard(const StashTorchDispatchStackGuard&) = delete; + StashTorchDispatchStackGuard(StashTorchDispatchStackGuard&&) = delete; + StashTorchDispatchStackGuard& operator=(const StashTorchDispatchStackGuard&) = + delete; + StashTorchDispatchStackGuard& operator=(StashTorchDispatchStackGuard&&) = + delete; + + ~StashTorchDispatchStackGuard() { + c10::impl::TorchDispatchModeTLS::set_state(std::move(saved_state_)); + } + + private: + c10::impl::TorchDispatchModeTLS saved_state_; +}; + +} // namespace torch::torch_dispatch_mode diff --git a/lib/python3.12/site-packages/torch/include/torch/csrc/utils/variadic.h b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/variadic.h new file mode 100644 index 0000000000000000000000000000000000000000..44fe1028fe5c4eeaff2b73ce01771fea8e94a6f4 --- /dev/null +++ b/lib/python3.12/site-packages/torch/include/torch/csrc/utils/variadic.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace torch { + +using at::IterArgs; + +struct CountTensors : IterArgs { + size_t out = 0; + void operator()(const at::Tensor& x) { + out += 1; + } + void operator()(const std::optional& x) { + out += x.has_value(); + } + void operator()(at::ArrayRef xs) { + out += xs.size(); + } +}; + +template +size_t count_tensors(Args&&... args) { + return CountTensors().apply(std::forward(args)...).out; +} + +struct CountVariables : IterArgs { + size_t out = 0; + void operator()(const autograd::Variable& x) { + out += 1; + } + void operator()(at::ArrayRef xs) { + out += xs.size(); + } +}; + +template +inline size_t count_variables(Args&&... args) { + return CountVariables().apply(std::forward(args)...).out; +} + +//===----------------------------------------------------------------------===// +// std::index_sequence shim for C++11 +//===----------------------------------------------------------------------===// + +// A container of type-template parameter indices. +template +struct Indices {}; + +// Decrements the index N, adds N-1 to the list of indices and forwards +// whatever we already have. +template +struct MakeIndices : MakeIndices {}; + +// Partial specialization that forms our base case. When N is zero, we stop +// and define a typedef that will be visible to earlier classes due to +// inheritance. The typedef we define is an index list containing the numbers +// 0 through N-1. +template +struct MakeIndices<0, Is...> { + using indices = Indices; +}; + +//===----------------------------------------------------------------------===// +// Utilities +//===----------------------------------------------------------------------===// + +template +void apply(Function function, Ts&&... ts) { + // https://stackoverflow.com/questions/13978916/inserting-a-variadic-argument-list-into-a-vector + // Creates a dummy array, so that each function call is evaluated in order. + // `(function(), 0)` is because `function` should (!) return `void`, so + // according to the comma operator, it is evaluated and its result (`void`) + // is discarded. Then the zero is evaluated and used as an element in the + // array. The first zero ensures the array is not empty. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + int _[]{0, (function(std::forward(ts)), 0)...}; + (void)_; +} + +template < + typename ReturnType, + typename... Ts, + typename Function, + typename Accessor> +ReturnType unpack(Function function, Accessor accessor) { + return ReturnType(unpack( + std::move(function), + std::move(accessor), + typename MakeIndices::indices())); +} + +template < + typename ReturnType, + typename... Ts, + typename Function, + typename Accessor, + size_t... Is> +ReturnType unpack(Function function, Accessor accessor, Indices) { + return ReturnType(function(accessor.template operator()(Is)...)); +} + +} // namespace torch