chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -0,0 +1,278 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/Device.h>
#include <c10/core/DeviceType.h>
#include <c10/util/Exception.h>
#include <c10/util/UniqueVoidPtr.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <memory>
#include <utility>
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/allocator.h"
namespace c10 {
// Deleter function pointer type (compatible with LibTorch)
using DeleterFnPtr = void (*)(void*);
using CaptureId_t = uint64_t;
using MempoolId_t = std::pair<CaptureId_t, CaptureId_t>;
struct MempoolIdHash {
std::size_t operator()(const MempoolId_t& mempool_id) const noexcept {
return mempool_id.first != 0 ? mempool_id.first : mempool_id.second;
}
};
// DataPtr class compatible with LibTorch's c10::DataPtr
// Wraps a pointer with associated device and deleter
class DataPtr {
public:
DataPtr() : device_(phi::CPUPlace()) {}
DataPtr(void* data, Device device)
: ptr_(data), device_(device._PD_GetInner()) {}
DataPtr(void* data, void* ctx, DeleterFnPtr ctx_deleter, Device device)
: ptr_(data, ctx, ctx_deleter), device_(device._PD_GetInner()) {}
// DataPtr is move-only, matching PyTorch's c10::DataPtr interface.
DataPtr(const DataPtr&) = delete;
DataPtr& operator=(const DataPtr&) = delete;
DataPtr(DataPtr&&) = default;
DataPtr& operator=(DataPtr&&) = default;
void* operator->() const { return ptr_.get(); }
bool unsafe_reset_data_and_ctx(void* new_data_and_ctx) {
return ptr_.unsafe_reset_data_and_ctx(new_data_and_ctx);
}
void clear() { ptr_.clear(); }
void* get() const { return ptr_.get(); }
void* mutable_get() { return ptr_.get(); }
void* get_context() const { return ptr_.get_context(); }
void* release_context() { return ptr_.release_context(); }
std::unique_ptr<void, DeleterFnPtr>&& move_context() {
return ptr_.move_context();
}
operator bool() const { return static_cast<bool>(ptr_); }
template <typename T>
T* cast_context(DeleterFnPtr expected_deleter) const {
return ptr_.cast_context<T>(expected_deleter);
}
DeleterFnPtr get_deleter() const { return ptr_.get_deleter(); }
// Atomically replaces the deleter if it matches expected_deleter.
// Returns true and installs new_deleter on match; does nothing and
// returns false otherwise.
[[nodiscard]] bool compare_exchange_deleter(DeleterFnPtr expected_deleter,
DeleterFnPtr new_deleter) {
return ptr_.compare_exchange_deleter(expected_deleter, new_deleter);
}
Device device() const { return Device(device_); }
void unsafe_set_device(Device device) { device_ = device._PD_GetInner(); }
private:
c10::detail::UniqueVoidPtr ptr_;
phi::Place device_;
};
inline bool operator==(const DataPtr& dp, std::nullptr_t) noexcept {
return !dp;
}
inline bool operator==(std::nullptr_t, const DataPtr& dp) noexcept {
return !dp;
}
inline bool operator!=(const DataPtr& dp, std::nullptr_t) noexcept {
return static_cast<bool>(dp);
}
inline bool operator!=(std::nullptr_t, const DataPtr& dp) noexcept {
return static_cast<bool>(dp);
}
struct Allocator {
virtual ~Allocator() = default;
virtual DataPtr allocate(size_t n) = 0;
// Clones an allocation that came from this allocator.
//
// To perform the copy, this function calls `copy_data`, which
// must be implemented by derived classes.
//
// Note that this explicitly ignores any context that may have been
// attached to the input data.
//
// Requires: input data was allocated by the same allocator.
DataPtr clone(const void* data, std::size_t n) {
auto new_data = allocate(n);
copy_data(new_data.mutable_get(), data, n);
return new_data;
}
// Checks if DataPtr has a simple context, not wrapped with any out of the
// ordinary contexts.
virtual bool is_simple_data_ptr(const DataPtr& data_ptr) const {
return data_ptr.get() == data_ptr.get_context();
}
// If this returns a non nullptr, it means that allocate()
// is guaranteed to return a unique_ptr with this deleter attached;
// it means the rawAllocate and rawDeallocate APIs are safe to use.
// This function MUST always return the same BoundDeleter.
virtual DeleterFnPtr raw_deleter() const { return nullptr; }
void* raw_allocate(size_t n) {
auto dptr = allocate(n);
TORCH_CHECK(dptr.get() == dptr.get_context(),
"raw_allocate: DataPtr context must equal data pointer");
return dptr.release_context();
}
void raw_deallocate(void* ptr) {
auto d = raw_deleter();
TORCH_CHECK(d != nullptr, "raw_deallocate: deleter must not be null");
d(ptr);
}
// Copies data from one allocation to another.
// Pure virtual, so derived classes must define behavior.
// Derived class implementation can simply call `default_copy_data`
// to use `std::memcpy`.
//
// Requires: src and dest were allocated by this allocator
// Requires: src and dest both have length >= count
virtual void copy_data(void* dest,
const void* src,
std::size_t count) const = 0;
protected:
// Uses `std::memcpy` to copy data.
// Child classes can use this as `copy_data` when an alternative copy
// API is not needed.
void default_copy_data(void* dest, const void* src, std::size_t count) const {
std::memcpy(dest, src, count);
}
};
struct InefficientStdFunctionContext {
void* ptr_{nullptr};
std::function<void(void*)> deleter_;
InefficientStdFunctionContext(void* ptr, std::function<void(void*)> deleter)
: ptr_(ptr), deleter_(std::move(deleter)) {}
InefficientStdFunctionContext(const InefficientStdFunctionContext&) = delete;
InefficientStdFunctionContext(InefficientStdFunctionContext&& rhs) noexcept
: ptr_(std::exchange(rhs.ptr_, nullptr)),
deleter_(std::move(rhs.deleter_)) {}
InefficientStdFunctionContext& operator=(
const InefficientStdFunctionContext&) = delete;
InefficientStdFunctionContext& operator=(
InefficientStdFunctionContext&& rhs) {
this->~InefficientStdFunctionContext();
ptr_ = std::exchange(rhs.ptr_, nullptr);
deleter_ = std::move(rhs.deleter_);
return *this;
}
~InefficientStdFunctionContext() {
if (deleter_) {
deleter_(ptr_);
}
}
static DataPtr makeDataPtr(void* ptr,
std::function<void(void*)> deleter,
Device device) {
return DataPtr(ptr,
new InefficientStdFunctionContext(ptr, std::move(deleter)),
&deleteContext,
device);
}
private:
static void deleteContext(void* ptr) {
delete static_cast<InefficientStdFunctionContext*>(ptr);
}
};
inline constexpr size_t kAllocatorRegistrySize =
static_cast<size_t>(DeviceType::CUSTOM) + 1;
inline std::array<Allocator*, kAllocatorRegistrySize> g_allocator_array{};
inline std::array<uint8_t, kAllocatorRegistrySize> g_allocator_priority{};
inline size_t allocator_device_index(DeviceType t) {
const size_t index = static_cast<size_t>(t);
TORCH_CHECK(index < kAllocatorRegistrySize,
"Allocator device type index out of range: ",
index);
return index;
}
inline void SetAllocator(DeviceType t, Allocator* alloc, uint8_t priority = 0) {
const size_t index = allocator_device_index(t);
if (priority >= g_allocator_priority[index]) {
g_allocator_array[index] = alloc;
g_allocator_priority[index] = priority;
}
}
inline Allocator* GetAllocator(const DeviceType& t) {
const size_t index = allocator_device_index(t);
auto* alloc = g_allocator_array[index];
TORCH_CHECK(alloc != nullptr, "Allocator for ", t, " is not set.");
return alloc;
}
template <DeviceType t>
struct AllocatorRegisterer {
explicit AllocatorRegisterer(Allocator* alloc) { SetAllocator(t, alloc); }
};
#define REGISTER_ALLOCATOR(t, f) \
namespace { \
static c10::AllocatorRegisterer<t> g_allocator_d(f); \
}
} // namespace c10
namespace at {
using DataPtr = c10::DataPtr;
} // namespace at
@@ -0,0 +1,77 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
namespace c10 {
/**
* This legacy enum class defines the set of backends supported by old school,
* code generated Type-based ATen. A "backend" in this sense roughly
* corresponds to the cartesian product of (device type, layout), but restricted
* only to combinations which we actually have kernels for. Backend does NOT
* include dtype.
*
* The reason we are sunsetting this enum class is because it doesn't allow for
* open registration; e.g., if you want to add SparseXLA, you'd have to
* edit this enum; you wouldn't be able to do it out of tree. DispatchKey is
* the replacement for Backend which supports open registration.
*
* NB: The concept of 'Backend' here disagrees with the notion of backend
* exposed to users in torch.backends. Backend here is something like "CPU"
* or "SparseCUDA"; backend in torch.backends is something like "MKL" or
* "CUDNN".
*/
enum class Backend {
CPU,
CUDA,
HIP,
VE,
FPGA,
IPU,
XPU,
SparseCPU,
SparseCUDA,
SparseCsrCPU,
SparseCsrCUDA,
SparseCsrMPS,
SparseMPS,
SparseHIP,
SparseVE,
SparseXPU,
SparsePrivateUse1,
SparseCsrHIP,
SparseCsrVE,
SparseCsrXPU,
SparseCsrPrivateUse1,
MAIA,
XLA,
Vulkan,
Metal,
Meta,
QuantizedCPU,
QuantizedCUDA,
QuantizedXPU,
QuantizedPrivateUse1,
Undefined,
MkldnnCPU,
MPS,
HPU,
Lazy,
MTIA,
PrivateUse1,
NumOptions
};
} // namespace c10
@@ -0,0 +1,50 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <c10/core/DefaultDtype.h>
#include <c10/util/complex.h>
#include <c10/util/typeid.h>
namespace c10 {
static auto default_dtype = caffe2::TypeMeta::Make<float>();
static auto default_dtype_as_scalartype = default_dtype.toScalarType();
static auto default_complex_dtype =
caffe2::TypeMeta::Make<c10::complex<float>>();
void set_default_dtype(caffe2::TypeMeta dtype) {
default_dtype = dtype;
default_dtype_as_scalartype = default_dtype.toScalarType();
switch (default_dtype_as_scalartype) {
case ScalarType::Half:
default_complex_dtype = ScalarType::ComplexHalf;
break;
case ScalarType::Double:
default_complex_dtype = ScalarType::ComplexDouble;
break;
default:
default_complex_dtype = ScalarType::ComplexFloat;
break;
}
}
const caffe2::TypeMeta get_default_dtype() { return default_dtype; }
ScalarType get_default_dtype_as_scalartype() {
return default_dtype_as_scalartype;
}
const caffe2::TypeMeta get_default_complex_dtype() {
return default_complex_dtype;
}
} // namespace c10
@@ -0,0 +1,30 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/ScalarType.h>
#include "paddle/common/macros.h"
namespace caffe2 {
class TypeMeta;
} // namespace caffe2
namespace c10 {
PADDLE_API void set_default_dtype(caffe2::TypeMeta dtype);
PADDLE_API const caffe2::TypeMeta get_default_dtype();
PADDLE_API ScalarType get_default_dtype_as_scalartype();
PADDLE_API const caffe2::TypeMeta get_default_complex_dtype();
} // namespace c10
@@ -0,0 +1,161 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#include <c10/core/Device.h>
#include <c10/util/Exception.h>
#include <algorithm>
#include <array>
#include <cctype>
#include <exception>
#include <string>
#include "paddle/common/enforce.h"
namespace c10 {
namespace {
const char* DeviceTypeToString(DeviceType type) {
switch (type) {
case DeviceType::CPU:
return "cpu";
case DeviceType::CUDA:
return "cuda";
case DeviceType::XPU:
return "xpu";
case DeviceType::IPU:
return "ipu";
case DeviceType::CUSTOM:
return "privateuseone";
}
return "cpu";
}
} // namespace
DeviceType parse_type(const std::string& device_string) {
static const std::array<std::pair<const char*, DeviceType>,
static_cast<size_t>(5)>
types = {{
{"cpu", DeviceType::CPU},
{"cuda", DeviceType::CUDA},
{"ipu", DeviceType::IPU},
{"xpu", DeviceType::XPU},
{"privateuseone", DeviceType::PrivateUse1},
}};
auto device = std::find_if(
types.begin(),
types.end(),
[&device_string](const std::pair<const char*, DeviceType>& p) {
return p.first && p.first == device_string;
});
if (device != types.end()) {
return device->second;
}
TORCH_CHECK(false,
"Expected one of cpu, cuda, ipu, xpu, privateuseone device type "
"at start of device string: ",
device_string);
}
enum DeviceStringParsingState { kStart, kIndexStart, kIndexRest, kError };
Device::Device(const std::string& device_string) : Device(Type::CPU) {
TORCH_CHECK(!device_string.empty(), "Device string must not be empty");
std::string device_name, device_index_str;
DeviceStringParsingState pstate = DeviceStringParsingState::kStart;
for (size_t i = 0;
pstate != DeviceStringParsingState::kError && i < device_string.size();
++i) {
const char ch = device_string.at(i);
const unsigned char uch = static_cast<unsigned char>(ch);
switch (pstate) {
case DeviceStringParsingState::kStart:
if (ch != ':') {
if (std::isalpha(uch) || ch == '_') {
device_name.push_back(ch);
} else {
pstate = DeviceStringParsingState::kError;
}
} else {
pstate = DeviceStringParsingState::kIndexStart;
}
break;
case DeviceStringParsingState::kIndexStart:
if (std::isdigit(uch)) {
device_index_str.push_back(ch);
pstate = DeviceStringParsingState::kIndexRest;
} else {
pstate = DeviceStringParsingState::kError;
}
break;
case DeviceStringParsingState::kIndexRest:
if (device_index_str.at(0) == '0') {
pstate = DeviceStringParsingState::kError;
break;
}
if (std::isdigit(uch)) {
device_index_str.push_back(ch);
} else {
pstate = DeviceStringParsingState::kError;
}
break;
case DeviceStringParsingState::kError:
break;
}
}
const bool has_error = device_name.empty() ||
pstate == DeviceStringParsingState::kError ||
(pstate == DeviceStringParsingState::kIndexStart &&
device_index_str.empty());
TORCH_CHECK(!has_error, "Invalid device string: '", device_string, "'");
try {
if (!device_index_str.empty()) {
index_ = static_cast<DeviceIndex>(std::stoi(device_index_str));
}
} catch (const std::exception&) {
TORCH_CHECK(false,
"Could not parse device index '",
device_index_str,
"' in device string '",
device_string,
"'");
}
type_ = parse_type(device_name);
validate();
}
std::string Device::str() const {
std::string str = DeviceTypeToString(type());
if (has_index()) {
str.push_back(':');
str.append(std::to_string(index()));
}
return str;
}
std::ostream& operator<<(std::ostream& stream, const Device& device) {
stream << device.str();
return stream;
}
} // namespace c10
@@ -0,0 +1,191 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef PADDLE_WITH_CUDA
#include <cuda_runtime.h>
using gpuStream_t = cudaStream_t;
#endif
#ifdef PADDLE_WITH_HIP
#include <hip/hip_runtime.h>
using gpuStream_t = hipStream_t;
#endif
#include <c10/core/DeviceType.h>
#include <c10/util/Exception.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iosfwd>
#include <string>
#include <utility>
#include "paddle/common/macros.h"
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
#include "paddle/phi/core/platform/device_event_base.h"
namespace c10 {
using DeviceIndex = int8_t;
struct PADDLE_API Device final {
using Type = DeviceType;
Device() = default;
Device(phi::Place place)
: type_(PhiToDeviceType(place.GetType())),
index_(place.GetType() == phi::AllocationType::CPU
? static_cast<DeviceIndex>(-1)
: place.GetDeviceId()),
custom_device_type_(place.GetDeviceType()) {
validate();
}
Device(DeviceType type, DeviceIndex index = -1)
: type_(type), index_(index) { // NOLINT
validate();
}
Device(DeviceType type, DeviceIndex index, std::string custom_device_type)
: type_(type),
index_(index),
custom_device_type_(std::move(custom_device_type)) { // NOLINT
validate();
}
/// Constructs a `Device` from a string description, for convenience.
/// The string supplied must follow the following schema:
/// `(cpu|cuda)[:<device-index>]`
/// where `cpu` or `cuda` specifies the device type, and
/// `:<device-index>` optionally specifies a device index.
/* implicit */ Device(const std::string& device_string);
DeviceIndex index() const noexcept { return index_; }
bool has_index() const noexcept { return index() != -1; }
DeviceType type() const noexcept { return type_; }
bool operator!=(const Device& other) const noexcept {
return !(*this == other);
}
void set_index(DeviceIndex index) {
index_ = index;
validate();
}
bool is_cuda() const noexcept { return type_ == DeviceType::CUDA; }
bool is_privateuseone() const noexcept {
return type_ == DeviceType::PrivateUse1;
}
bool is_mps() const noexcept { return false; }
bool is_hip() const noexcept { return false; }
bool is_ve() const noexcept { return false; }
bool is_xpu() const noexcept { return type_ == DeviceType::XPU; }
bool is_ipu() const noexcept { return type_ == DeviceType::IPU; }
bool is_xla() const noexcept { return false; }
bool is_mtia() const noexcept { return false; }
bool is_hpu() const noexcept { return false; }
bool is_lazy() const noexcept { return false; }
bool is_vulkan() const noexcept { return false; }
bool is_metal() const noexcept { return false; }
bool is_maia() const noexcept { return false; }
bool is_meta() const noexcept { return false; }
bool is_cpu() const noexcept { return type_ == DeviceType::CPU; }
bool supports_as_strided() const noexcept { return type_ != DeviceType::IPU; }
std::string str() const;
bool operator==(const Device& other) const noexcept {
return type() == other.type() && this->index() == other.index() &&
custom_device_type_ == other.custom_device_type_;
}
phi::Place _PD_GetInner() const {
switch (type_) {
case DeviceType::CPU:
return phi::CPUPlace();
case DeviceType::CUDA:
return has_index() ? phi::GPUPlace(index_) : paddle::DefaultGPUPlace();
case DeviceType::XPU:
return has_index() ? phi::XPUPlace(index_) : paddle::DefaultXPUPlace();
case DeviceType::IPU:
return has_index() ? phi::IPUPlace(index_) : phi::IPUPlace();
case DeviceType::CUSTOM:
return phi::CustomPlace(
custom_device_type_.empty() ? "custom" : custom_device_type_,
has_index() ? index_ : 0);
}
return phi::CPUPlace();
}
private:
DeviceType type_{DeviceType::CPU};
DeviceIndex index_{-1};
std::string custom_device_type_;
void validate() {
#ifndef NDEBUG
TORCH_INTERNAL_ASSERT(index_ >= -1,
"Device index must be -1 or non-negative, got ",
static_cast<int>(index_));
TORCH_INTERNAL_ASSERT(!is_cpu() || index_ <= 0,
"CPU device index must be -1 or zero, got ",
static_cast<int>(index_));
#endif
}
};
PADDLE_API std::ostream& operator<<(std::ostream& stream, const Device& device);
} // namespace c10
namespace std {
template <>
struct hash<c10::Device> {
size_t operator()(c10::Device d) const noexcept {
static_assert(sizeof(c10::DeviceType) == 1, "DeviceType is not 8-bit");
static_assert(sizeof(c10::DeviceIndex) == 1, "DeviceIndex is not 8-bit");
uint32_t bits = static_cast<uint32_t>(static_cast<uint8_t>(d.type()))
<< 16 |
static_cast<uint32_t>(static_cast<uint8_t>(d.index()));
return std::hash<uint32_t>{}(bits);
}
};
} // namespace std
namespace at {
using c10::Device;
using c10::DeviceIndex;
} // namespace at
namespace torch {
using c10::Device;
using c10::DeviceIndex;
} // namespace torch
@@ -0,0 +1,94 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
// If you modified DeviceType in this file, please also sync your changes into
// torch/headeronly/core/DeviceType.h.
#include <torch/headeronly/core/DeviceType.h>
#include <ostream>
#include "paddle/phi/common/place.h"
namespace c10 {
inline phi::AllocationType DeviceTypeToPhi(DeviceType d) {
switch (d) {
case DeviceType::CPU:
return phi::AllocationType::CPU;
case DeviceType::CUDA:
return phi::AllocationType::GPU;
case DeviceType::XPU:
return phi::AllocationType::XPU;
case DeviceType::IPU:
return phi::AllocationType::IPU;
case DeviceType::CUSTOM:
return phi::AllocationType::CUSTOM;
}
return phi::AllocationType::UNDEFINED;
}
inline DeviceType PhiToDeviceType(phi::AllocationType d) {
switch (d) {
case phi::AllocationType::CPU:
return DeviceType::CPU;
case phi::AllocationType::GPU:
return DeviceType::CUDA;
case phi::AllocationType::XPU:
return DeviceType::XPU;
case phi::AllocationType::IPU:
return DeviceType::IPU;
case phi::AllocationType::CUSTOM:
return DeviceType::CUSTOM;
default:
return DeviceType::CPU;
}
}
inline bool isValidDeviceType(DeviceType d) {
switch (d) {
case DeviceType::CPU:
case DeviceType::CUDA:
case DeviceType::XPU:
case DeviceType::IPU:
case DeviceType::CUSTOM:
return true;
default:
return false;
}
}
inline std::ostream& operator<<(std::ostream& os, DeviceType d) {
switch (d) {
case DeviceType::CPU:
os << "cpu";
break;
case DeviceType::CUDA:
os << "cuda";
break;
case DeviceType::XPU:
os << "xpu";
break;
case DeviceType::IPU:
os << "ipu";
break;
case DeviceType::CUSTOM:
os << "privateuseone";
break;
}
return os;
}
} // namespace c10
@@ -0,0 +1,598 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/DeviceType.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <ostream>
#include <string>
namespace c10 {
#define C10_FORALL_BACKEND_COMPONENTS(_, extra) \
_(CPU, extra) \
_(CUDA, extra) \
_(HIP, extra) \
_(XLA, extra) \
_(MPS, extra) \
_(IPU, extra) \
_(XPU, extra) \
_(HPU, extra) \
_(VE, extra) \
_(Lazy, extra) \
_(MTIA, extra) \
_(MAIA, extra) \
_(PrivateUse1, extra) \
_(PrivateUse2, extra) \
_(PrivateUse3, extra) \
_(Meta, extra)
#define C10_FORALL_FUNCTIONALITY_KEYS(_) \
_(Dense, ) /* NOLINT */ \
_(Quantized, Quantized) \
_(Sparse, Sparse) \
_(SparseCsr, SparseCsr) \
_(NestedTensor, NestedTensor) \
_(AutogradFunctionality, Autograd)
enum class BackendComponent : uint8_t {
InvalidBit = 0,
#define DEFINE_BACKEND_COMPONENT(n, _) n##Bit,
C10_FORALL_BACKEND_COMPONENTS(DEFINE_BACKEND_COMPONENT, unused)
#undef DEFINE_BACKEND_COMPONENT
EndOfBackendKeys = MetaBit,
};
enum class DispatchKey : uint16_t {
Undefined = 0,
CatchAll = Undefined,
Dense,
FPGA,
Vulkan,
Metal,
Quantized,
CustomRNGKeyId,
MkldnnCPU,
Sparse,
SparseCsr,
NestedTensor,
BackendSelect,
Python,
Fake,
FuncTorchDynamicLayerBackMode,
Functionalize,
Named,
Conjugate,
Negative,
ZeroTensor,
ADInplaceOrView,
AutogradOther,
AutogradFunctionality,
AutogradNestedTensor,
Tracer,
AutocastCPU,
AutocastMTIA,
AutocastMAIA,
AutocastXPU,
AutocastIPU,
AutocastHPU,
AutocastXLA,
AutocastMPS,
AutocastCUDA,
AutocastPrivateUse1,
FuncTorchBatched,
BatchedNestedTensor,
FuncTorchVmapMode,
Batched,
VmapMode,
FuncTorchGradWrapper,
DeferredInit,
PythonTLSSnapshot,
FuncTorchDynamicLayerFrontMode,
TESTING_ONLY_GenericWrapper,
TESTING_ONLY_GenericMode,
PreDispatch,
PythonDispatcher,
EndOfFunctionalityKeys,
#define DEFINE_PER_BACKEND_KEYS_FOR_BACKEND(n, prefix) prefix##n,
#define DEFINE_PER_BACKEND_KEYS(fullname, prefix) \
StartOf##fullname##Backends, \
C10_FORALL_BACKEND_COMPONENTS(DEFINE_PER_BACKEND_KEYS_FOR_BACKEND, \
prefix) EndOf##fullname##Backends = \
prefix##Meta,
C10_FORALL_FUNCTIONALITY_KEYS(DEFINE_PER_BACKEND_KEYS)
#undef DEFINE_PER_BACKEND_KEYS
#undef DEFINE_PER_BACKEND_KEYS_FOR_BACKEND
EndOfRuntimeBackendKeys = EndOfAutogradFunctionalityBackends,
Autograd,
CompositeImplicitAutograd,
FuncTorchBatchedDecomposition,
CompositeImplicitAutogradNestedTensor,
CompositeExplicitAutograd,
CompositeExplicitAutogradNonFunctional,
StartOfAliasKeys = Autograd,
EndOfAliasKeys = CompositeExplicitAutogradNonFunctional,
CPUTensorId = CPU,
CUDATensorId = CUDA,
DefaultBackend = CompositeExplicitAutograd,
PrivateUse1_PreAutograd = AutogradPrivateUse1,
PrivateUse2_PreAutograd = AutogradPrivateUse2,
PrivateUse3_PreAutograd = AutogradPrivateUse3,
Autocast = AutocastCUDA,
};
static_assert(
(static_cast<uint8_t>(BackendComponent::EndOfBackendKeys) +
static_cast<uint8_t>(DispatchKey::EndOfFunctionalityKeys)) <= 64,
"The BackendComponent and DispatchKey enums (below EndOfFunctionalityKeys)"
" both map to backend and functionality bits"
" into a 64-bit bitmask; you must have less than 64 total entries between "
"them");
constexpr bool isAliasDispatchKey(DispatchKey k) {
return k >= DispatchKey::StartOfAliasKeys && k <= DispatchKey::EndOfAliasKeys;
}
constexpr bool isPerBackendFunctionalityKey(DispatchKey k) {
if (k == DispatchKey::Dense || k == DispatchKey::Quantized ||
k == DispatchKey::Sparse || k == DispatchKey::SparseCsr ||
k == DispatchKey::AutogradFunctionality ||
k == DispatchKey::NestedTensor) {
return true;
} else {
return false;
}
}
constexpr uint8_t num_functionality_keys =
static_cast<uint8_t>(DispatchKey::EndOfFunctionalityKeys);
constexpr uint8_t num_backends =
static_cast<uint8_t>(BackendComponent::EndOfBackendKeys);
static_assert(static_cast<uint8_t>(BackendComponent::EndOfBackendKeys) <= 16,
"BackendComponent currently only supports <= 16 backends. "
"If we really need to extend this, "
"there are a few places where this invariant is baked in");
constexpr uint8_t numPerBackendFunctionalityKeys() {
uint8_t count = 0;
for (uint8_t k = 0; k <= num_functionality_keys; ++k) {
if (isPerBackendFunctionalityKey(static_cast<DispatchKey>(k))) ++count;
}
return count;
}
#if defined(C10_MOBILE_TRIM_DISPATCH_KEYS)
constexpr uint16_t num_runtime_entries = 8;
#else
constexpr uint16_t num_runtime_entries =
num_functionality_keys +
(numPerBackendFunctionalityKeys() * (num_backends - 1));
#endif
constexpr uint16_t full_backend_mask =
(static_cast<uint16_t>(1) << num_backends) - 1;
const char* toString(DispatchKey /*t*/);
const char* toString(BackendComponent /*t*/);
std::ostream& operator<<(std::ostream& /*str*/, DispatchKey /*rhs*/);
std::ostream& operator<<(std::ostream& /*str*/, BackendComponent /*rhs*/);
DispatchKey getAutogradKeyFromBackend(BackendComponent k);
c10::DispatchKey parseDispatchKey(const std::string& k);
constexpr DispatchKey kAutograd = DispatchKey::Autograd;
constexpr BackendComponent toBackendComponent(DispatchKey k) {
if (k >= DispatchKey::StartOfDenseBackends &&
k <= DispatchKey::EndOfDenseBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(DispatchKey::StartOfDenseBackends));
} else if (k >= DispatchKey::StartOfQuantizedBackends &&
k <= DispatchKey::EndOfQuantizedBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(DispatchKey::StartOfQuantizedBackends));
} else if (k >= DispatchKey::StartOfSparseBackends &&
k <= DispatchKey::EndOfSparseBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(DispatchKey::StartOfSparseBackends));
} else if (k >= DispatchKey::StartOfSparseCsrBackends &&
k <= DispatchKey::EndOfSparseCsrBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(DispatchKey::StartOfSparseCsrBackends));
} else if (k >= DispatchKey::StartOfNestedTensorBackends &&
k <= DispatchKey::EndOfNestedTensorBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(DispatchKey::StartOfNestedTensorBackends));
} else if (k >= DispatchKey::StartOfAutogradFunctionalityBackends &&
k <= DispatchKey::EndOfAutogradFunctionalityBackends) {
return static_cast<BackendComponent>(
static_cast<uint8_t>(k) -
static_cast<uint8_t>(
DispatchKey::StartOfAutogradFunctionalityBackends));
} else {
return BackendComponent::InvalidBit;
}
}
constexpr DispatchKey toFunctionalityKey(DispatchKey k) {
if (k <= DispatchKey::EndOfFunctionalityKeys) {
return k;
} else if (k <= DispatchKey::EndOfDenseBackends) {
return DispatchKey::Dense;
} else if (k <= DispatchKey::EndOfQuantizedBackends) {
return DispatchKey::Quantized;
} else if (k <= DispatchKey::EndOfSparseBackends) {
return DispatchKey::Sparse;
} else if (k <= DispatchKey::EndOfSparseCsrBackends) {
return DispatchKey::SparseCsr;
} else if (k <= DispatchKey::EndOfNestedTensorBackends) {
return DispatchKey::NestedTensor;
} else if (k <= DispatchKey::EndOfAutogradFunctionalityBackends) {
return DispatchKey::AutogradFunctionality;
} else {
return DispatchKey::Undefined;
}
}
inline BackendComponent toBackendComponent(DeviceType device_type) {
switch (device_type) {
case DeviceType::CPU:
return BackendComponent::CPUBit;
case DeviceType::CUDA:
return BackendComponent::CUDABit;
case DeviceType::XPU:
return BackendComponent::XPUBit;
default:
return BackendComponent::InvalidBit;
}
}
constexpr DispatchKey toRuntimePerBackendFunctionalityKey(
DispatchKey functionality_k, BackendComponent backend_k) {
if (functionality_k == DispatchKey::Dense) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(DispatchKey::StartOfDenseBackends) +
static_cast<uint8_t>(backend_k));
}
if (functionality_k == DispatchKey::Sparse) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(DispatchKey::StartOfSparseBackends) +
static_cast<uint8_t>(backend_k));
}
if (functionality_k == DispatchKey::SparseCsr) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(DispatchKey::StartOfSparseCsrBackends) +
static_cast<uint8_t>(backend_k));
}
if (functionality_k == DispatchKey::Quantized) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(DispatchKey::StartOfQuantizedBackends) +
static_cast<uint8_t>(backend_k));
}
if (functionality_k == DispatchKey::NestedTensor) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(DispatchKey::StartOfNestedTensorBackends) +
static_cast<uint8_t>(backend_k));
}
if (functionality_k == DispatchKey::AutogradFunctionality) {
return static_cast<DispatchKey>(
static_cast<uint8_t>(
DispatchKey::StartOfAutogradFunctionalityBackends) +
static_cast<uint8_t>(backend_k));
}
return DispatchKey::Undefined;
}
// toString implementations
inline const char* toString(DispatchKey t) {
switch (t) {
case DispatchKey::Undefined:
return "Undefined";
case DispatchKey::Dense:
return "Dense";
case DispatchKey::FPGA:
return "FPGA";
case DispatchKey::Vulkan:
return "Vulkan";
case DispatchKey::Metal:
return "Metal";
case DispatchKey::Quantized:
return "Quantized";
case DispatchKey::CustomRNGKeyId:
return "CustomRNGKeyId";
case DispatchKey::MkldnnCPU:
return "MkldnnCPU";
case DispatchKey::Sparse:
return "Sparse";
case DispatchKey::SparseCsr:
return "SparseCsr";
case DispatchKey::NestedTensor:
return "NestedTensor";
case DispatchKey::BackendSelect:
return "BackendSelect";
case DispatchKey::Python:
return "Python";
case DispatchKey::Fake:
return "Fake";
case DispatchKey::FuncTorchDynamicLayerBackMode:
return "FuncTorchDynamicLayerBackMode";
case DispatchKey::Functionalize:
return "Functionalize";
case DispatchKey::Named:
return "Named";
case DispatchKey::Conjugate:
return "Conjugate";
case DispatchKey::Negative:
return "Negative";
case DispatchKey::ZeroTensor:
return "ZeroTensor";
case DispatchKey::ADInplaceOrView:
return "ADInplaceOrView";
case DispatchKey::AutogradOther:
return "AutogradOther";
case DispatchKey::AutogradFunctionality:
return "AutogradFunctionality";
case DispatchKey::AutogradNestedTensor:
return "AutogradNestedTensor";
case DispatchKey::Tracer:
return "Tracer";
case DispatchKey::AutocastCPU:
return "AutocastCPU";
case DispatchKey::AutocastMTIA:
return "AutocastMTIA";
case DispatchKey::AutocastMAIA:
return "AutocastMAIA";
case DispatchKey::AutocastXPU:
return "AutocastXPU";
case DispatchKey::AutocastIPU:
return "AutocastIPU";
case DispatchKey::AutocastHPU:
return "AutocastHPU";
case DispatchKey::AutocastXLA:
return "AutocastXLA";
case DispatchKey::AutocastMPS:
return "AutocastMPS";
case DispatchKey::AutocastCUDA:
return "AutocastCUDA";
case DispatchKey::AutocastPrivateUse1:
return "AutocastPrivateUse1";
case DispatchKey::FuncTorchBatched:
return "FuncTorchBatched";
case DispatchKey::BatchedNestedTensor:
return "BatchedNestedTensor";
case DispatchKey::FuncTorchVmapMode:
return "FuncTorchVmapMode";
case DispatchKey::Batched:
return "Batched";
case DispatchKey::VmapMode:
return "VmapMode";
case DispatchKey::FuncTorchGradWrapper:
return "FuncTorchGradWrapper";
case DispatchKey::DeferredInit:
return "DeferredInit";
case DispatchKey::PythonTLSSnapshot:
return "PythonTLSSnapshot";
case DispatchKey::FuncTorchDynamicLayerFrontMode:
return "FuncTorchDynamicLayerFrontMode";
case DispatchKey::TESTING_ONLY_GenericWrapper:
return "TESTING_ONLY_GenericWrapper";
case DispatchKey::TESTING_ONLY_GenericMode:
return "TESTING_ONLY_GenericMode";
case DispatchKey::PreDispatch:
return "PreDispatch";
case DispatchKey::PythonDispatcher:
return "PythonDispatcher";
case DispatchKey::Autograd:
return "Autograd";
case DispatchKey::CompositeImplicitAutograd:
return "CompositeImplicitAutograd";
case DispatchKey::FuncTorchBatchedDecomposition:
return "FuncTorchBatchedDecomposition";
case DispatchKey::CompositeImplicitAutogradNestedTensor:
return "CompositeImplicitAutogradNestedTensor";
case DispatchKey::CompositeExplicitAutograd:
return "CompositeExplicitAutograd";
case DispatchKey::CompositeExplicitAutogradNonFunctional:
return "CompositeExplicitAutogradNonFunctional";
default:
return "Unknown";
}
}
inline const char* toString(BackendComponent t) {
switch (t) {
case BackendComponent::CPUBit:
return "CPUBit";
case BackendComponent::CUDABit:
return "CUDABit";
case BackendComponent::HIPBit:
return "HIPBit";
case BackendComponent::XLABit:
return "XLABit";
case BackendComponent::MPSBit:
return "MPSBit";
case BackendComponent::IPUBit:
return "IPUBit";
case BackendComponent::XPUBit:
return "XPUBit";
case BackendComponent::HPUBit:
return "HPUBit";
case BackendComponent::VEBit:
return "VEBit";
case BackendComponent::LazyBit:
return "LazyBit";
case BackendComponent::MTIABit:
return "MTIABit";
case BackendComponent::MAIABit:
return "MAIABit";
case BackendComponent::PrivateUse1Bit:
return "PrivateUse1Bit";
case BackendComponent::PrivateUse2Bit:
return "PrivateUse2Bit";
case BackendComponent::PrivateUse3Bit:
return "PrivateUse3Bit";
case BackendComponent::MetaBit:
return "MetaBit";
default:
return "InvalidBit";
}
}
// operator<< implementations
inline std::ostream& operator<<(std::ostream& str, DispatchKey rhs) {
return str << toString(rhs);
}
inline std::ostream& operator<<(std::ostream& str, BackendComponent rhs) {
return str << toString(rhs);
}
// getAutogradKeyFromBackend implementation
inline DispatchKey getAutogradKeyFromBackend(BackendComponent k) {
switch (k) {
case BackendComponent::CPUBit:
return DispatchKey::AutogradCPU;
case BackendComponent::CUDABit:
return DispatchKey::AutogradCUDA;
case BackendComponent::XPUBit:
return DispatchKey::AutogradXPU;
case BackendComponent::IPUBit:
return DispatchKey::AutogradIPU;
case BackendComponent::HPUBit:
return DispatchKey::AutogradHPU;
case BackendComponent::LazyBit:
return DispatchKey::AutogradLazy;
case BackendComponent::MetaBit:
return DispatchKey::AutogradMeta;
case BackendComponent::MPSBit:
return DispatchKey::AutogradMPS;
case BackendComponent::PrivateUse1Bit:
return DispatchKey::AutogradPrivateUse1;
case BackendComponent::PrivateUse2Bit:
return DispatchKey::AutogradPrivateUse2;
case BackendComponent::PrivateUse3Bit:
return DispatchKey::AutogradPrivateUse3;
default:
return DispatchKey::AutogradOther;
}
}
// parseDispatchKey implementation
inline c10::DispatchKey parseDispatchKey(const std::string& k) {
if (k == "Undefined") return DispatchKey::Undefined;
if (k == "Dense") return DispatchKey::Dense;
if (k == "FPGA") return DispatchKey::FPGA;
if (k == "Vulkan") return DispatchKey::Vulkan;
if (k == "Metal") return DispatchKey::Metal;
if (k == "Quantized") return DispatchKey::Quantized;
if (k == "Sparse") return DispatchKey::Sparse;
if (k == "SparseCsr") return DispatchKey::SparseCsr;
if (k == "NestedTensor") return DispatchKey::NestedTensor;
if (k == "BackendSelect") return DispatchKey::BackendSelect;
if (k == "Python") return DispatchKey::Python;
if (k == "Fake") return DispatchKey::Fake;
if (k == "Functionalize") return DispatchKey::Functionalize;
if (k == "Named") return DispatchKey::Named;
if (k == "Conjugate") return DispatchKey::Conjugate;
if (k == "Negative") return DispatchKey::Negative;
if (k == "ZeroTensor") return DispatchKey::ZeroTensor;
if (k == "ADInplaceOrView") return DispatchKey::ADInplaceOrView;
if (k == "AutogradOther") return DispatchKey::AutogradOther;
if (k == "AutogradFunctionality") return DispatchKey::AutogradFunctionality;
if (k == "AutogradNestedTensor") return DispatchKey::AutogradNestedTensor;
if (k == "Tracer") return DispatchKey::Tracer;
if (k == "AutocastCPU") return DispatchKey::AutocastCPU;
if (k == "AutocastCUDA") return DispatchKey::AutocastCUDA;
if (k == "Autograd") return DispatchKey::Autograd;
if (k == "CompositeImplicitAutograd")
return DispatchKey::CompositeImplicitAutograd;
if (k == "CompositeExplicitAutograd")
return DispatchKey::CompositeExplicitAutograd;
return DispatchKey::Undefined;
}
} // namespace c10
namespace torch {
using c10::kAutograd; // NOLINT
} // namespace torch
namespace std {
template <>
struct hash<c10::DispatchKey> {
typedef size_t result_type;
typedef c10::DispatchKey argument_type;
size_t operator()(c10::DispatchKey x) const { return static_cast<size_t>(x); }
};
} // namespace std
@@ -0,0 +1,735 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/DispatchKey.h>
#include <c10/macros/Macros.h>
#include <c10/util/Exception.h>
#include <array>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
namespace c10 {
struct FunctionalityOffsetAndMask {
FunctionalityOffsetAndMask() = default;
FunctionalityOffsetAndMask(uint16_t offset, uint16_t mask)
: offset(offset), mask(mask) {}
uint16_t offset{};
uint16_t mask{};
};
static_assert(
c10::num_runtime_entries < 65536,
"The dispatcher currently only supports up to 2^16 runtime entries");
std::array<FunctionalityOffsetAndMask, num_functionality_keys>
initializeFunctionalityOffsetsAndMasks();
static const std::array<FunctionalityOffsetAndMask, num_functionality_keys>&
offsetsAndMasks() {
static auto offsets_and_masks_ = initializeFunctionalityOffsetsAndMasks();
return offsets_and_masks_;
}
class DispatchKeySet final {
public:
enum Full { FULL };
enum FullAfter { FULL_AFTER };
enum Raw { RAW };
constexpr DispatchKeySet() = default;
constexpr DispatchKeySet(Full /*unused*/)
: repr_((1ULL << (num_backends + num_functionality_keys - 1)) - 1) {}
constexpr DispatchKeySet(FullAfter /*unused*/, DispatchKey t)
: repr_((1ULL << (num_backends +
static_cast<uint8_t>(toFunctionalityKey(t)) - 1)) -
1) {
*this = add(DispatchKey::PythonDispatcher);
}
constexpr DispatchKeySet(Raw /*unused*/, uint64_t x) : repr_(x) {}
constexpr explicit DispatchKeySet(BackendComponent k) {
if (k == BackendComponent::InvalidBit) {
repr_ = 0;
} else {
repr_ = 1ULL << (static_cast<uint8_t>(k) - 1);
}
}
constexpr explicit DispatchKeySet(DispatchKey k) { // NOLINT
if (k == DispatchKey::Undefined) {
repr_ = 0;
} else if (k <= DispatchKey::EndOfFunctionalityKeys) {
uint64_t functionality_val =
1ULL << (num_backends + static_cast<uint8_t>(k) - 1);
repr_ = functionality_val;
} else if (k <= DispatchKey::EndOfRuntimeBackendKeys) {
auto functionality_k = toFunctionalityKey(k);
uint64_t functionality_val =
1ULL << (num_backends + static_cast<uint8_t>(functionality_k) - 1);
auto backend_k = toBackendComponent(k);
uint64_t backend_val = backend_k == BackendComponent::InvalidBit
? 0
: 1ULL
<< (static_cast<uint8_t>(backend_k) - 1);
repr_ = functionality_val + backend_val;
} else {
repr_ = 0;
}
}
constexpr uint64_t keys_to_repr(std::initializer_list<DispatchKey> ks) {
uint64_t repr = 0;
for (auto k : ks) {
repr |= DispatchKeySet(k).repr_;
}
return repr;
}
constexpr uint64_t backend_bits_to_repr(
std::initializer_list<BackendComponent> ks) {
uint64_t repr = 0;
for (auto k : ks) {
repr |= DispatchKeySet(k).repr_;
}
return repr;
}
explicit constexpr DispatchKeySet(std::initializer_list<DispatchKey> ks)
: repr_(keys_to_repr(ks)) {}
explicit constexpr DispatchKeySet(std::initializer_list<BackendComponent> ks)
: repr_(backend_bits_to_repr(ks)) {}
inline bool has(DispatchKey t) const {
PD_CHECK(t != DispatchKey::Undefined);
return has_all(DispatchKeySet(t));
}
constexpr bool has_backend(BackendComponent t) const {
return has_all(DispatchKeySet(t));
}
constexpr bool has_all(DispatchKeySet ks) const {
return static_cast<bool>((repr_ & ks.repr_) == ks.repr_);
}
inline bool has_any(DispatchKeySet ks) const {
PD_CHECK(((ks.repr_ & full_backend_mask) == 0) ||
((ks & DispatchKeySet({
DispatchKey::Dense,
DispatchKey::Quantized,
DispatchKey::Sparse,
DispatchKey::SparseCsr,
DispatchKey::AutogradFunctionality,
})
.repr_) == 0));
return static_cast<bool>((repr_ & ks.repr_) != 0);
}
bool isSupersetOf(DispatchKeySet ks) const {
return (repr_ & ks.repr_) == ks.repr_;
}
constexpr DispatchKeySet operator|(DispatchKeySet other) const {
return DispatchKeySet(repr_ | other.repr_);
}
constexpr DispatchKeySet operator&(DispatchKeySet other) const {
return DispatchKeySet(repr_ & other.repr_);
}
constexpr DispatchKeySet operator-(DispatchKeySet other) const {
return DispatchKeySet(repr_ & (full_backend_mask | ~other.repr_));
}
constexpr DispatchKeySet operator^(DispatchKeySet other) const {
return DispatchKeySet(repr_ ^ other.repr_);
}
bool operator==(DispatchKeySet other) const { return repr_ == other.repr_; }
bool operator!=(DispatchKeySet other) const { return repr_ != other.repr_; }
[[nodiscard]] constexpr DispatchKeySet add(DispatchKey t) const {
return *this | DispatchKeySet(t);
}
[[nodiscard]] constexpr DispatchKeySet add(DispatchKeySet ks) const {
return *this | ks;
}
[[nodiscard]] constexpr DispatchKeySet remove(DispatchKey t) const {
return DispatchKeySet(repr_ &
~(DispatchKeySet(t).repr_ & ~full_backend_mask));
}
constexpr DispatchKeySet remove_backend(BackendComponent b) const {
return DispatchKeySet(repr_ & ~(DispatchKeySet(b).repr_));
}
bool empty() const { return repr_ == 0; }
uint64_t raw_repr() const { return repr_; }
static DispatchKeySet from_raw_repr(uint64_t x) {
return DispatchKeySet(RAW, x);
}
DispatchKey highestFunctionalityKey() const {
auto functionality_idx = indexOfHighestBit();
if (functionality_idx < num_backends) return DispatchKey::Undefined;
return static_cast<DispatchKey>(functionality_idx - num_backends);
}
BackendComponent highestBackendKey() const {
auto backend_idx =
DispatchKeySet(repr_ & full_backend_mask).indexOfHighestBit();
if (backend_idx == 0) return BackendComponent::InvalidBit;
return static_cast<BackendComponent>(backend_idx);
}
DispatchKey highestPriorityTypeId() const {
auto functionality_k = highestFunctionalityKey();
if (isPerBackendFunctionalityKey(functionality_k)) {
return toRuntimePerBackendFunctionalityKey(functionality_k,
highestBackendKey());
}
return functionality_k;
}
uint8_t indexOfHighestBit() const {
// Use compiler built-in instead of llvm::countLeadingZeros.
if (repr_ == 0) return 0;
#if defined(_MSC_VER)
unsigned long index; // NOLINT(runtime/int)
_BitScanReverse64(&index, repr_);
return static_cast<uint8_t>(index + 1);
#else
return static_cast<uint8_t>(64 - __builtin_clzll(repr_));
#endif
}
#if defined(C10_MOBILE_TRIM_DISPATCH_KEYS)
/**
* The method below maps the dispatch key in the enum DispatchKey to an
* integer index in the dispatchTable_ array in OperatorEntry. The array
* is trimmed for mobile to reduce peak memory usage since it's
* unnecessary to reserve additional space for dispatch keys that will
* never be used on mobile.
*/
int getDispatchTableIndexForDispatchKeySet() const {
auto dk = highestPriorityTypeId();
switch (dk) {
case DispatchKey::Undefined:
return 0;
case DispatchKey::CPU:
return 1;
case DispatchKey::QuantizedCPU:
return 2;
case DispatchKey::SparseCPU:
return 3;
case DispatchKey::BackendSelect:
return 4;
case DispatchKey::ADInplaceOrView:
return 5;
case DispatchKey::AutogradOther:
return 6;
case DispatchKey::AutogradCPU:
return 7;
default:
return -1;
}
}
#else
int getDispatchTableIndexForDispatchKeySet() const {
auto functionality_idx =
DispatchKeySet(repr_ >> num_backends).indexOfHighestBit();
auto offset_and_mask = offsetsAndMasks()[functionality_idx];
auto backend_idx =
DispatchKeySet((repr_ & offset_and_mask.mask) >> 1).indexOfHighestBit();
return offset_and_mask.offset + backend_idx;
}
#endif
uint64_t getBackendIndex() const {
return DispatchKeySet((repr_ & full_backend_mask) >> 1).indexOfHighestBit();
}
private:
constexpr DispatchKeySet(uint64_t repr) : repr_(repr) {}
uint64_t repr_ = 0;
public:
class iterator {
public:
using self_type = iterator;
using iterator_category = std::input_iterator_tag;
using value_type = DispatchKey;
using difference_type = ptrdiff_t;
using reference = value_type&;
using pointer = value_type*;
static const uint8_t end_iter_mask_val =
num_backends + num_functionality_keys;
static const uint8_t end_iter_key_val = num_functionality_keys;
explicit iterator(const uint64_t* data_ptr,
uint8_t next_functionality = num_backends,
uint8_t next_backend = 0)
: data_ptr_(data_ptr),
next_functionality_(next_functionality),
next_backend_(next_backend),
current_dispatchkey_idx_(end_iter_key_val),
current_backendcomponent_idx_(end_iter_key_val) {
TORCH_INTERNAL_ASSERT(next_functionality_ >= num_backends,
"num_backends=",
static_cast<uint32_t>(num_backends),
"next_functionality_=",
static_cast<uint32_t>(next_functionality_));
++(*this);
}
self_type& operator++() {
while (next_functionality_ < end_iter_mask_val) {
if (*data_ptr_ & (1ULL << next_functionality_)) {
current_dispatchkey_idx_ =
static_cast<uint8_t>(next_functionality_ - num_backends);
if (isPerBackendFunctionalityKey(
static_cast<DispatchKey>(current_dispatchkey_idx_))) {
while (next_backend_ < num_backends) {
if (*data_ptr_ & (1ULL << next_backend_)) {
// BackendComponent is 1-based (InvalidBit=0, CPUBit=1, ...),
// so bit position next_backend_ maps to enum value
// next_backend_+1.
current_backendcomponent_idx_ = next_backend_ + 1;
++next_backend_;
return *this;
}
++next_backend_;
}
// No backend bits set for this functionality key; advance.
next_backend_ = 0;
current_backendcomponent_idx_ = end_iter_key_val;
++next_functionality_;
continue;
}
++next_functionality_;
return *this;
}
++next_functionality_;
}
current_dispatchkey_idx_ = end_iter_key_val;
current_backendcomponent_idx_ = end_iter_key_val;
return *this;
}
self_type operator++(int) {
self_type previous_iterator = *this;
++(*this);
return previous_iterator;
}
bool operator==(const self_type& rhs) const {
return next_functionality_ == rhs.next_functionality_ &&
current_dispatchkey_idx_ == rhs.current_dispatchkey_idx_ &&
next_backend_ == rhs.next_backend_ &&
current_backendcomponent_idx_ == rhs.current_backendcomponent_idx_;
}
bool operator!=(const self_type& rhs) const {
return next_functionality_ != rhs.next_functionality_ ||
current_dispatchkey_idx_ != rhs.current_dispatchkey_idx_ ||
next_backend_ != rhs.next_backend_ ||
current_backendcomponent_idx_ != rhs.current_backendcomponent_idx_;
}
DispatchKey operator*() const {
auto functionality_key =
static_cast<DispatchKey>(current_dispatchkey_idx_);
if (isPerBackendFunctionalityKey(functionality_key)) {
auto next_key = toRuntimePerBackendFunctionalityKey(
functionality_key,
static_cast<BackendComponent>(current_backendcomponent_idx_));
TORCH_INTERNAL_ASSERT(
toBackendComponent(next_key) ==
static_cast<BackendComponent>(current_backendcomponent_idx_),
"Tried to map functionality key ",
toString(functionality_key),
" and backend bit ",
toString(
static_cast<BackendComponent>(current_backendcomponent_idx_)),
" to a runtime key, but ended up with ",
toString(next_key),
". This can happen if the order of the backend dispatch keys in "
"DispatchKey.h isn't consistent.",
" Please double check that enum for inconsistencies.");
return next_key;
} else {
return functionality_key;
}
}
private:
const uint64_t* data_ptr_;
uint8_t next_functionality_;
uint8_t next_backend_;
uint8_t current_dispatchkey_idx_;
uint8_t current_backendcomponent_idx_;
};
public:
iterator begin() const { return iterator(&repr_); }
iterator end() const { return iterator(&repr_, iterator::end_iter_mask_val); }
};
std::string toString(DispatchKeySet /*ts*/);
std::ostream& operator<<(std::ostream& /*os*/, DispatchKeySet /*ts*/);
inline int getDispatchTableIndexForDispatchKey(DispatchKey k) {
return DispatchKeySet(k).getDispatchTableIndexForDispatchKeySet();
}
constexpr DispatchKeySet autograd_dispatch_keyset = DispatchKeySet({
DispatchKey::AutogradFunctionality,
DispatchKey::AutogradOther,
DispatchKey::AutogradNestedTensor,
});
constexpr DispatchKeySet autocast_dispatch_keyset = DispatchKeySet({
DispatchKey::AutocastCPU,
DispatchKey::AutocastMPS,
DispatchKey::AutocastCUDA,
DispatchKey::AutocastXPU,
DispatchKey::AutocastIPU,
DispatchKey::AutocastHPU,
DispatchKey::AutocastXLA,
DispatchKey::AutocastPrivateUse1,
DispatchKey::AutocastMTIA,
DispatchKey::AutocastMAIA,
});
constexpr DispatchKeySet default_included_set = DispatchKeySet({
DispatchKey::BackendSelect,
DispatchKey::ADInplaceOrView,
});
constexpr DispatchKeySet default_excluded_set = DispatchKeySet({
DispatchKey::AutocastCPU,
DispatchKey::AutocastMPS,
DispatchKey::AutocastCUDA,
DispatchKey::AutocastXPU,
DispatchKey::AutocastIPU,
DispatchKey::AutocastHPU,
DispatchKey::AutocastXLA,
DispatchKey::AutocastPrivateUse1,
DispatchKey::AutocastMTIA,
DispatchKey::AutocastMAIA,
});
constexpr DispatchKeySet autograd_dispatch_keyset_with_ADInplaceOrView =
autograd_dispatch_keyset | DispatchKeySet(DispatchKey::ADInplaceOrView);
constexpr DispatchKeySet python_ks = DispatchKeySet({
DispatchKey::Python,
DispatchKey::PythonTLSSnapshot,
});
constexpr DispatchKeySet sparse_ks = DispatchKeySet(DispatchKey::Sparse);
constexpr DispatchKeySet sparse_csr_ks = DispatchKeySet(DispatchKey::SparseCsr);
constexpr DispatchKeySet mkldnn_ks = DispatchKeySet(DispatchKey::MkldnnCPU);
constexpr DispatchKeySet autogradother_backends =
DispatchKeySet({DispatchKey::FPGA,
DispatchKey::Vulkan,
DispatchKey::Metal,
DispatchKey::CustomRNGKeyId,
DispatchKey::MkldnnCPU,
DispatchKey::Sparse,
DispatchKey::SparseCsr,
DispatchKey::Quantized}) |
DispatchKeySet(DispatchKeySet::RAW, full_backend_mask);
constexpr DispatchKeySet after_autograd_keyset =
DispatchKeySet(DispatchKeySet::FULL_AFTER, c10::DispatchKey::AutogradOther);
constexpr DispatchKeySet after_ADInplaceOrView_keyset = DispatchKeySet(
DispatchKeySet::FULL_AFTER, c10::DispatchKey::ADInplaceOrView);
constexpr DispatchKeySet after_func_keyset =
DispatchKeySet(DispatchKeySet::FULL_AFTER, c10::DispatchKey::Functionalize)
.remove(c10::DispatchKey::ADInplaceOrView);
constexpr DispatchKeySet backend_bitset_mask =
DispatchKeySet(DispatchKeySet::RAW, (1ULL << num_backends) - 1);
constexpr auto inplace_or_view_ks =
DispatchKeySet(DispatchKey::ADInplaceOrView);
constexpr auto autograd_cpu_ks = DispatchKeySet(DispatchKey::AutogradCPU);
constexpr auto autograd_ipu_ks = DispatchKeySet(DispatchKey::AutogradIPU);
constexpr auto autograd_mtia_ks = DispatchKeySet(DispatchKey::AutogradMTIA);
constexpr auto autograd_maia_ks = DispatchKeySet(DispatchKey::AutogradMAIA);
constexpr auto autograd_xpu_ks = DispatchKeySet(DispatchKey::AutogradXPU);
constexpr auto autograd_cuda_ks = DispatchKeySet(DispatchKey::AutogradCUDA);
constexpr auto autograd_xla_ks = DispatchKeySet(DispatchKey::AutogradXLA);
constexpr auto autograd_lazy_ks = DispatchKeySet(DispatchKey::AutogradLazy);
constexpr auto autograd_meta_ks = DispatchKeySet(DispatchKey::AutogradMeta);
constexpr auto autograd_mps_ks = DispatchKeySet(DispatchKey::AutogradMPS);
constexpr auto autograd_hpu_ks = DispatchKeySet(DispatchKey::AutogradHPU);
constexpr auto autograd_privateuse1_ks =
DispatchKeySet(DispatchKey::AutogradPrivateUse1);
constexpr auto autograd_privateuse2_ks =
DispatchKeySet(DispatchKey::AutogradPrivateUse2);
constexpr auto autograd_privateuse3_ks =
DispatchKeySet(DispatchKey::AutogradPrivateUse3);
constexpr auto autograd_other_ks = DispatchKeySet(DispatchKey::AutogradOther);
constexpr auto autograd_nested =
DispatchKeySet(DispatchKey::AutogradNestedTensor);
constexpr auto functorch_transforms_ks =
DispatchKeySet({DispatchKey::FuncTorchBatched,
DispatchKey::FuncTorchVmapMode,
DispatchKey::Batched,
DispatchKey::VmapMode,
DispatchKey::FuncTorchGradWrapper});
constexpr auto functorch_batched_ks =
DispatchKeySet({DispatchKey::FuncTorchBatched});
constexpr DispatchKeySet backend_functionality_keys =
DispatchKeySet({
DispatchKey::Dense,
DispatchKey::Quantized,
DispatchKey::Sparse,
DispatchKey::SparseCsr,
}) |
DispatchKeySet(DispatchKeySet::RAW, full_backend_mask);
struct OpTableOffsetAndMask {
uint16_t offset;
uint16_t backend_mask;
};
static_assert(num_backends <= 16,
"Right now we expect the number of backends not to exceed 16. In "
"the (unlikely) event"
" that this changes, the size of "
"OpTableOffsetAndMask::backend_mask needs to be increased too.");
bool isBackendDispatchKey(DispatchKey t);
DispatchKeySet getRuntimeDispatchKeySet(DispatchKey t);
bool runtimeDispatchKeySetHas(DispatchKey t, DispatchKey k);
DispatchKeySet getBackendKeySetFromAutograd(DispatchKey t);
inline DispatchKeySet getAutogradRelatedKeySetFromBackend(BackendComponent t) {
switch (t) {
case BackendComponent::CPUBit:
return inplace_or_view_ks | autograd_cpu_ks;
case BackendComponent::IPUBit:
return inplace_or_view_ks | autograd_ipu_ks;
case BackendComponent::MTIABit:
return inplace_or_view_ks | autograd_mtia_ks;
case BackendComponent::MAIABit:
return inplace_or_view_ks | autograd_maia_ks;
case BackendComponent::XPUBit:
return inplace_or_view_ks | autograd_xpu_ks;
case BackendComponent::CUDABit:
return inplace_or_view_ks | autograd_cuda_ks;
case BackendComponent::XLABit:
return inplace_or_view_ks | autograd_xla_ks;
case BackendComponent::LazyBit:
return inplace_or_view_ks | autograd_lazy_ks;
case BackendComponent::MetaBit:
return inplace_or_view_ks | autograd_meta_ks;
case BackendComponent::MPSBit:
return inplace_or_view_ks | autograd_mps_ks;
case BackendComponent::HPUBit:
return inplace_or_view_ks | autograd_hpu_ks;
case BackendComponent::PrivateUse1Bit:
return inplace_or_view_ks | autograd_privateuse1_ks;
case BackendComponent::PrivateUse2Bit:
return inplace_or_view_ks | autograd_privateuse2_ks;
case BackendComponent::PrivateUse3Bit:
return inplace_or_view_ks | autograd_privateuse3_ks;
default:
return inplace_or_view_ks | autograd_other_ks;
}
}
inline DispatchKeySet getAutocastRelatedKeySetFromBackend(BackendComponent t) {
constexpr auto autocast_cpu_ks = DispatchKeySet(DispatchKey::AutocastCPU);
constexpr auto autocast_mtia_ks = DispatchKeySet(DispatchKey::AutocastMTIA);
constexpr auto autocast_maia_ks = DispatchKeySet(DispatchKey::AutocastMAIA);
constexpr auto autocast_xpu_ks = DispatchKeySet(DispatchKey::AutocastXPU);
constexpr auto autocast_ipu_ks = DispatchKeySet(DispatchKey::AutocastIPU);
constexpr auto autocast_hpu_ks = DispatchKeySet(DispatchKey::AutocastHPU);
constexpr auto autocast_cuda_ks = DispatchKeySet(DispatchKey::AutocastCUDA);
constexpr auto autocast_xla_ks = DispatchKeySet(DispatchKey::AutocastXLA);
constexpr auto autocast_privateuse1_ks =
DispatchKeySet(DispatchKey::AutocastPrivateUse1);
constexpr auto autocast_mps_ks = DispatchKeySet(DispatchKey::AutocastMPS);
switch (t) {
case BackendComponent::CPUBit:
return autocast_cpu_ks;
case BackendComponent::MTIABit:
return autocast_mtia_ks;
case BackendComponent::MAIABit:
return autocast_maia_ks;
case BackendComponent::XPUBit:
return autocast_xpu_ks;
case BackendComponent::IPUBit:
return autocast_ipu_ks;
case BackendComponent::HPUBit:
return autocast_hpu_ks;
case BackendComponent::CUDABit:
return autocast_cuda_ks;
case BackendComponent::XLABit:
return autocast_xla_ks;
case BackendComponent::PrivateUse1Bit:
return autocast_privateuse1_ks;
case BackendComponent::MPSBit:
return autocast_mps_ks;
default:
return DispatchKeySet();
}
}
inline DispatchKey highestPriorityBackendTypeId(DispatchKeySet ks) {
return (ks & backend_functionality_keys).highestPriorityTypeId();
}
bool isIncludedInAlias(DispatchKey k, DispatchKey alias);
inline DispatchKey legacyExtractDispatchKey(DispatchKeySet s) {
return (s - autograd_dispatch_keyset_with_ADInplaceOrView -
autocast_dispatch_keyset -
DispatchKeySet({DispatchKey::Functionalize,
DispatchKey::PythonTLSSnapshot,
DispatchKey::FuncTorchGradWrapper,
DispatchKey::FuncTorchVmapMode,
DispatchKey::FuncTorchBatched,
DispatchKey::Python}))
.highestPriorityTypeId();
}
template <class T>
using is_not_DispatchKeySet = std::negation<std::is_same<DispatchKeySet, T>>;
// NOTE: remove_DispatchKeySet_arg_from_func is omitted because the
// c10::guts type-list utilities are not yet ported. The template is
// only used by the PyTorch dispatcher internals, which are not part
// of this compatibility layer.
inline std::string toString(DispatchKeySet ts) {
std::ostringstream oss;
oss << ts;
return oss.str();
}
inline std::ostream& operator<<(std::ostream& os, DispatchKeySet ts) {
os << "DispatchKeySet(";
bool first = true;
for (auto k : ts) {
if (!first) os << ", ";
os << toString(k);
first = false;
}
os << ")";
return os;
}
inline bool isBackendDispatchKey(DispatchKey t) {
return t >= DispatchKey::StartOfDenseBackends &&
t <= DispatchKey::EndOfRuntimeBackendKeys;
}
inline DispatchKeySet getRuntimeDispatchKeySet(DispatchKey t) {
if (isPerBackendFunctionalityKey(t)) {
DispatchKeySet result;
for (uint8_t backend = 1; backend <= num_backends; ++backend) {
result = result.add(toRuntimePerBackendFunctionalityKey(
t, static_cast<BackendComponent>(backend)));
}
return result;
}
return DispatchKeySet(t);
}
inline bool runtimeDispatchKeySetHas(DispatchKey t, DispatchKey k) {
return getRuntimeDispatchKeySet(t).has(k);
}
inline DispatchKeySet getBackendKeySetFromAutograd(DispatchKey t) {
if (t == DispatchKey::AutogradCPU) {
return DispatchKeySet(DispatchKey::CPU);
} else if (t == DispatchKey::AutogradCUDA) {
return DispatchKeySet(DispatchKey::CUDA);
} else if (t == DispatchKey::AutogradXPU) {
return DispatchKeySet(DispatchKey::XPU);
} else if (t == DispatchKey::AutogradIPU) {
return DispatchKeySet(DispatchKey::IPU);
} else if (t == DispatchKey::AutogradHPU) {
return DispatchKeySet(DispatchKey::HPU);
} else if (t == DispatchKey::AutogradLazy) {
return DispatchKeySet(DispatchKey::Lazy);
} else if (t == DispatchKey::AutogradMeta) {
return DispatchKeySet(DispatchKey::Meta);
} else if (t == DispatchKey::AutogradMPS) {
return DispatchKeySet(DispatchKey::MPS);
} else if (t == DispatchKey::AutogradPrivateUse1) {
return DispatchKeySet(DispatchKey::PrivateUse1);
} else if (t == DispatchKey::AutogradPrivateUse2) {
return DispatchKeySet(DispatchKey::PrivateUse2);
} else if (t == DispatchKey::AutogradPrivateUse3) {
return DispatchKeySet(DispatchKey::PrivateUse3);
} else if (t == DispatchKey::AutogradNestedTensor) {
return DispatchKeySet(DispatchKey::NestedTensor);
} else if (t == DispatchKey::AutogradOther) {
return autogradother_backends;
}
return DispatchKeySet();
}
inline bool isIncludedInAlias(DispatchKey k, DispatchKey alias) {
if (alias == DispatchKey::Autograd) {
return autograd_dispatch_keyset.has(k);
} else if (alias == DispatchKey::CompositeImplicitAutograd) {
return true;
} else if (alias == DispatchKey::CompositeExplicitAutograd) {
return k != DispatchKey::Autograd && !autograd_dispatch_keyset.has(k);
}
return false;
}
inline std::array<FunctionalityOffsetAndMask, num_functionality_keys>
initializeFunctionalityOffsetsAndMasks() {
std::array<FunctionalityOffsetAndMask, num_functionality_keys> result{};
uint16_t offset = 0;
for (uint8_t i = 0; i < num_functionality_keys; ++i) {
DispatchKey key = static_cast<DispatchKey>(i);
if (isPerBackendFunctionalityKey(key)) {
result[i] = FunctionalityOffsetAndMask(offset, full_backend_mask);
offset += num_backends;
} else {
result[i] = FunctionalityOffsetAndMask(offset, 0);
offset += 1;
}
}
return result;
}
} // namespace c10
@@ -0,0 +1,339 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/Device.h>
#include <c10/core/DeviceType.h>
#include <c10/core/Stream.h>
#include <utility>
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include <c10/cuda/CUDAGuard.h>
#include <c10/cuda/CUDAStream.h>
#endif
namespace c10 {
enum class EventFlag { PYTORCH_DEFAULT, BACKEND_DEFAULT, INVALID };
struct Event final {
public:
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#ifdef PADDLE_WITH_HIP
using BackendEvent = hipEvent_t;
using BackendStream = hipStream_t;
#else
using BackendEvent = cudaEvent_t;
using BackendStream = cudaStream_t;
#endif
#endif
Event() = delete;
Event(const DeviceType device_type,
const EventFlag flag = EventFlag::PYTORCH_DEFAULT)
: device_type_(device_type), flag_(flag) {}
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
Event(Event&& other) noexcept { MoveFrom(std::move(other)); }
Event& operator=(Event&& other) noexcept {
if (this != &other) {
DestroyBackendEvent();
MoveFrom(std::move(other));
}
return *this;
}
~Event() { DestroyBackendEvent(); }
Device device() const noexcept { return Device(device_type_, device_index_); }
DeviceType device_type() const noexcept { return device_type_; }
DeviceIndex device_index() const noexcept { return device_index_; }
EventFlag flag() const noexcept { return flag_; }
bool was_marked_for_recording() const noexcept {
return was_marked_for_recording_;
}
void recordOnce(const Stream& stream) {
if (!was_marked_for_recording_) {
record(stream);
}
}
void record(const Stream& stream) {
TORCH_CHECK(stream.device_type() == device_type_,
"Event device type ",
device_type_,
" does not match recording stream's device type ",
stream.device_type(),
".");
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type_ == DeviceType::CUDA) {
RecordBackendEvent(static_cast<BackendStream>(stream.native_handle()),
stream.device_index());
return;
}
#endif
TORCH_CHECK(false, "Backend doesn't support events.");
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
void record(const c10::cuda::CUDAStream& stream) { record(stream.unwrap()); }
#endif
void block(const Stream& stream) const {
if (!was_marked_for_recording_) {
return;
}
TORCH_CHECK(stream.device_type() == device_type_,
"Event device type ",
device_type_,
" does not match blocking stream's device type ",
stream.device_type(),
".");
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type_ == DeviceType::CUDA && backend_event_) {
TORCH_CHECK(device_index_ == stream.device_index(),
"Event device index ",
static_cast<int>(device_index_),
" does not match blocking stream's device index ",
static_cast<int>(stream.device_index()),
".");
c10::cuda::CUDAGuard guard(device_index_);
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(
hipStreamWaitEvent(static_cast<BackendStream>(stream.native_handle()),
backend_event_,
0));
#else
C10_CUDA_CHECK(cudaStreamWaitEvent(
static_cast<BackendStream>(stream.native_handle()),
backend_event_,
0));
#endif
return;
}
#endif
TORCH_CHECK(false, "Backend doesn't support events.");
}
bool query() const {
if (!was_marked_for_recording_) {
return true;
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type_ == DeviceType::CUDA && backend_event_) {
#ifdef PADDLE_WITH_HIP
const auto err = hipEventQuery(backend_event_);
if (err == hipSuccess) {
return true;
}
if (err != hipErrorNotReady) {
PADDLE_ENFORCE_GPU_SUCCESS(err);
} else {
(void)hipGetLastError();
}
#else
const auto err = cudaEventQuery(backend_event_);
if (err == cudaSuccess) {
return true;
}
if (err != cudaErrorNotReady) {
C10_CUDA_CHECK(err);
} else {
(void)cudaGetLastError();
}
#endif
return false;
}
#endif
TORCH_CHECK(false, "Backend doesn't support events.");
return true;
}
double elapsedTime(const Event& event) const {
TORCH_CHECK(event.device_type() == device_type_,
"Event device type ",
device_type_,
" does not match other's device type ",
event.device_type(),
".");
TORCH_CHECK(
flag_ == EventFlag::BACKEND_DEFAULT &&
event.flag_ == EventFlag::BACKEND_DEFAULT,
"Both events must be created with argument 'enable_timing=True'.");
TORCH_CHECK(
was_marked_for_recording_ && event.was_marked_for_recording_,
"Both events must be recorded before calculating elapsed time.");
TORCH_CHECK(
query() && event.query(),
"Both events must be completed before calculating elapsed time.");
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type_ == DeviceType::CUDA && backend_event_ &&
event.backend_event_) {
TORCH_CHECK(device_index_ == event.device_index_,
"Event device index ",
static_cast<int>(device_index_),
" does not match other's device index ",
static_cast<int>(event.device_index_),
".");
c10::cuda::CUDAGuard guard(device_index_);
float time_ms = 0.0f;
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(
hipEventElapsedTime(&time_ms, backend_event_, event.backend_event_));
#else
C10_CUDA_CHECK(
cudaEventElapsedTime(&time_ms, backend_event_, event.backend_event_));
#endif
return static_cast<double>(time_ms);
}
#endif
TORCH_CHECK(false, "Backend doesn't support event elapsedTime.");
return 0.0;
}
void* eventId() const {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
return backend_event_;
#else
return nullptr;
#endif
}
void synchronize() const {
if (!was_marked_for_recording_) {
return;
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type_ == DeviceType::CUDA && backend_event_) {
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventSynchronize(backend_event_));
#else
C10_CUDA_CHECK(cudaEventSynchronize(backend_event_));
#endif
return;
}
#endif
TORCH_CHECK(false, "Backend doesn't support events.");
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#ifdef PADDLE_WITH_HIP
hipEvent_t cuda_event() const { return backend_event_; }
#else
cudaEvent_t cuda_event() const { return backend_event_; }
#endif
#endif
private:
DeviceType device_type_;
DeviceIndex device_index_ = -1;
EventFlag flag_ = EventFlag::PYTORCH_DEFAULT;
bool was_marked_for_recording_ = false;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
BackendEvent backend_event_ = nullptr;
static unsigned int BackendEventCreateFlags(EventFlag flag) {
switch (flag) {
case EventFlag::PYTORCH_DEFAULT:
#ifdef PADDLE_WITH_HIP
return hipEventDisableTiming;
#else
return cudaEventDisableTiming;
#endif
case EventFlag::BACKEND_DEFAULT:
#ifdef PADDLE_WITH_HIP
return hipEventDefault;
#else
return cudaEventDefault;
#endif
default:
TORCH_CHECK(false, "CUDA event received unknown flag");
}
}
void EnsureBackendEventCreated(DeviceIndex stream_device_index) {
if (backend_event_) {
return;
}
c10::cuda::CUDAGuard guard(stream_device_index);
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventCreateWithFlags(
&backend_event_, BackendEventCreateFlags(flag_)));
#else
C10_CUDA_CHECK(cudaEventCreateWithFlags(&backend_event_,
BackendEventCreateFlags(flag_)));
#endif
}
void RecordBackendEvent(BackendStream stream,
DeviceIndex stream_device_index) {
TORCH_CHECK(device_index_ == -1 || device_index_ == stream_device_index,
"Event device index ",
static_cast<int>(device_index_),
" does not match recording stream's device index ",
static_cast<int>(stream_device_index),
".");
EnsureBackendEventCreated(stream_device_index);
c10::cuda::CUDAGuard guard(stream_device_index);
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventRecord(backend_event_, stream));
#else
C10_CUDA_CHECK(cudaEventRecord(backend_event_, stream));
#endif
device_index_ = stream_device_index;
was_marked_for_recording_ = true;
}
void DestroyBackendEvent() noexcept {
if (!backend_event_) {
return;
}
try {
c10::cuda::CUDAGuard guard(device_index_);
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipEventDestroy(backend_event_));
#else
C10_CUDA_CHECK(cudaEventDestroy(backend_event_));
#endif
} catch (...) {
}
backend_event_ = nullptr;
}
#else
void DestroyBackendEvent() noexcept {}
#endif
void MoveFrom(Event&& other) noexcept {
device_type_ = other.device_type_;
device_index_ = other.device_index_;
flag_ = other.flag_;
was_marked_for_recording_ = other.was_marked_for_recording_;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
backend_event_ = std::exchange(other.backend_event_, nullptr);
#endif
other.device_index_ = -1;
other.was_marked_for_recording_ = false;
}
};
} // namespace c10
namespace torch {
using c10::Event;
} // namespace torch
@@ -0,0 +1,147 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/Device.h>
#include <c10/core/DispatchKeySet.h>
#include <c10/util/Exception.h>
#include <c10/util/intrusive_ptr.h>
#include <cstdint>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <utility>
#include "paddle/phi/core/generator.h"
// Forward declare PyObject for the pyobj interface.
#ifndef PyObject
struct _object;
using PyObject = _object;
#endif
namespace c10 {
/**
* GeneratorImpl — base implementation class for at::Generator.
*
* Wraps a Paddle phi::Generator and exposes the PyTorch-style API
* (set_current_seed, seed, offset, device, key_set, etc.).
*
* Subclasses (e.g., CPUGeneratorImpl, CUDAGeneratorImpl) may extend
* this to add backend-specific functionality.
*
* Note: Inherits from intrusive_ptr_target to support intrusive_ptr.
*/
class GeneratorImpl : public intrusive_ptr_target {
public:
// ------- constructors / destructor ----------------------------------------
/// Construct from an existing Paddle generator and a device.
explicit GeneratorImpl(Device device,
std::shared_ptr<phi::Generator> gen = nullptr)
: device_(device), pyobj_(nullptr) {
if (gen) {
gen_ = std::move(gen);
} else {
gen_ = std::make_shared<phi::Generator>();
}
}
// Virtual destructor required for polymorphic base class
// Note: intrusive_ptr_target destructor is protected, but we need public
// destructor for delete to work through base pointer
~GeneratorImpl() override = default;
// Non-copyable / non-movable (mirroring PyTorch semantics).
GeneratorImpl(const GeneratorImpl&) = delete;
GeneratorImpl& operator=(const GeneratorImpl&) = delete;
// ------- seed / offset API ------------------------------------------------
virtual void set_current_seed(uint64_t seed) { gen_->SetCurrentSeed(seed); }
virtual uint64_t current_seed() const { return gen_->GetCurrentSeed(); }
/// Generate and set a new random seed; return it.
virtual uint64_t seed() { return gen_->Seed(); }
/// Set the Philox offset (supported in CUDA / MPS generators).
virtual void set_offset(uint64_t offset) {
// phi::Generator stores offset in its state; we reset it via
// IncrementOffset after backing up the current offset.
auto state = gen_->GetState();
uint64_t cur = state.offset;
if (offset > cur) {
gen_->IncrementOffset(offset - cur);
} else {
// To move backwards, we need to reset the state.
state.offset = offset;
gen_->SetState(state);
}
}
virtual uint64_t get_offset() const { return gen_->GetCurrentOffset(); }
// ------- device / dispatch ------------------------------------------------
Device device() const { return device_; }
DispatchKeySet key_set() const {
auto dt = device_.type();
if (dt == kCPU) {
return DispatchKeySet(DispatchKey::CPU);
} else if (dt == kCUDA) {
return DispatchKeySet(DispatchKey::CUDA);
}
return DispatchKeySet();
}
// ------- clone ------------------------------------------------------------
virtual intrusive_ptr<GeneratorImpl> clone() const {
auto state = gen_->GetState();
auto new_gen = std::make_shared<phi::Generator>(state.seed);
new_gen->SetState(state);
auto impl = make_intrusive<GeneratorImpl>(device_, new_gen);
return impl;
}
// ------- mutex (for thread-safe usage) ------------------------------------
/// Public mutex for external locking (see PyTorch Note [Acquire lock ...]).
std::mutex mutex_;
// ------- PyObject binding -------------------------------------------------
void set_pyobj(PyObject* pyobj) noexcept { pyobj_ = pyobj; }
PyObject* pyobj() const noexcept { return pyobj_; }
// ------- internal accessor ------------------------------------------------
/// Access the underlying Paddle generator.
std::shared_ptr<phi::Generator> paddle_generator() const { return gen_; }
protected:
std::shared_ptr<phi::Generator> gen_;
Device device_;
PyObject* pyobj_;
};
} // namespace c10
@@ -0,0 +1,96 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/util/Exception.h>
#include <cstdint>
#include <ostream>
namespace c10 {
enum class Layout : int8_t {
Strided,
Sparse,
SparseCsr,
Mkldnn,
SparseCsc,
SparseBsr,
SparseBsc,
Jagged,
NumOptions
};
constexpr auto kStrided = Layout::Strided;
constexpr auto kSparse = Layout::Sparse;
constexpr auto kSparseCsr = Layout::SparseCsr;
constexpr auto kMkldnn = Layout::Mkldnn;
constexpr auto kSparseCsc = Layout::SparseCsc;
constexpr auto kSparseBsr = Layout::SparseBsr;
constexpr auto kSparseBsc = Layout::SparseBsc;
constexpr auto kJagged = Layout::Jagged;
inline std::ostream& operator<<(std::ostream& stream, c10::Layout layout) {
switch (layout) {
case c10::kStrided:
return stream << "Strided";
case c10::kSparse:
return stream << "Sparse";
case c10::kSparseCsr:
return stream << "SparseCsr";
case c10::kSparseCsc:
return stream << "SparseCsc";
case c10::kSparseBsr:
return stream << "SparseBsr";
case c10::kSparseBsc:
return stream << "SparseBsc";
case c10::kMkldnn:
return stream << "Mkldnn";
case c10::kJagged:
return stream << "Jagged";
default:
TORCH_CHECK(false, "Unknown layout");
}
}
} // namespace c10
namespace at {
using c10::kJagged;
using c10::kMkldnn;
using c10::kSparse;
using c10::kSparseBsc;
using c10::kSparseBsr;
using c10::kSparseCsc;
using c10::kSparseCsr;
using c10::kStrided;
using c10::Layout;
} // namespace at
namespace torch {
using c10::kJagged;
using c10::kMkldnn;
using c10::kSparse;
using c10::kSparseBsc;
using c10::kSparseBsr;
using c10::kSparseCsc;
using c10::kSparseCsr;
using c10::kStrided;
using c10::Layout;
} // namespace torch
@@ -0,0 +1,92 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <vector>
namespace c10 {
// c10::List is a type-safe wrapper around std::vector for PyTorch compatibility
template <typename T>
class List {
public:
using value_type = T;
using size_type = typename std::vector<T>::size_type;
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
using reference = typename std::vector<T>::reference;
using const_reference = typename std::vector<T>::const_reference;
List() = default;
List(std::initializer_list<T> init) : vec_(init) {}
explicit List(size_type count) : vec_(count) {}
List(size_type count, const T& value) : vec_(count, value) {}
template <typename InputIt>
List(InputIt first, InputIt last) : vec_(first, last) {}
List(const std::vector<T>& vec) : vec_(vec) {} // NOLINT
List(std::vector<T>&& vec) : vec_(std::move(vec)) {} // NOLINT
// Conversion to std::vector
const std::vector<T>& vec() const { return vec_; }
std::vector<T>& vec() { return vec_; }
operator const std::vector<T>&() const { return vec_; } // NOLINT
operator std::vector<T>&() { return vec_; } // NOLINT
// Standard vector-like interface
size_type size() const { return vec_.size(); }
bool empty() const { return vec_.empty(); }
void clear() { vec_.clear(); }
void reserve(size_type new_cap) { vec_.reserve(new_cap); }
size_type capacity() const { return vec_.capacity(); }
reference operator[](size_type pos) { return vec_[pos]; }
const_reference operator[](size_type pos) const { return vec_[pos]; }
reference at(size_type pos) { return vec_.at(pos); }
const_reference at(size_type pos) const { return vec_.at(pos); }
reference front() { return vec_.front(); }
const_reference front() const { return vec_.front(); }
reference back() { return vec_.back(); }
const_reference back() const { return vec_.back(); }
void push_back(const T& value) { vec_.push_back(value); }
void push_back(T&& value) { vec_.push_back(std::move(value)); }
template <typename... Args>
reference emplace_back(Args&&... args) {
return vec_.emplace_back(std::forward<Args>(args)...);
}
void pop_back() { vec_.pop_back(); }
iterator begin() { return vec_.begin(); }
const_iterator begin() const { return vec_.begin(); }
const_iterator cbegin() const { return vec_.cbegin(); }
iterator end() { return vec_.end(); }
const_iterator end() const { return vec_.end(); }
const_iterator cend() const { return vec_.cend(); }
void resize(size_type count) { vec_.resize(count); }
void resize(size_type count, const T& value) { vec_.resize(count, value); }
bool operator==(const List<T>& other) const { return vec_ == other.vec_; }
bool operator!=(const List<T>& other) const { return vec_ != other.vec_; }
private:
std::vector<T> vec_;
};
} // namespace c10
@@ -0,0 +1,40 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
enum class PADDLE_API MemoryFormat : int8_t {
Contiguous,
Preserve,
ChannelsLast,
ChannelsLast3d,
NumOptions
};
}
namespace at {
using c10::MemoryFormat;
} // namespace at
namespace torch {
using c10::MemoryFormat;
} // namespace torch
@@ -0,0 +1,28 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/scalar.h"
namespace c10 {
using Scalar = paddle::experimental::Scalar;
}
namespace at {
using c10::Scalar;
} // namespace at
namespace torch {
using c10::Scalar;
} // namespace torch
@@ -0,0 +1,267 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
// If you modified ScalarType foundational definitions in this file, please
// also sync your changes into torch/headeronly/core/ScalarType.h.
#include <torch/headeronly/core/ScalarType.h>
#include <c10/util/Exception.h>
#include <limits>
#include <sstream>
namespace c10 {
#define DEFINE_CONSTANT(_1, _2, name) \
constexpr ScalarType k##name = ScalarType::name;
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(DEFINE_CONSTANT)
#undef DEFINE_CONSTANT
constexpr ScalarType kUndefined = ScalarType::Undefined;
inline size_t elementSize(ScalarType t) {
#define CASE_ELEMENTSIZE_CASE(ctype, _2, name) \
case ScalarType::name: \
return sizeof(ctype);
switch (t) {
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(CASE_ELEMENTSIZE_CASE)
default:
TORCH_CHECK(false, "Unknown ScalarType");
}
#undef CASE_ELEMENTSIZE_CASE
}
inline bool isIntegralType(ScalarType t, bool includeBool) {
bool isIntegral = (t == ScalarType::Byte || t == ScalarType::Char ||
t == ScalarType::Int || t == ScalarType::Long ||
t == ScalarType::Short || t == ScalarType::UInt16 ||
t == ScalarType::UInt32 || t == ScalarType::UInt64);
return isIntegral || (includeBool && t == ScalarType::Bool);
}
inline bool isFloat8Type(ScalarType t) {
return t == ScalarType::Float8_e5m2 || t == ScalarType::Float8_e4m3fn ||
t == ScalarType::Float8_e5m2fnuz || t == ScalarType::Float8_e4m3fnuz ||
t == ScalarType::Float8_e8m0fnu;
}
inline bool isReducedFloatingType(ScalarType t) {
return t == ScalarType::Half || t == ScalarType::BFloat16 ||
isFloat8Type(t) || t == ScalarType::Float4_e2m1fn_x2;
}
inline bool isFloatingType(ScalarType t) {
return t == ScalarType::Double || t == ScalarType::Float ||
isReducedFloatingType(t);
}
inline bool isComplexType(ScalarType t) {
return (t == ScalarType::ComplexHalf || t == ScalarType::ComplexFloat ||
t == ScalarType::ComplexDouble);
}
inline bool isBitsType(ScalarType t) {
return t == ScalarType::Bits1x8 || t == ScalarType::Bits2x4 ||
t == ScalarType::Bits4x2 || t == ScalarType::Bits8 ||
t == ScalarType::Bits16;
}
inline bool isBarebonesUnsignedType(ScalarType t) {
return t == ScalarType::UInt1 || t == ScalarType::UInt2 ||
t == ScalarType::UInt3 || t == ScalarType::UInt4 ||
t == ScalarType::UInt5 || t == ScalarType::UInt6 ||
t == ScalarType::UInt7 || t == ScalarType::UInt16 ||
t == ScalarType::UInt32 || t == ScalarType::UInt64;
}
inline ScalarType toQIntType(ScalarType t) {
switch (t) {
case ScalarType::Byte:
return ScalarType::QUInt8;
case ScalarType::Char:
return ScalarType::QInt8;
case ScalarType::Int:
return ScalarType::QInt32;
default:
return t;
}
}
inline bool isSignedType(ScalarType t) {
#define CASE_ISSIGNED(name) \
case ScalarType::name: \
return std::numeric_limits< \
::c10::impl::ScalarTypeToCPPTypeT<ScalarType::name>>::is_signed;
switch (t) {
// Signed integer types (using numeric_limits)
CASE_ISSIGNED(Char); // int8_t
CASE_ISSIGNED(Short); // int16_t
CASE_ISSIGNED(Int); // int32_t
CASE_ISSIGNED(Long); // int64_t
// Signed integer types (dummy types, explicitly return true)
case ScalarType::Int1:
case ScalarType::Int2:
case ScalarType::Int3:
case ScalarType::Int4:
case ScalarType::Int5:
case ScalarType::Int6:
case ScalarType::Int7:
return true;
// Signed floating point types (using numeric_limits)
CASE_ISSIGNED(Half); // float16
CASE_ISSIGNED(Float); // float32
CASE_ISSIGNED(Double); // float64
CASE_ISSIGNED(BFloat16);
CASE_ISSIGNED(Float8_e5m2);
CASE_ISSIGNED(Float8_e4m3fn);
// Complex types (treated as signed)
case ScalarType::ComplexHalf:
case ScalarType::ComplexFloat:
case ScalarType::ComplexDouble:
return true;
// Signed quantized types (explicitly return true)
case ScalarType::QInt8:
case ScalarType::QInt32:
return true;
// Unsigned integer types (using numeric_limits)
CASE_ISSIGNED(Byte); // uint8_t
// Unsigned integer types (explicitly return false)
case ScalarType::UInt16:
case ScalarType::UInt32:
case ScalarType::UInt64:
case ScalarType::UInt1:
case ScalarType::UInt2:
case ScalarType::UInt3:
case ScalarType::UInt4:
case ScalarType::UInt5:
case ScalarType::UInt6:
case ScalarType::UInt7:
return false;
// Unsigned quantized types (explicitly return false)
case ScalarType::QUInt8:
case ScalarType::QUInt4x2:
case ScalarType::QUInt2x4:
case ScalarType::Bits1x8:
case ScalarType::Bits2x4:
case ScalarType::Bits4x2:
case ScalarType::Bits8:
case ScalarType::Bits16:
return false;
// Bool is unsigned (using numeric_limits)
CASE_ISSIGNED(Bool);
case ScalarType::Float8_e5m2fnuz:
case ScalarType::Float8_e4m3fnuz:
case ScalarType::Float8_e8m0fnu:
case ScalarType::Float4_e2m1fn_x2:
return true;
// Invalid/undefined types - should not happen in normal usage
// If this is hit, it indicates a programming error or unsupported type
case ScalarType::Undefined:
case ScalarType::NumOptions: {
std::ostringstream oss;
oss << "isSignedType: Invalid or unsupported ScalarType value: "
<< toString(t) << " (" << static_cast<int>(t) << ")";
PD_THROW(oss.str());
return false; // Unreachable, but satisfies compiler
}
// Note: If a new ScalarType is added to the enum but not handled here,
// the compiler will warn about missing case. This ensures all types are
// explicitly handled.
}
#undef CASE_ISSIGNED
return false; // Unreachable, but satisfies compiler
}
inline bool isUnderlying(ScalarType type, ScalarType qtype) {
return type == toUnderlying(qtype);
}
inline ScalarType toRealValueType(ScalarType t) {
switch (t) {
case ScalarType::ComplexHalf:
return ScalarType::Half;
case ScalarType::ComplexFloat:
return ScalarType::Float;
case ScalarType::ComplexDouble:
return ScalarType::Double;
default:
return t;
}
}
inline ScalarType toComplexType(ScalarType t) {
switch (t) {
case ScalarType::BFloat16:
return ScalarType::ComplexFloat;
case ScalarType::Half:
return ScalarType::ComplexHalf;
case ScalarType::Float:
return ScalarType::ComplexFloat;
case ScalarType::Double:
return ScalarType::ComplexDouble;
case ScalarType::ComplexHalf:
return ScalarType::ComplexHalf;
case ScalarType::ComplexFloat:
return ScalarType::ComplexFloat;
case ScalarType::ComplexDouble:
return ScalarType::ComplexDouble;
default:
TORCH_CHECK(false, "Unknown Complex ScalarType for ", t);
}
}
inline bool canCast(const ScalarType from, const ScalarType to) {
if (isComplexType(from) && !isComplexType(to)) {
return false;
}
if (isFloatingType(from) && isIntegralType(to, false)) {
return false;
}
if (from != ScalarType::Bool && to == ScalarType::Bool) {
return false;
}
return true;
}
} // namespace c10
namespace at {
using c10::CppTypeToScalarType;
using c10::ScalarType;
} // namespace at
namespace torch {
using c10::CppTypeToScalarType;
using c10::ScalarType;
} // namespace torch
@@ -0,0 +1,54 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/ScalarType.h>
#include <c10/util/typeid.h>
#include <optional>
namespace c10 {
/// Convert ScalarType enum values to TypeMeta handles.
inline caffe2::TypeMeta scalarTypeToTypeMeta(ScalarType scalar_type) {
return caffe2::TypeMeta::fromScalarType(scalar_type);
}
/// Convert TypeMeta handles to ScalarType enum values.
inline ScalarType typeMetaToScalarType(caffe2::TypeMeta dtype) {
return dtype.toScalarType();
}
/// typeMetaToScalarType(), lifted to optional.
inline std::optional<ScalarType> optTypeMetaToScalarType(
std::optional<caffe2::TypeMeta> type_meta) {
if (!type_meta.has_value()) {
return std::nullopt;
}
return type_meta->toScalarType();
}
/// Equality across TypeMeta / ScalarType.
inline bool operator==(ScalarType t, caffe2::TypeMeta m) {
return m.isScalarType(t);
}
inline bool operator==(caffe2::TypeMeta m, ScalarType t) { return t == m; }
inline bool operator!=(ScalarType t, caffe2::TypeMeta m) { return !(t == m); }
inline bool operator!=(caffe2::TypeMeta m, ScalarType t) { return !(t == m); }
} // namespace c10
@@ -0,0 +1,498 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <memory>
#include <utility>
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/allocator.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/storage_properties.h"
#include "c10/core/Allocator.h" // For DataPtr
namespace c10 {
struct Storage;
class StorageHolderView;
// Check if two storages share the same underlying allocation
inline bool isSharedStorageAlias(const Storage& storage0,
const Storage& storage1);
// Shared state for Storage handles. All Storage copies that refer to the
// same logical storage share a single StorageImpl, so mutations via
// set_data_ptr*(), set_nbytes(), or mutable_data_ptr() are visible to all
// handles — matching the observable contract of c10::StorageImpl in PyTorch.
struct StorageImpl {
std::shared_ptr<phi::Allocation> data_allocation_;
phi::Allocator* allocator_ = nullptr;
size_t nbytes_ = 0;
bool resizable_ = false;
phi::Place place_;
// DataPtr is stored directly (not in a shared_ptr) because all Storage
// copies already share this StorageImpl, so one level of indirection
// suffices to propagate mutations.
DataPtr data_ptr_;
std::weak_ptr<StorageHolderView> tensor_holder_;
};
class StorageHolderView final : public phi::Allocation {
public:
explicit StorageHolderView(std::shared_ptr<StorageImpl> impl)
: impl_(std::move(impl)) {}
std::shared_ptr<StorageImpl> get_impl() const { return impl_; }
void* ptr() const noexcept override {
if (!impl_) {
return nullptr;
}
if (impl_->data_allocation_) {
return impl_->data_allocation_->ptr();
}
return impl_->data_ptr_.get();
}
size_t size() const noexcept override { return impl_ ? impl_->nbytes_ : 0; }
const Place& place() const noexcept override {
return impl_ ? impl_->place_ : place_;
}
private:
std::shared_ptr<StorageImpl> impl_;
Place place_;
};
struct Storage {
public:
// Tag types for constructor disambiguation (LibTorch compatible)
struct use_byte_size_t {};
struct unsafe_borrow_t {
unsafe_borrow_t() = default;
};
// Default constructor: empty storage, no allocation, no data.
Storage() : impl_(std::make_shared<StorageImpl>()) {}
// Copy constructor: shares the StorageImpl so that mutations made through
// either handle are visible through the other.
Storage(const Storage& other) : impl_(other.impl_) {}
// Copy assignment operator
Storage& operator=(const Storage& other) {
if (this != &other) {
impl_ = other.impl_;
}
return *this;
}
// Move constructor: transfers ownership of the StorageImpl.
// The moved-from object is left in an unspecified (but destructible) state.
Storage(Storage&& other) noexcept : impl_(std::move(other.impl_)) {}
// Move assignment operator
Storage& operator=(Storage&& other) noexcept {
if (this != &other) {
impl_ = std::move(other.impl_);
}
return *this;
}
// Constructor with allocation and optional storage properties
Storage(std::shared_ptr<phi::Allocation> alloc,
std::unique_ptr<phi::StorageProperties> props = nullptr) {
impl_ = std::make_shared<StorageImpl>();
if (alloc) {
syncFromAllocation(std::move(alloc));
}
}
// Constructor with size and allocator (LibTorch compatible)
explicit Storage(size_t size_bytes, phi::Allocator* allocator = nullptr) {
impl_ = std::make_shared<StorageImpl>();
if (allocator) {
auto alloc =
std::shared_ptr<phi::Allocation>(allocator->Allocate(size_bytes));
impl_->allocator_ = allocator;
syncFromAllocation(std::move(alloc));
impl_->nbytes_ = size_bytes;
}
}
// LibTorch compatible constructor with use_byte_size_t tag
Storage(use_byte_size_t /*use_byte_size*/,
size_t size_bytes,
phi::Allocator* allocator = nullptr,
bool resizable = false) {
impl_ = std::make_shared<StorageImpl>();
impl_->allocator_ = allocator;
impl_->nbytes_ = size_bytes;
impl_->resizable_ = resizable;
if (allocator) {
auto alloc =
std::shared_ptr<phi::Allocation>(allocator->Allocate(size_bytes));
syncFromAllocation(std::move(alloc));
impl_->nbytes_ = size_bytes;
}
}
// LibTorch compatible constructor with pre-allocated phi::Allocation
Storage(use_byte_size_t /*use_byte_size*/,
size_t size_bytes,
std::shared_ptr<phi::Allocation> alloc,
phi::Allocator* allocator = nullptr,
bool resizable = false) {
impl_ = std::make_shared<StorageImpl>();
impl_->allocator_ = allocator;
impl_->nbytes_ = size_bytes;
impl_->resizable_ = resizable;
syncFromAllocation(std::move(alloc));
impl_->nbytes_ = size_bytes;
}
// LibTorch compatible constructor with pre-allocated DataPtr
Storage(use_byte_size_t /*use_byte_size*/,
size_t size_bytes,
DataPtr data_ptr,
phi::Allocator* allocator = nullptr,
bool resizable = false) {
impl_ = std::make_shared<StorageImpl>();
impl_->allocator_ = allocator;
impl_->nbytes_ = size_bytes;
impl_->resizable_ = resizable;
syncFromDataPtr(std::move(data_ptr), size_bytes);
}
protected:
// Unsafe borrow constructor (for MaybeOwnedTraits): shares the StorageImpl.
// With the shared-impl design this is equivalent to a regular copy, but the
// tag distinguishes "borrow" intent from ordinary copies at call sites.
explicit Storage(unsafe_borrow_t, const Storage& rhs) : impl_(rhs.impl_) {}
// Forward declare template and make specialization a friend
template <typename T>
friend struct MaybeOwnedTraits;
public:
// Construct from a pre-existing shared StorageImpl (used by the global
// per-tensor storage registry to reuse an existing StorageImpl).
explicit Storage(std::shared_ptr<StorageImpl> impl)
: impl_(std::move(impl)) {}
// Returns the underlying shared StorageImpl (used by the global per-tensor
// storage registry).
std::shared_ptr<StorageImpl> get_impl() const { return impl_; }
static Storage createTensorStorage(
const std::shared_ptr<phi::Allocation>& holder) {
if (!holder) {
return Storage();
}
if (auto storage_holder =
std::dynamic_pointer_cast<StorageHolderView>(holder)) {
return Storage(storage_holder->get_impl());
}
auto impl = std::make_shared<StorageImpl>();
Storage storage(std::move(impl));
storage.syncFromAllocation(holder);
storage.ensureTensorHolder();
return storage;
}
std::shared_ptr<phi::Allocation> ensureTensorHolder() const {
if (!impl_) {
return nullptr;
}
auto holder = impl_->tensor_holder_.lock();
if (!holder) {
holder = std::make_shared<StorageHolderView>(impl_);
impl_->tensor_holder_ = holder;
}
return holder;
}
// Check if storage is valid (has allocation or data)
bool valid() const {
return impl_ && (static_cast<bool>(impl_->data_allocation_) ||
static_cast<bool>(impl_->data_ptr_));
}
// Boolean conversion operator (LibTorch compatible)
explicit operator bool() const { return valid(); }
// Get the number of bytes in the storage
size_t nbytes() const { return impl_ ? impl_->nbytes_ : 0; }
// Set the number of bytes.
// For resizable storage with an allocator, reallocates; otherwise updates
// the byte count directly so the change is visible to all copies.
void set_nbytes(size_t size_bytes) {
if (!impl_) return;
if (impl_->resizable_ && impl_->allocator_) {
syncFromAllocation(std::shared_ptr<phi::Allocation>(
impl_->allocator_->Allocate(size_bytes)));
impl_->nbytes_ = size_bytes;
} else {
impl_->nbytes_ = size_bytes;
}
}
// Check if storage is resizable
bool resizable() const { return impl_ ? impl_->resizable_ : false; }
// Get mutable data pointer
void* mutable_data() const {
if (!impl_) {
return nullptr;
}
if (impl_->data_allocation_) {
return impl_->data_allocation_->ptr();
}
return impl_->data_ptr_.get();
}
// Get const data pointer
const void* data() const {
if (!impl_) {
return nullptr;
}
if (impl_->data_allocation_) {
return impl_->data_allocation_->ptr();
}
return impl_->data_ptr_.get();
}
// Get a const reference to the underlying DataPtr (LibTorch compatible)
const DataPtr& data_ptr() const { return impl_->data_ptr_; }
// Get a mutable reference to the underlying DataPtr (LibTorch compatible).
// Because all Storage copies share the same StorageImpl, this reference
// reflects and propagates changes to all handles — matching PyTorch's
// StorageImpl semantics where mutable_data_ptr() returns the member directly.
DataPtr& mutable_data_ptr() const { return impl_->data_ptr_; }
// Get the underlying phi::Allocation (Paddle-specific)
std::shared_ptr<phi::Allocation> allocation() const {
return impl_ ? impl_->data_allocation_ : nullptr;
}
// Get the allocator
phi::Allocator* allocator() const {
return impl_ ? impl_->allocator_ : nullptr;
}
// Get the device/place type
phi::AllocationType device_type() const {
if (!impl_) return phi::AllocationType::CPU;
if (impl_->data_allocation_)
return impl_->data_allocation_->place().GetType();
if (impl_->data_ptr_)
return c10::DeviceTypeToPhi(impl_->data_ptr_.device().type());
return phi::AllocationType::CPU;
}
// Get the device/place
phi::Place device() const {
if (!impl_) return phi::Place();
if (impl_->data_allocation_) return impl_->data_allocation_->place();
return impl_->place_;
}
// Returns the number of c10::Storage handles currently sharing this
// StorageImpl (i.e. impl_.use_count()), matching PyTorch's
// c10::Storage::use_count() semantics. Returns 0 for empty / invalid
// storage (neither allocation nor data_ptr set).
size_t use_count() const {
if (!valid()) return 0;
size_t count = impl_.use_count();
if (!impl_->tensor_holder_.expired() && count > 0) {
--count;
}
return count;
}
// Check if this storage is unique (use_count == 1)
bool unique() const { return use_count() == 1; }
// Check if this storage is an alias of another
bool is_alias_of(const Storage& other) const {
if (!valid() || !other.valid()) {
return false;
}
// Fast path: same StorageImpl (e.g. two copies of the same Storage handle)
if (impl_ == other.impl_) return true;
return isSharedStorageAlias(*this, other);
}
// Set data pointer (swap and return old) - LibTorch compatible DataPtr
// version. Clears allocation-backed state since the new DataPtr manages its
// own lifecycle. The change is propagated to all Storage copies that share
// this StorageImpl. Use set_data_ptr(shared_ptr<phi::Allocation>) for
// Paddle paths.
DataPtr set_data_ptr(DataPtr&& new_data_ptr) {
DataPtr old = std::move(impl_->data_ptr_);
syncFromDataPtr(std::move(new_data_ptr), impl_->nbytes_);
return old;
}
DataPtr set_data_ptr(std::nullptr_t) = delete;
// Set data pointer (no swap) - LibTorch compatible DataPtr version.
// Propagated to all Storage copies that share this StorageImpl.
void set_data_ptr_noswap(DataPtr&& new_data_ptr) {
syncFromDataPtr(std::move(new_data_ptr), impl_->nbytes_);
}
void set_data_ptr_noswap(std::nullptr_t) = delete;
// Set data pointer - Paddle-specific shared_ptr<phi::Allocation> version.
// Propagated to all Storage copies that share this StorageImpl.
std::shared_ptr<phi::Allocation> set_data_ptr(
std::shared_ptr<phi::Allocation> new_alloc) {
std::shared_ptr<phi::Allocation> old_alloc =
std::move(impl_->data_allocation_);
syncFromAllocation(std::move(new_alloc));
return old_alloc;
}
// Set data pointer (no swap) - Paddle-specific shared_ptr version.
// Propagated to all Storage copies that share this StorageImpl.
void set_data_ptr_noswap(std::shared_ptr<phi::Allocation> new_alloc) {
syncFromAllocation(std::move(new_alloc));
}
private:
// Shared implementation state. All Storage copies that were created by
// copying this Storage share the same StorageImpl, so writes through any
// handle are immediately visible through all other handles.
std::shared_ptr<StorageImpl> impl_;
// Update allocation-backed storage state. The tensor holder stays attached
// to the shared StorageImpl, so tensors observe pointer changes without
// extra registry state.
void syncFromAllocation(std::shared_ptr<phi::Allocation> new_alloc) {
impl_->data_allocation_ = std::move(new_alloc);
if (impl_->data_allocation_) {
impl_->nbytes_ = impl_->data_allocation_->size();
impl_->place_ = impl_->data_allocation_->place();
} else {
impl_->nbytes_ = 0;
impl_->place_ = phi::Place();
}
impl_->data_ptr_ = viewDataPtrFrom(impl_->data_allocation_);
}
void syncFromDataPtr(DataPtr&& new_data_ptr, size_t size_bytes) {
impl_->data_allocation_ = nullptr;
impl_->nbytes_ = size_bytes;
impl_->place_ =
new_data_ptr ? new_data_ptr.device()._PD_GetInner() : phi::Place();
impl_->data_ptr_ = std::move(new_data_ptr);
}
// Create a non-owning DataPtr view of a phi::Allocation.
// The allocation's lifetime is managed by impl_->data_allocation_.
// No deleter is installed so the DataPtr holds only a raw pointer.
static DataPtr viewDataPtrFrom(
const std::shared_ptr<phi::Allocation>& alloc) {
if (!alloc) return DataPtr();
return DataPtr(alloc->ptr(), c10::Device(alloc->place()));
}
};
// Implementation of isSharedStorageAlias
inline bool isSharedStorageAlias(const Storage& storage0,
const Storage& storage1) {
if (!storage0.valid() || !storage1.valid()) {
return false;
}
c10::DeleterFnPtr deleter0 = storage0.data_ptr().get_deleter();
c10::DeleterFnPtr deleter1 = storage1.data_ptr().get_deleter();
if (deleter0 == nullptr || deleter1 == nullptr || deleter0 != deleter1) {
return false;
}
void* context0 = storage0.data_ptr().get_context();
void* context1 = storage1.data_ptr().get_context();
return context0 != nullptr && context0 == context1;
}
// Template specialization for MaybeOwnedTraits<c10::Storage>
template <typename T>
struct MaybeOwnedTraits;
template <>
struct MaybeOwnedTraits<c10::Storage> {
using owned_type = c10::Storage;
using borrow_type = c10::Storage;
static borrow_type createBorrow(const owned_type& from) {
return borrow_type(borrow_type::unsafe_borrow_t{}, from);
}
static void assignBorrow(borrow_type& lhs, // NOLINT(runtime/references)
const borrow_type& rhs) {
lhs = borrow_type(borrow_type::unsafe_borrow_t{}, rhs);
}
// NOLINTNEXTLINE(runtime/references)
static void destroyBorrow(borrow_type& toDestroy) { toDestroy = Storage(); }
static const owned_type& referenceFromBorrow(const borrow_type& borrow) {
return borrow;
}
static const owned_type* pointerFromBorrow(const borrow_type& borrow) {
return &borrow;
}
static bool debugBorrowIsValid(const borrow_type& /*borrow*/) { return true; }
};
// Template specialization for ExclusivelyOwnedTraits<c10::Storage>
template <typename T>
struct ExclusivelyOwnedTraits;
template <>
struct ExclusivelyOwnedTraits<c10::Storage> {
using repr_type = c10::Storage;
using pointer_type = c10::Storage*;
using const_pointer_type = const c10::Storage*;
static repr_type nullRepr() { return c10::Storage(); }
template <class... Args>
static repr_type createInPlace(Args&&... args) {
return c10::Storage(std::forward<Args>(args)...);
}
static repr_type moveToRepr(c10::Storage&& x) { return std::move(x); }
static c10::Storage take(c10::Storage& x) { // NOLINT(runtime/references)
return std::move(x);
}
static pointer_type getImpl(repr_type& x) { // NOLINT(runtime/references)
return &x;
}
static const_pointer_type getImpl(const repr_type& x) { return &x; }
};
} // namespace c10
@@ -0,0 +1,96 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#include <c10/core/Stream.h>
#ifdef PADDLE_WITH_HIP
#include <hip/hip_runtime.h>
#elif defined(PADDLE_WITH_CUDA)
#include <cuda_runtime.h>
#endif
#include "paddle/common/enforce.h"
namespace c10 {
// id_ encodes the raw platform stream handle via reinterpret_cast<StreamId>.
// A zero id_ corresponds to the null (default) stream on any backend.
// native_handle() reverses that cast to expose the underlying platform handle.
void* Stream::native_handle() const {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (device_type() == DeviceType::CUDA) {
return reinterpret_cast<void*>(static_cast<intptr_t>(id_));
}
#endif
#if defined(PADDLE_WITH_XPU)
if (device_type() == DeviceType::XPU) {
return reinterpret_cast<void*>(static_cast<intptr_t>(id_));
}
#endif
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
if (device_type() == DeviceType::CUSTOM) {
return reinterpret_cast<void*>(static_cast<intptr_t>(id_));
}
#endif
// Match PyTorch error message format for unsupported device types
PD_CHECK(false,
"native_handle() is not supported for this device type (",
static_cast<int>(device_type()),
")");
}
bool Stream::query() const {
#if defined(PADDLE_WITH_HIP)
if (device_type() == DeviceType::CUDA) {
hipStream_t s = reinterpret_cast<hipStream_t>(static_cast<intptr_t>(id_));
hipError_t err = hipStreamQuery(s);
if (err == hipSuccess) return true;
if (err == hipErrorNotReady) return false;
PADDLE_ENFORCE_GPU_SUCCESS(err);
}
#elif defined(PADDLE_WITH_CUDA)
if (device_type() == DeviceType::CUDA) {
cudaStream_t s = reinterpret_cast<cudaStream_t>(static_cast<intptr_t>(id_));
cudaError_t err = cudaStreamQuery(s);
if (err == cudaSuccess) return true;
if (err == cudaErrorNotReady) return false;
PADDLE_ENFORCE_GPU_SUCCESS(err);
}
#endif
// CPU streams are always ready.
return true;
}
void Stream::synchronize() const {
#if defined(PADDLE_WITH_HIP)
if (device_type() == DeviceType::CUDA) {
hipStream_t s = reinterpret_cast<hipStream_t>(static_cast<intptr_t>(id_));
PADDLE_ENFORCE_GPU_SUCCESS(hipStreamSynchronize(s));
return;
}
#elif defined(PADDLE_WITH_CUDA)
if (device_type() == DeviceType::CUDA) {
cudaStream_t s = reinterpret_cast<cudaStream_t>(static_cast<intptr_t>(id_));
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamSynchronize(s));
return;
}
#endif
// CPU streams: nothing to synchronize.
}
} // namespace c10
@@ -0,0 +1,113 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/Device.h>
#include <c10/core/DeviceType.h>
#include <c10/util/Exception.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <ostream>
#include "paddle/common/macros.h"
namespace c10 {
using StreamId = int64_t;
struct StreamData3 {
StreamId stream_id;
DeviceIndex device_index;
DeviceType device_type;
};
class PADDLE_API Stream final {
private:
Device device_;
StreamId id_;
public:
enum Unsafe { UNSAFE };
enum Default { DEFAULT };
explicit Stream(Unsafe /*unused*/, Device device, StreamId id)
: device_(device), id_(id) {}
explicit Stream(Default /*unused*/, Device device)
: device_(device), id_(0) {}
bool operator==(const Stream& other) const noexcept {
return this->device_ == other.device_ && this->id_ == other.id_;
}
bool operator!=(const Stream& other) const noexcept {
return !(*this == other);
}
Device device() const noexcept { return device_; }
DeviceType device_type() const noexcept { return device_.type(); }
DeviceIndex device_index() const noexcept { return device_.index(); }
StreamId id() const noexcept { return id_; }
void* native_handle() const;
template <typename T>
void wait(const T& event) const {
event.block(*this);
}
bool query() const;
void synchronize() const;
uint64_t hash() const noexcept {
uint64_t bits = static_cast<uint64_t>(device_type()) << 56 |
static_cast<uint64_t>(device_index()) << 48 |
(static_cast<uint64_t>(id()) & ((1ull << 48) - 1));
return bits;
}
struct StreamData3 pack3() const {
return {id(), device_index(), device_type()};
}
static Stream unpack3(StreamId stream_id,
DeviceIndex device_index,
DeviceType device_type) {
PD_CHECK(isValidDeviceType(device_type));
return Stream(UNSAFE, Device(device_type, device_index), stream_id);
}
};
inline std::ostream& operator<<(std::ostream& os, const Stream& s) {
// Format: "stream {id} on device {device_type}:{device_index}"
os << "stream " << s.id() << " on device " << s.device();
return os;
}
} // namespace c10
namespace std {
template <>
struct hash<c10::Stream> {
size_t operator()(const c10::Stream& s) const noexcept {
return std::hash<uint64_t>{}(s.hash());
}
};
} // namespace std
namespace at {
using c10::Stream;
}
@@ -0,0 +1,40 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/util/accumulate.h>
#include <cstdint>
#include <optional>
namespace c10 {
class SymInt {
public:
SymInt() : data_(0) {}
/*implicit*/ SymInt(int64_t d) : data_(d) {} // NOLINT
/*implicit*/ operator int64_t() const { return data_; }
int64_t guard_int(const char* file, int64_t line) const {
(void)file;
(void)line;
return data_;
}
std::optional<int64_t> maybe_as_int() const { return data_; }
private:
int64_t data_;
};
} // namespace c10
@@ -0,0 +1,29 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/SymInt.h>
#include <c10/util/ArrayRef.h>
namespace c10 {
using SymIntArrayRef = ArrayRef<SymInt>;
} // namespace c10
namespace at {
using c10::SymIntArrayRef;
} // namespace at
namespace torch {
using c10::SymIntArrayRef;
} // namespace torch
@@ -0,0 +1,26 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
namespace c10 {
using SymFloat = double;
} // namespace c10
namespace at {
using c10::SymFloat;
} // namespace at
namespace torch {
using c10::SymFloat;
} // namespace torch
@@ -0,0 +1,375 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/DefaultDtype.h>
#include <c10/core/Device.h>
#include <c10/core/Layout.h>
#include <c10/core/MemoryFormat.h>
#include <c10/core/ScalarType.h>
#include <c10/core/ScalarTypeToTypeMeta.h>
#include <c10/util/typeid.h>
#include <optional>
#include "paddle/common/macros.h"
#include "paddle/phi/common/place.h"
namespace c10 {
inline Layout layout_or_default(std::optional<Layout> layout) {
return layout.value_or(kStrided);
}
inline Device device_or_default(std::optional<Device> device) {
return device.value_or(Device(kCPU));
}
inline caffe2::TypeMeta dtype_or_default(
std::optional<caffe2::TypeMeta> dtype) {
return dtype.value_or(get_default_dtype());
}
// legacy overload
inline ScalarType dtype_or_default(std::optional<ScalarType> dtype) {
return dtype.value_or(get_default_dtype_as_scalartype());
}
inline bool pinned_memory_or_default(std::optional<bool> pinned_memory) {
return pinned_memory.value_or(false);
}
struct PADDLE_API TensorOptions {
TensorOptions()
: requires_grad_(false),
pinned_memory_(false),
has_device_(false),
has_dtype_(false),
has_layout_(false),
has_requires_grad_(false),
has_pinned_memory_(false),
has_memory_format_(false) {}
/* implicit */ explicit TensorOptions(Layout layout) // NOLINT
: TensorOptions() {
this->set_layout(layout);
}
template <
typename T,
typename = std::enable_if_t<std::is_same_v<std::decay_t<T>, Device>>>
/* implicit */ explicit TensorOptions(T&& device) // NOLINT
: TensorOptions() {
this->set_device(std::forward<T>(device));
}
template <
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<Device, Args&&...>>>
/* implicit */ TensorOptions(Args&&... args) // NOLINT
: TensorOptions(Device(std::forward<Args>(args)...)) {}
/* implicit */ TensorOptions(caffe2::TypeMeta dtype) // NOLINT
: TensorOptions() {
this->set_dtype(dtype);
}
// legacy constructor to support ScalarType
/* implicit */ TensorOptions(c10::ScalarType dtype) // NOLINT
: TensorOptions() {
this->set_dtype(dtype);
}
/* implicit */ TensorOptions(MemoryFormat memory_format) // NOLINT
: TensorOptions() {
set_memory_format(memory_format);
}
[[nodiscard]] TensorOptions device(
std::optional<Device> device) const noexcept {
TensorOptions r = *this;
r.set_device(device);
return r;
}
template <typename... Args>
[[nodiscard]] TensorOptions device(Args&&... args) const noexcept {
return device(
std::optional<Device>(std::in_place, std::forward<Args>(args)...));
}
[[nodiscard]] TensorOptions device_index(
c10::DeviceIndex device_index) const noexcept {
return device(Device(kCUDA, device_index));
}
[[nodiscard]] TensorOptions dtype(
std::optional<caffe2::TypeMeta> dtype) const noexcept {
TensorOptions r = *this;
r.set_dtype(dtype);
return r;
}
// legacy overload to support ScalarType
[[nodiscard]] TensorOptions dtype(
std::optional<ScalarType> dtype) const noexcept {
TensorOptions r = *this;
r.set_dtype(dtype);
return r;
}
template <typename T>
TensorOptions& dtype() {
dtype_ = caffe2::TypeMeta::Make<T>();
has_dtype_ = true;
return *this;
}
[[nodiscard]] TensorOptions layout(
std::optional<Layout> layout) const noexcept {
TensorOptions r = *this;
r.set_layout(layout);
return r;
}
[[nodiscard]] TensorOptions requires_grad(
std::optional<bool> requires_grad) const noexcept {
TensorOptions r = *this;
r.set_requires_grad(requires_grad);
return r;
}
[[nodiscard]] TensorOptions pinned_memory(
std::optional<bool> pinned_memory) const noexcept {
TensorOptions r = *this;
r.set_pinned_memory(pinned_memory);
return r;
}
[[nodiscard]] TensorOptions memory_format(
std::optional<MemoryFormat> memory_format) const noexcept {
TensorOptions r = *this;
r.set_memory_format(memory_format);
return r;
}
Device device() const noexcept { return device_or_default(device_opt()); }
bool has_device() const noexcept { return has_device_; }
std::optional<Device> device_opt() const noexcept {
return has_device_ ? std::make_optional(device_) : std::nullopt;
}
c10::DeviceIndex device_index() const noexcept { return device().index(); }
caffe2::TypeMeta dtype() const noexcept {
return dtype_or_default(dtype_opt());
}
bool has_dtype() const noexcept { return has_dtype_; }
std::optional<caffe2::TypeMeta> dtype_opt() const noexcept {
return has_dtype_ ? std::make_optional(dtype_) : std::nullopt;
}
Layout layout() const noexcept { return layout_or_default(layout_opt()); }
bool has_layout() const noexcept { return has_layout_; }
std::optional<Layout> layout_opt() const noexcept {
return has_layout_ ? std::make_optional(layout_) : std::nullopt;
}
bool requires_grad() const noexcept {
return has_requires_grad_ ? requires_grad_ : false;
}
bool has_requires_grad() const noexcept { return has_requires_grad_; }
std::optional<bool> requires_grad_opt() const noexcept {
return has_requires_grad_ ? std::make_optional(requires_grad_)
: std::nullopt;
}
bool pinned_memory() const noexcept {
return pinned_memory_or_default(pinned_memory_opt());
}
bool has_pinned_memory() const noexcept { return has_pinned_memory_; }
bool is_sparse() const { return layout_ == c10::Layout::Sparse; }
bool is_sparse_csr() const { return layout_ == c10::Layout::SparseCsr; }
bool is_sparse_compressed() const {
return layout_ == c10::Layout::SparseCsr ||
layout_ == c10::Layout::SparseCsc ||
layout_ == c10::Layout::SparseBsr ||
layout_ == c10::Layout::SparseBsc;
}
std::optional<bool> pinned_memory_opt() const noexcept {
return has_pinned_memory_ ? std::make_optional(pinned_memory_)
: std::nullopt;
}
bool has_memory_format() const noexcept { return has_memory_format_; }
std::optional<MemoryFormat> memory_format_opt() const noexcept {
return has_memory_format_ ? std::make_optional(memory_format_)
: std::nullopt;
}
TensorOptions merge_memory_format(
std::optional<MemoryFormat> optional_memory_format) const noexcept {
TensorOptions merged = *this;
if (optional_memory_format.has_value()) {
merged.set_memory_format(optional_memory_format);
}
return merged;
}
::phi::Place _PD_GetPlace() const { return device_._PD_GetInner(); }
private:
void set_device(std::optional<Device> device) & noexcept {
if (device) {
device_ = *device;
has_device_ = true;
} else {
has_device_ = false;
}
}
void set_dtype(std::optional<caffe2::TypeMeta> dtype) & noexcept {
if (dtype) {
dtype_ = *dtype;
has_dtype_ = true;
} else {
has_dtype_ = false;
}
}
// legacy overload to support ScalarType
void set_dtype(std::optional<ScalarType> dtype) & noexcept {
if (dtype) {
dtype_ = scalarTypeToTypeMeta(*dtype);
has_dtype_ = true;
} else {
has_dtype_ = false;
}
}
void set_layout(std::optional<Layout> layout) & noexcept {
if (layout) {
layout_ = *layout;
has_layout_ = true;
} else {
has_layout_ = false;
}
}
void set_requires_grad(std::optional<bool> requires_grad) & noexcept {
if (requires_grad) {
requires_grad_ = *requires_grad;
has_requires_grad_ = true;
} else {
has_requires_grad_ = false;
}
}
void set_pinned_memory(std::optional<bool> pinned_memory) & noexcept {
if (pinned_memory) {
pinned_memory_ = *pinned_memory;
has_pinned_memory_ = true;
} else {
has_pinned_memory_ = false;
}
}
void set_memory_format(std::optional<MemoryFormat> memory_format) & noexcept {
if (memory_format) {
memory_format_ = *memory_format;
has_memory_format_ = true;
} else {
has_memory_format_ = false;
}
}
Device device_ = c10::kCPU;
caffe2::TypeMeta dtype_ = caffe2::TypeMeta::Make<float>(); // 16-bit
Layout layout_ = at::kStrided; // 8-bit
MemoryFormat memory_format_ = MemoryFormat::Contiguous; // 8-bit
bool requires_grad_ : 1;
bool pinned_memory_ : 1;
bool has_device_ : 1;
bool has_dtype_ : 1;
bool has_layout_ : 1;
bool has_requires_grad_ : 1;
bool has_pinned_memory_ : 1;
bool has_memory_format_ : 1;
};
inline TensorOptions dtype(caffe2::TypeMeta dtype) {
return TensorOptions().dtype(dtype);
}
// legacy overload
inline TensorOptions dtype(ScalarType dtype) {
return TensorOptions().dtype(scalarTypeToTypeMeta(dtype));
}
template <typename T>
inline TensorOptions dtype() {
return dtype(caffe2::TypeMeta::Make<T>());
}
inline TensorOptions layout(Layout layout) {
return TensorOptions().layout(layout);
}
inline TensorOptions device(Device device) {
return TensorOptions().device(device);
}
inline TensorOptions device_index(c10::DeviceIndex device_index) {
return TensorOptions().device_index(device_index);
}
inline TensorOptions requires_grad(bool requires_grad = true) {
return TensorOptions().requires_grad(requires_grad);
}
inline TensorOptions memory_format(MemoryFormat memory_format) {
return TensorOptions().memory_format(memory_format);
}
std::ostream& operator<<(std::ostream& stream, const TensorOptions& options);
inline std::string toString(const TensorOptions& options) {
std::ostringstream stream;
stream << options;
return stream.str();
}
} // namespace c10
namespace at {
using namespace c10; // NOLINT
} // namespace at
@@ -0,0 +1,61 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <exception>
#include <string>
#if defined(PADDLE_WITH_HIP)
#include <hip/hip_runtime.h>
#elif defined(PADDLE_WITH_CUDA)
#include <cuda_runtime.h>
#endif
class CompatException : public std::exception {
private:
std::string message = {};
public:
explicit CompatException(const char* name,
const char* file,
const int line,
const std::string& error) {
message = std::string("Failed: ") + name + " error " + file + ":" +
std::to_string(line) + " '" + error + "'";
}
const char* what() const noexcept override { return message.c_str(); }
};
#ifndef C10_CUDA_CHECK
#if defined(PADDLE_WITH_HIP)
#define C10_CUDA_CHECK(cmd) \
do { \
hipError_t e = (cmd); \
if (e != hipSuccess) { \
throw CompatException("HIP", __FILE__, __LINE__, hipGetErrorString(e)); \
} \
} while (0)
#elif defined(PADDLE_WITH_CUDA)
#define C10_CUDA_CHECK(cmd) \
do { \
cudaError_t e = (cmd); \
if (e != cudaSuccess) { \
throw CompatException( \
"CUDA", __FILE__, __LINE__, cudaGetErrorString(e)); \
} \
} while (0)
#endif
#endif
@@ -0,0 +1,48 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#include <c10/cuda/CUDAFunctions.h>
namespace c10::cuda {
c10::DeviceIndex device_count() {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
return phi::backends::gpu::GetGPUDeviceCount();
#else
// Return 0 instead of throwing to match PyTorch API semantics
// at::cuda::is_available() relies on this returning 0/false
return 0;
#endif
}
void device_synchronize() {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
int curr_device_id = paddle::platform::GetCurrentDeviceId();
paddle::platform::SetDeviceId(curr_device_id);
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(hipDeviceSynchronize());
#else
PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceSynchronize());
#endif
#else
PADDLE_THROW(common::errors::Unavailable(
"Paddle is not compiled with CUDA. Cannot visit device synchronize."));
#endif
}
} // namespace c10::cuda
@@ -0,0 +1,38 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/core/Device.h>
namespace c10::cuda {
PADDLE_API c10::DeviceIndex device_count();
PADDLE_API void device_synchronize();
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
void __inline__ stream_synchronize(gpuStream_t stream) {
phi::backends::gpu::GpuStreamSync(stream);
}
#endif
} // namespace c10::cuda
namespace at::cuda {
using c10::cuda::device_synchronize;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
using c10::cuda::stream_synchronize;
#endif
} // namespace at::cuda
@@ -0,0 +1,169 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/Device.h>
#include <c10/util/Exception.h>
#include <optional>
#include "paddle/phi/backends/gpu/gpu_info.h"
namespace c10::cuda {
namespace detail {
inline Device current_cuda_device() {
return Device(kCUDA, phi::backends::gpu::GetCurrentDeviceId());
}
inline Device normalize_cuda_device(Device device) {
TORCH_CHECK(device.is_cuda(), "Expected a CUDA device, but got ", device);
return device.has_index() ? Device(kCUDA, device.index())
: current_cuda_device();
}
} // namespace detail
struct CUDAGuard {
explicit CUDAGuard() = delete; // NOLINT
explicit CUDAGuard(DeviceIndex device_index)
: original_device_(detail::current_cuda_device()),
current_device_(original_device_) {
set_index(device_index);
}
explicit CUDAGuard(Device device)
: original_device_(detail::current_cuda_device()),
current_device_(original_device_) {
set_device(device);
}
CUDAGuard(const CUDAGuard&) = delete;
CUDAGuard& operator=(const CUDAGuard&) = delete;
CUDAGuard(CUDAGuard&& other) = delete;
CUDAGuard& operator=(CUDAGuard&& other) = delete;
~CUDAGuard() {
// Always restore to original_device_ to handle cases where the device
// was changed outside of this guard, matching PyTorch semantics.
phi::backends::gpu::SetDeviceId(static_cast<int>(original_device_.index()));
}
void set_device(Device device) {
const Device normalized = detail::normalize_cuda_device(device);
if (normalized.index() != current_device_.index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(normalized.index()));
current_device_ = normalized;
}
}
void reset_device(Device device) { set_device(device); }
void set_index(DeviceIndex device_index) {
if (current_device_.index() != device_index) {
phi::backends::gpu::SetDeviceId(static_cast<int>(device_index));
current_device_ = Device(kCUDA, device_index);
}
}
Device original_device() const { return original_device_; }
Device current_device() const { return current_device_; }
private:
Device original_device_;
Device current_device_;
};
struct OptionalCUDAGuard {
OptionalCUDAGuard() = default;
explicit OptionalCUDAGuard(std::optional<Device> device_opt) {
if (device_opt.has_value()) {
set_device(device_opt.value());
}
}
explicit OptionalCUDAGuard(std::optional<DeviceIndex> device_index_opt) {
if (device_index_opt.has_value()) {
set_index(device_index_opt.value());
}
}
OptionalCUDAGuard(const OptionalCUDAGuard&) = delete;
OptionalCUDAGuard& operator=(const OptionalCUDAGuard&) = delete;
OptionalCUDAGuard(OptionalCUDAGuard&& other) = delete;
OptionalCUDAGuard& operator=(OptionalCUDAGuard&& other) = delete;
~OptionalCUDAGuard() { reset(); }
void set_device(Device device) {
const Device normalized = detail::normalize_cuda_device(device);
init_if_needed();
if (normalized.index() != current_device_->index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(normalized.index()));
}
current_device_ = normalized;
}
void reset_device(Device device) { set_device(device); }
void set_index(DeviceIndex device_index) {
init_if_needed();
if (device_index != current_device_->index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(device_index));
}
current_device_ = Device(kCUDA, device_index);
}
std::optional<Device> original_device() const { return original_device_; }
std::optional<Device> current_device() const { return current_device_; }
void reset() {
if (original_device_.has_value()) {
// Always restore to original_device_ to handle external device changes.
// This matches PyTorch OptionalDeviceGuard semantics.
phi::backends::gpu::SetDeviceId(
static_cast<int>(original_device_->index()));
}
original_device_.reset();
current_device_.reset();
}
private:
void init_if_needed() {
if (!original_device_.has_value()) {
original_device_ = detail::current_cuda_device();
current_device_ = original_device_;
}
}
std::optional<Device> original_device_;
std::optional<Device> current_device_;
};
} // namespace c10::cuda
namespace at::cuda {
using c10::cuda::CUDAGuard;
using c10::cuda::OptionalCUDAGuard;
} // namespace at::cuda
@@ -0,0 +1,239 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <c10/cuda/CUDAStream.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <vector>
#include "paddle/phi/api/include/context_pool.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include "paddle/phi/backends/gpu/gpu_info.h"
#endif
namespace c10::cuda {
namespace {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
constexpr int kStreamsPerPool = 32;
std::once_flag g_init_once;
c10::DeviceIndex g_num_gpus = -1;
struct DevicePools {
#ifdef PADDLE_WITH_HIP
std::vector<hipStream_t> low_priority;
std::vector<hipStream_t> high_priority;
#else
std::vector<cudaStream_t> low_priority;
std::vector<cudaStream_t> high_priority;
#endif
std::atomic<uint32_t> lp_counter{0};
std::atomic<uint32_t> hp_counter{0};
std::once_flag init_flag;
};
std::vector<std::unique_ptr<DevicePools>> g_pools;
void initGlobalState() {
std::call_once(g_init_once, []() {
g_num_gpus =
static_cast<c10::DeviceIndex>(phi::backends::gpu::GetGPUDeviceCount());
g_pools.resize(g_num_gpus);
for (auto& ptr : g_pools) {
ptr = std::make_unique<DevicePools>();
}
});
}
void initDevicePools(c10::DeviceIndex device_index) {
phi::backends::gpu::GPUDeviceGuard guard(device_index);
int lo_pri = 0, hi_pri = 0;
#ifdef PADDLE_WITH_HIP
C10_CUDA_CHECK(hipDeviceGetStreamPriorityRange(&lo_pri, &hi_pri));
#else
C10_CUDA_CHECK(cudaDeviceGetStreamPriorityRange(&lo_pri, &hi_pri));
#endif
auto& pool = *g_pools[device_index];
pool.low_priority.resize(kStreamsPerPool);
pool.high_priority.resize(kStreamsPerPool);
for (int i = 0; i < kStreamsPerPool; ++i) {
#ifdef PADDLE_WITH_HIP
C10_CUDA_CHECK(hipStreamCreateWithPriority(
&pool.low_priority[i], hipStreamNonBlocking, lo_pri));
C10_CUDA_CHECK(hipStreamCreateWithPriority(
&pool.high_priority[i], hipStreamNonBlocking, hi_pri));
#else
C10_CUDA_CHECK(cudaStreamCreateWithPriority(
&pool.low_priority[i], cudaStreamNonBlocking, lo_pri));
C10_CUDA_CHECK(cudaStreamCreateWithPriority(
&pool.high_priority[i], cudaStreamNonBlocking, hi_pri));
#endif
}
}
inline void check_gpu(c10::DeviceIndex device_index) {
TORCH_CHECK(device_index >= 0 && device_index < g_num_gpus,
"Device index value ",
static_cast<int>(device_index),
" is out of index range [0, ",
static_cast<int>(g_num_gpus),
")");
}
inline phi::GPUContext* getMutableGPUContext(c10::DeviceIndex device_index) {
return static_cast<phi::GPUContext*>(
paddle::experimental::DeviceContextPool::Instance().GetMutable(
phi::GPUPlace(device_index)));
}
#ifdef PADDLE_WITH_HIP
inline hipStream_t getPaddleCurrentStream(c10::DeviceIndex device_index) {
auto* current_stream =
paddle::GetCurrentCUDAStream(phi::GPUPlace(device_index));
return current_stream == nullptr ? nullptr : current_stream->raw_stream();
}
#else
inline cudaStream_t getPaddleCurrentStream(c10::DeviceIndex device_index) {
auto* current_stream =
paddle::GetCurrentCUDAStream(phi::GPUPlace(device_index));
return current_stream == nullptr ? nullptr : current_stream->raw_stream();
}
#endif
#endif // defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
} // namespace
#ifdef PADDLE_WITH_HIP
inline CUDAStream make_cuda_stream(hipStream_t raw,
c10::DeviceIndex device_index) {
#else
inline CUDAStream make_cuda_stream(cudaStream_t raw,
c10::DeviceIndex device_index) {
#endif
c10::StreamId sid =
static_cast<c10::StreamId>(reinterpret_cast<intptr_t>(raw));
return CUDAStream(
c10::Stream(c10::Stream::UNSAFE,
c10::Device(c10::DeviceType::CUDA, device_index),
sid));
}
CUDAStream getStreamFromPool(const int priority,
c10::DeviceIndex device_index) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
if (device_index == -1) {
device_index =
static_cast<c10::DeviceIndex>(phi::backends::gpu::GetCurrentDeviceId());
}
check_gpu(device_index);
std::call_once(
g_pools[device_index]->init_flag, initDevicePools, device_index);
const uint32_t idx = (priority < 0 ? g_pools[device_index]->hp_counter++
: g_pools[device_index]->lp_counter++) %
kStreamsPerPool;
#ifdef PADDLE_WITH_HIP
hipStream_t raw = (priority < 0 ? g_pools[device_index]->high_priority[idx]
: g_pools[device_index]->low_priority[idx]);
#else
cudaStream_t raw = (priority < 0 ? g_pools[device_index]->high_priority[idx]
: g_pools[device_index]->low_priority[idx]);
#endif
return make_cuda_stream(raw, device_index);
#else
TORCH_CHECK(false, "getStreamFromPool is not supported without CUDA/HIP");
return getDefaultCUDAStream(device_index);
#endif
}
CUDAStream getStreamFromPool(const bool isHighPriority,
c10::DeviceIndex device_index) {
return getStreamFromPool(isHighPriority ? -1 : 0, device_index);
}
#ifdef PADDLE_WITH_HIP
CUDAStream getStreamFromExternal(hipStream_t ext_stream,
c10::DeviceIndex device_index) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
check_gpu(device_index);
#endif
return make_cuda_stream(ext_stream, device_index);
}
#else
CUDAStream getStreamFromExternal(cudaStream_t ext_stream,
c10::DeviceIndex device_index) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
check_gpu(device_index);
#endif
return make_cuda_stream(ext_stream, device_index);
}
#endif
CUDAStream getDefaultCUDAStream(c10::DeviceIndex device_index) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
if (device_index == -1) {
device_index =
static_cast<c10::DeviceIndex>(phi::backends::gpu::GetCurrentDeviceId());
}
check_gpu(device_index);
#endif
return CUDAStream(c10::Stream(
c10::Stream::DEFAULT, c10::Device(c10::DeviceType::CUDA, device_index)));
}
CUDAStream getCurrentCUDAStream(c10::DeviceIndex device_index) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
if (device_index == -1) {
device_index =
static_cast<c10::DeviceIndex>(phi::backends::gpu::GetCurrentDeviceId());
}
check_gpu(device_index);
auto raw = getPaddleCurrentStream(device_index);
if (raw == nullptr) {
return getDefaultCUDAStream(device_index);
}
return make_cuda_stream(raw, device_index);
#else
return getDefaultCUDAStream(device_index);
#endif
}
void setCurrentCUDAStream(CUDAStream stream) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
initGlobalState();
c10::DeviceIndex idx = stream.unwrap().device_index();
check_gpu(idx);
getMutableGPUContext(idx)->SetStream(stream.stream());
#else
(void)stream;
#endif
}
} // namespace c10::cuda
@@ -0,0 +1,198 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/Device.h>
#include <c10/core/Stream.h>
#include <c10/cuda/CUDAException.h>
#include <ostream>
#include "paddle/common/macros.h"
#include "paddle/phi/backends/gpu/gpu_info.h"
#include "paddle/phi/common/place.h"
namespace c10::cuda {
using StreamId = int64_t;
static constexpr int max_compile_time_stream_priorities = 4;
class CUDAStream {
public:
enum Unchecked { UNCHECKED };
CUDAStream() = delete;
explicit CUDAStream(Stream stream) : stream_(stream) {
TORCH_CHECK(stream_.device_type() == DeviceType::CUDA);
}
explicit CUDAStream(Unchecked /*unused*/, Stream stream) : stream_(stream) {}
bool operator==(const CUDAStream& other) const noexcept {
return unwrap() == other.unwrap();
}
bool operator!=(const CUDAStream& other) const noexcept {
return unwrap() != other.unwrap();
}
StreamId id() const { return stream_.id(); }
#ifdef PADDLE_WITH_HIP
operator hipStream_t() const { return stream(); }
#else
operator cudaStream_t() const { return stream(); }
#endif
operator Stream() const { return unwrap(); }
bool query() const { return unwrap().query(); }
void synchronize() const { unwrap().synchronize(); }
int priority() const {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
phi::backends::gpu::GPUDeviceGuard guard(device_index());
int priority = 0;
#ifdef PADDLE_WITH_HIP
C10_CUDA_CHECK(hipStreamGetPriority(stream(), &priority));
#else
C10_CUDA_CHECK(cudaStreamGetPriority(stream(), &priority));
#endif
return priority;
#else
return 0;
#endif
}
#ifdef PADDLE_WITH_HIP
hipStream_t stream() const {
return reinterpret_cast<hipStream_t>(stream_.id());
}
#else
cudaStream_t stream() const {
return reinterpret_cast<cudaStream_t>(stream_.id());
}
#endif
Stream unwrap() const { return stream_; }
DeviceType device_type() const { return DeviceType::CUDA; }
DeviceIndex device_index() const { return stream_.device_index(); }
Device device() const { return Device(DeviceType::CUDA, device_index()); }
struct c10::StreamData3 pack3() const {
return stream_.pack3();
}
static CUDAStream unpack3(StreamId stream_id,
DeviceIndex device_index,
DeviceType device_type) {
return CUDAStream(Stream::unpack3(stream_id, device_index, device_type));
}
static std::tuple<int, int> priority_range() {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
int least_priority = 0;
int greatest_priority = 0;
#ifdef PADDLE_WITH_HIP
C10_CUDA_CHECK(
hipDeviceGetStreamPriorityRange(&least_priority, &greatest_priority));
#else
C10_CUDA_CHECK(
cudaDeviceGetStreamPriorityRange(&least_priority, &greatest_priority));
#endif
greatest_priority =
std::max(-max_compile_time_stream_priorities + 1, greatest_priority);
return std::make_tuple(least_priority, greatest_priority);
#else
return std::make_tuple(0, 0);
#endif
}
private:
Stream stream_;
};
/**
* Get the current CUDA stream for the passed CUDA device, or for the
* current device if no device index is passed.
*/
PADDLE_API CUDAStream getCurrentCUDAStream(c10::DeviceIndex device_index = -1);
/**
* Get a new stream from the CUDA stream pool.
* Priority -1 is high priority, 0 is default/low priority.
* Matches PyTorch behavior where negative priority = high priority.
*/
PADDLE_API CUDAStream getStreamFromPool(const int priority = 0,
c10::DeviceIndex device_index = -1);
/**
* Get a new stream from the CUDA stream pool.
* Bool overload: true = high priority (-1), false = default priority (0).
*/
PADDLE_API CUDAStream getStreamFromPool(const bool isHighPriority,
c10::DeviceIndex device_index = -1);
#ifdef PADDLE_WITH_HIP
PADDLE_API CUDAStream getStreamFromExternal(hipStream_t ext_stream,
c10::DeviceIndex device_index);
#else
PADDLE_API CUDAStream getStreamFromExternal(cudaStream_t ext_stream,
c10::DeviceIndex device_index);
#endif
/**
* Set the current CUDA stream for the device of the given stream.
*
* Keeps the compat c10 stream state aligned with Paddle's GPUContext so
* Paddle stream guards and c10 callers observe the same current stream.
*/
PADDLE_API void setCurrentCUDAStream(CUDAStream stream);
PADDLE_API CUDAStream getDefaultCUDAStream(c10::DeviceIndex device_index = -1);
inline std::ostream& operator<<(std::ostream& stream, const CUDAStream& s) {
return stream << s.unwrap();
}
} // namespace c10::cuda
namespace std {
template <>
struct hash<c10::cuda::CUDAStream> {
size_t operator()(c10::cuda::CUDAStream s) const noexcept {
return std::hash<c10::Stream>{}(s.unwrap());
}
};
} // namespace std
namespace at::cuda {
using c10::cuda::CUDAStream;
using c10::cuda::getCurrentCUDAStream;
using c10::cuda::getDefaultCUDAStream;
using c10::cuda::getStreamFromExternal;
using c10::cuda::getStreamFromPool;
using c10::cuda::setCurrentCUDAStream;
} // namespace at::cuda
@@ -0,0 +1,20 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
// Placeholder header to satisfy PyTorch compatibility checks.
// Paddle does not use the same CUDA cmake macros as PyTorch,
// but the presence of this file allows downstream code to use
// __has_include(<c10/cuda/impl/cuda_cmake_macros.h>) for feature detection.
@@ -0,0 +1,46 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#define C10_CONCATENATE_IMPL(s1, s2) s1##s2
#define C10_CONCATENATE(s1, s2) C10_CONCATENATE_IMPL(s1, s2)
#define C10_MACRO_EXPAND(args) args
#define C10_STRINGIZE_IMPL(x) #x
#define C10_STRINGIZE(x) C10_STRINGIZE_IMPL(x)
#ifdef __COUNTER__
#define C10_UID __COUNTER__
#define C10_ANONYMOUS_VARIABLE(str) C10_CONCATENATE(str, __COUNTER__)
#else
#define C10_UID __LINE__
#define C10_ANONYMOUS_VARIABLE(str) C10_CONCATENATE(str, __LINE__)
#endif
#if defined(__CUDACC__) || defined(__HIPCC__)
// Designates functions callable from the host (CPU) and the device (GPU)
#define C10_HOST_DEVICE __host__ __device__
#define C10_DEVICE __device__
#define C10_HOST __host__
#else
#define C10_HOST_DEVICE
#define C10_HOST
#define C10_DEVICE
#endif
@@ -0,0 +1,213 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/util/Exception.h>
#include <cstdint>
#include <functional>
#include <iterator>
#include <vector>
#include "paddle/phi/common/int_array.h"
namespace c10 {
#define TORCH_CHECK_CONSTEXPR(COND, MSG) \
((COND) ? void(0) : throw std::runtime_error(MSG))
template <typename T>
class ArrayRef {
private:
/// The start of the array, in an external buffer.
const T* Data;
/// The number of elements.
size_t Length;
public:
using iterator = const T*;
using const_iterator = const T*;
using size_type = size_t;
using value_type = T;
using reverse_iterator = std::reverse_iterator<iterator>;
/* implicit */ constexpr ArrayRef() : Data(nullptr), Length(0) {}
constexpr ArrayRef(const T& OneElt) : Data(&OneElt), Length(1) {} // NOLINT
constexpr ArrayRef(const T* data, size_t length)
: Data(data), Length(length) {}
constexpr ArrayRef(const T* begin, const T* end)
: Data(begin), Length(end - begin) {}
template <typename Container,
typename U = decltype(std::declval<Container>().data()),
typename = std::enable_if_t<(std::is_same_v<U, T*> ||
std::is_same_v<U, T const*>)>>
/* implicit */ ArrayRef(const Container& container) // NOLINT
: Data(container.data()), Length(container.size()) {}
template <typename A>
/* implicit */ ArrayRef(const std::vector<T, A>& Vec) // NOLINT
: Data(Vec.data()), Length(Vec.size()) {
static_assert(!std::is_same_v<T, bool>,
"ArrayRef<bool> cannot be constructed from a "
"std::vector<bool> bitfield.");
}
template <size_t N>
/* implicit */ constexpr ArrayRef(const std::array<T, N>& Arr) // NOLINT
: Data(Arr.data()), Length(N) {}
template <size_t N>
/* implicit */ constexpr ArrayRef(const T (&Arr)[N]) // NOLINT
: Data(Arr), Length(N) {}
/* implicit */ constexpr ArrayRef(const std::initializer_list<T>& Vec)
: Data(std::begin(Vec) == std::end(Vec) ? static_cast<T*>(nullptr)
: std::begin(Vec)),
Length(Vec.size()) {}
constexpr iterator begin() const { return Data; }
constexpr iterator end() const { return Data + Length; }
constexpr const_iterator cbegin() const { return Data; }
constexpr const_iterator cend() const { return Data + Length; }
constexpr reverse_iterator rbegin() const { return reverse_iterator(end()); }
constexpr reverse_iterator rend() const { return reverse_iterator(begin()); }
constexpr bool allMatch(const std::function<bool(const T&)>& pred) const {
return std::all_of(cbegin(), cend(), pred);
}
constexpr bool empty() const { return Length == 0; }
constexpr const T* data() const { return Data; }
constexpr size_t size() const { return Length; }
constexpr const T& front() const {
TORCH_CHECK_CONSTEXPR(
!empty(), "ArrayRef: attempted to access front() of empty list");
return Data[0];
}
constexpr const T& back() const {
TORCH_CHECK_CONSTEXPR(!empty(),
"ArrayRef: attempted to access back() of empty list");
return Data[Length - 1];
}
constexpr bool equals(ArrayRef RHS) const {
return Length == RHS.Length && std::equal(begin(), end(), RHS.begin());
}
/// slice(n, m) - Take M elements of the array starting at element N
constexpr ArrayRef<T> slice(size_t N, size_t M) const {
TORCH_CHECK_CONSTEXPR(N + M <= size(), "ArrayRef: invalid slice");
return ArrayRef<T>(data() + N, M);
}
/// slice(n) - Chop off the first N elements of the array.
constexpr ArrayRef<T> slice(size_t N) const {
TORCH_CHECK_CONSTEXPR(N <= size(), "ArrayRef: invalid slice");
return slice(N, size() - N);
}
constexpr const T& operator[](size_t Index) const { return Data[Index]; }
/// Vector compatibility
constexpr const T& at(size_t Index) const {
TORCH_CHECK_CONSTEXPR(Index < Length, "ArrayRef: invalid index");
return Data[Index];
}
template <typename U>
std::enable_if_t<std::is_same_v<U, T>, ArrayRef<T>>& operator=(
U&& Temporary) = delete;
template <typename U>
std::enable_if_t<std::is_same_v<U, T>, ArrayRef<T>>& operator=(
std::initializer_list<U>) = delete;
std::vector<T> vec() const { return std::vector<T>(Data, Data + Length); }
const paddle::experimental::IntArray _PD_ToPaddleIntArray() const {
return paddle::experimental::IntArray(
reinterpret_cast<const int64_t*>(Data), Length);
}
};
template <typename T>
std::ostream& operator<<(std::ostream& out, ArrayRef<T> list) {
int i = 0;
out << "[";
for (const auto& e : list) {
if (i++ > 0) out << ", ";
out << e;
}
out << "]";
return out;
}
template <typename T>
bool operator==(c10::ArrayRef<T> a1, c10::ArrayRef<T> a2) {
return a1.equals(a2);
}
template <typename T>
bool operator!=(c10::ArrayRef<T> a1, c10::ArrayRef<T> a2) {
return !a1.equals(a2);
}
template <typename T>
bool operator==(const std::vector<T>& a1, c10::ArrayRef<T> a2) {
return c10::ArrayRef<T>(a1).equals(a2);
}
template <typename T>
bool operator!=(const std::vector<T>& a1, c10::ArrayRef<T> a2) {
return !c10::ArrayRef<T>(a1).equals(a2);
}
template <typename T>
bool operator==(c10::ArrayRef<T> a1, const std::vector<T>& a2) {
return a1.equals(c10::ArrayRef<T>(a2));
}
template <typename T>
bool operator!=(c10::ArrayRef<T> a1, const std::vector<T>& a2) {
return !a1.equals(c10::ArrayRef<T>(a2));
}
using IntArrayRef = ArrayRef<int64_t>;
} // namespace c10
namespace at {
using c10::ArrayRef;
using c10::IntArrayRef;
} // namespace at
namespace torch {
using c10::ArrayRef;
using c10::IntArrayRef;
} // namespace torch
@@ -0,0 +1,29 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/bfloat16.h"
namespace c10 {
using BFloat16 = ::phi::dtype::bfloat16;
} // namespace c10
namespace at {
using c10::BFloat16;
} // namespace at
namespace torch {
using c10::BFloat16;
} // namespace torch
@@ -0,0 +1,170 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/macros/Macros.h>
#include <torch/headeronly/util/Exception.h>
#include <cstdint>
#include <exception>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <variant>
#include <vector>
#include "paddle/common/enforce.h"
#include "paddle/common/errors.h"
#include "paddle/common/exception.h"
#include "paddle/common/macros.h"
namespace c10 {
#define TORCH_CHECK(COND, ...) PD_CHECK(COND, ##__VA_ARGS__);
#define TORCH_INTERNAL_ASSERT(COND, ...) PD_CHECK(COND, ##__VA_ARGS__);
#define TORCH_CHECK_OP(val1, val2, op) \
do { \
auto&& _val1 = (val1); \
auto&& _val2 = (val2); \
if (!(_val1 op _val2)) { \
std::ostringstream _result; \
_result << "Check failed: " #val1 " " #op " " #val2 " (" << _val1 \
<< " vs. " << _val2 << "). "; \
PD_THROW(_result.str()); \
} \
} while (false);
// Check for a given boolean condition.
#ifndef CHECK
#define CHECK(condition) PD_CHECK(condition, "CHECK failed : ", #condition)
#endif
// TORCH_CHECK_OP macro definitions
#define TORCH_CHECK_EQ(val1, val2) TORCH_CHECK_OP(val1, val2, ==)
#define TORCH_CHECK_NE(val1, val2) TORCH_CHECK_OP(val1, val2, !=)
#define TORCH_CHECK_LE(val1, val2) TORCH_CHECK_OP(val1, val2, <=)
#define TORCH_CHECK_LT(val1, val2) TORCH_CHECK_OP(val1, val2, <)
#define TORCH_CHECK_GE(val1, val2) TORCH_CHECK_OP(val1, val2, >=)
#define TORCH_CHECK_GT(val1, val2) TORCH_CHECK_OP(val1, val2, >)
} // namespace c10
enum class C10ErrorType {
NotImplementedError,
Error,
};
constexpr auto NotImplementedError = C10ErrorType::NotImplementedError;
constexpr auto Error = C10ErrorType::Error;
inline void C10ThrowImpl(C10ErrorType err_type, const std::string& msg) {
switch (err_type) {
case C10ErrorType::NotImplementedError:
PADDLE_THROW(common::errors::Unimplemented(msg));
break;
case C10ErrorType::Error:
PADDLE_THROW(common::errors::InvalidArgument(msg));
break;
default:
PADDLE_THROW(common::errors::Fatal("Unknown error type: " + msg));
}
}
#define C10_THROW_ERROR(err_type, msg) C10ThrowImpl(err_type, msg)
// Warning support - simplified implementation compatible with PyTorch API
namespace c10 {
// Warning types
struct UserWarning {};
struct DeprecationWarning {};
// Simple Warning class
class Warning {
public:
template <typename WarningType, typename... Args>
Warning(WarningType type,
const std::tuple<const char*, const char*, uint32_t>& location,
const std::string& msg,
bool verbatim)
: msg_(msg), verbatim_(verbatim) {
(void)type;
(void)location;
(void)verbatim;
}
const std::string& msg() const { return msg_; }
private:
std::string msg_;
bool verbatim_;
};
// Warning handler - prints to stderr
inline void warn(const Warning& warning) {
std::cerr << "Warning: " << warning.msg() << std::endl;
}
// Helper to concatenate message arguments
template <typename... Args>
inline std::string torch_warn_msg_impl(const Args&... args) {
std::ostringstream oss;
(oss << ... << args);
return oss.str();
}
inline std::string torch_warn_msg_impl() { return ""; }
} // namespace c10
// TORCH_WARN macros
#ifdef DISABLE_WARN
#define _TORCH_WARN_WITH(...) ((void)0)
#else
#define _TORCH_WARN_WITH(warning_t, ...) \
do { \
::c10::warn(::c10::Warning( \
warning_t{}, \
std::make_tuple(__func__, __FILE__, static_cast<uint32_t>(__LINE__)), \
::c10::torch_warn_msg_impl(__VA_ARGS__), \
false)); \
} while (0)
#endif
#define TORCH_WARN(...) _TORCH_WARN_WITH(::c10::UserWarning, __VA_ARGS__)
#define TORCH_WARN_DEPRECATION(...) \
_TORCH_WARN_WITH(::c10::DeprecationWarning, __VA_ARGS__)
// TORCH_WARN_ONCE - only warns once per call site
#ifdef DISABLE_WARN
#define TORCH_WARN_ONCE(...) ((void)0)
#else
#define TORCH_WARN_ONCE(...) \
do { \
static bool C10_ANONYMOUS_VARIABLE(torch_warn_once_) = [] { \
TORCH_WARN(__VA_ARGS__); \
return true; \
}(); \
} while (0)
#endif
// Deprecated attribute macro
#define C10_DEPRECATED_MESSAGE(msg) [[deprecated(msg)]]
@@ -0,0 +1,65 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/// Defines the Float4_e2m1fn_x2 type (4-bit floating-point, two elements packed
/// into one byte). This is the FP4 dtype from the OCP MX format spec
/// (https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf,
/// Section 5.3.3)
///
/// Given two high precision values val0 and val1, here is the
/// binary configuration of their packed representation, from MSB to LSB:
///
/// original value | val1 : val0
/// ========================================
/// bit index (MSB==7, LSB==0) | 7654 : 3210
/// sign/exponent/mantissa | seem : seem
struct alignas(1) Float4_e2m1fn_x2 {
uint8_t val_;
Float4_e2m1fn_x2() = default;
explicit constexpr Float4_e2m1fn_x2(uint8_t val) : val_(val) {}
};
/// Comparison operators
inline bool operator==(const Float4_e2m1fn_x2& a, const Float4_e2m1fn_x2& b) {
return a.val_ == b.val_;
}
inline bool operator!=(const Float4_e2m1fn_x2& a, const Float4_e2m1fn_x2& b) {
return a.val_ != b.val_;
}
} // namespace c10
namespace at {
using c10::Float4_e2m1fn_x2;
using c10::operator!=;
using c10::operator==;
} // namespace at
namespace torch {
using c10::Float4_e2m1fn_x2;
using c10::operator!=;
using c10::operator==;
} // namespace torch
@@ -0,0 +1,27 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/float8_e4m3fn.h"
namespace c10 {
using Float8_e4m3fn = ::phi::dtype::float8_e4m3fn;
} // namespace c10
namespace at {
using c10::Float8_e4m3fn;
} // namespace at
namespace torch {
using c10::Float8_e4m3fn;
} // namespace torch
@@ -0,0 +1,40 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
struct Float8_e4m3fnuz {
constexpr Float8_e4m3fnuz() = default;
explicit constexpr Float8_e4m3fnuz(uint8_t value) : x(value) {}
uint8_t x{0};
};
} // namespace c10
namespace at {
using c10::Float8_e4m3fnuz;
} // namespace at
namespace torch {
using c10::Float8_e4m3fnuz;
} // namespace torch
@@ -0,0 +1,28 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/float8_e5m2.h"
namespace c10 {
using Float8_e5m2 = ::phi::dtype::float8_e5m2;
} // namespace c10
namespace at {
using c10::Float8_e5m2;
} // namespace at
namespace torch {
using c10::Float8_e5m2;
} // namespace torch
@@ -0,0 +1,40 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
struct Float8_e5m2fnuz {
constexpr Float8_e5m2fnuz() = default;
explicit constexpr Float8_e5m2fnuz(uint8_t value) : x(value) {}
uint8_t x{0};
};
} // namespace c10
namespace at {
using c10::Float8_e5m2fnuz;
} // namespace at
namespace torch {
using c10::Float8_e5m2fnuz;
} // namespace torch
@@ -0,0 +1,40 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
struct Float8_e8m0fnu {
constexpr Float8_e8m0fnu() = default;
explicit constexpr Float8_e8m0fnu(uint8_t value) : x(value) {}
uint8_t x{0};
};
} // namespace c10
namespace at {
using c10::Float8_e8m0fnu;
} // namespace at
namespace torch {
using c10::Float8_e8m0fnu;
} // namespace torch
@@ -0,0 +1,29 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/float16.h"
namespace c10 {
using Half = ::phi::dtype::float16;
} // namespace c10
namespace at {
using c10::Half;
} // namespace at
namespace torch {
using c10::Half;
} // namespace torch
@@ -0,0 +1,26 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <optional>
namespace c10 {
// Aliases from C++17 std::optional
using std::bad_optional_access;
using std::make_optional;
using std::nullopt;
using std::nullopt_t;
using std::optional;
} // namespace c10
@@ -0,0 +1,234 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/util/ArrayRef.h>
#include <cstdint>
#include <optional>
#include <vector>
namespace c10 {
template <typename T>
class OptionalArrayRef final {
public:
// Constructors
constexpr OptionalArrayRef() noexcept = default;
constexpr OptionalArrayRef(std::nullopt_t) noexcept {}
OptionalArrayRef(const OptionalArrayRef& other) = default;
OptionalArrayRef(OptionalArrayRef&& other) noexcept = default;
constexpr OptionalArrayRef(const std::optional<ArrayRef<T>>& other) noexcept
: wrapped_opt_array_ref(other) {}
constexpr OptionalArrayRef(std::optional<ArrayRef<T>>&& other) noexcept
: wrapped_opt_array_ref(std::move(other)) {}
constexpr OptionalArrayRef(const T& value) noexcept
: wrapped_opt_array_ref(value) {}
template <
typename U = ArrayRef<T>,
std::enable_if_t<!std::is_same_v<std::decay_t<U>, OptionalArrayRef> &&
!std::is_same_v<std::decay_t<U>, std::in_place_t> &&
std::is_constructible_v<ArrayRef<T>, U&&> &&
std::is_convertible_v<U&&, ArrayRef<T>> &&
!std::is_convertible_v<U&&, T>,
bool> = false>
constexpr OptionalArrayRef(U&& value) noexcept(
std::is_nothrow_constructible_v<ArrayRef<T>, U&&>)
: wrapped_opt_array_ref(std::forward<U>(value)) {}
template <
typename U = ArrayRef<T>,
std::enable_if_t<!std::is_same_v<std::decay_t<U>, OptionalArrayRef> &&
!std::is_same_v<std::decay_t<U>, std::in_place_t> &&
std::is_constructible_v<ArrayRef<T>, U&&> &&
!std::is_convertible_v<U&&, ArrayRef<T>>,
bool> = false>
constexpr explicit OptionalArrayRef(U&& value) noexcept(
std::is_nothrow_constructible_v<ArrayRef<T>, U&&>)
: wrapped_opt_array_ref(std::forward<U>(value)) {}
template <typename... Args>
constexpr explicit OptionalArrayRef(std::in_place_t ip,
Args&&... args) noexcept
: wrapped_opt_array_ref(ip, std::forward<Args>(args)...) {}
template <typename U, typename... Args>
constexpr explicit OptionalArrayRef(std::in_place_t ip,
std::initializer_list<U> il,
Args&&... args)
: wrapped_opt_array_ref(ip, il, std::forward<Args>(args)...) {}
constexpr OptionalArrayRef(const std::initializer_list<T>& Vec)
: wrapped_opt_array_ref(ArrayRef<T>(Vec)) {}
// Destructor
~OptionalArrayRef() = default;
// Assignment
constexpr OptionalArrayRef& operator=(std::nullopt_t) noexcept {
wrapped_opt_array_ref = std::nullopt;
return *this;
}
OptionalArrayRef& operator=(const OptionalArrayRef& other) = default;
OptionalArrayRef& operator=(OptionalArrayRef&& other) noexcept = default;
constexpr OptionalArrayRef& operator=(
const std::optional<ArrayRef<T>>& other) noexcept {
wrapped_opt_array_ref = other;
return *this;
}
constexpr OptionalArrayRef& operator=(
std::optional<ArrayRef<T>>&& other) noexcept {
wrapped_opt_array_ref = std::move(other);
return *this;
}
template <typename U = ArrayRef<T>,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<U>, OptionalArrayRef> &&
std::is_constructible_v<ArrayRef<T>, U&&> &&
std::is_assignable_v<ArrayRef<T>&, U&&>>>
constexpr OptionalArrayRef& operator=(U&& value) noexcept(
std::is_nothrow_constructible_v<ArrayRef<T>, U&&>&&
std::is_nothrow_assignable_v<ArrayRef<T>&, U&&>) {
wrapped_opt_array_ref = std::forward<U>(value);
return *this;
}
// Observers
constexpr ArrayRef<T>* operator->() noexcept {
return &wrapped_opt_array_ref.value();
}
constexpr const ArrayRef<T>* operator->() const noexcept {
return &wrapped_opt_array_ref.value();
}
constexpr ArrayRef<T>& operator*() & noexcept {
return wrapped_opt_array_ref.value();
}
constexpr const ArrayRef<T>& operator*() const& noexcept {
return wrapped_opt_array_ref.value();
}
constexpr ArrayRef<T>&& operator*() && noexcept {
return std::move(wrapped_opt_array_ref.value());
}
constexpr const ArrayRef<T>&& operator*() const&& noexcept {
return std::move(wrapped_opt_array_ref.value());
}
constexpr explicit operator bool() const noexcept {
return wrapped_opt_array_ref.has_value();
}
constexpr bool has_value() const noexcept {
return wrapped_opt_array_ref.has_value();
}
constexpr ArrayRef<T>& value() & { return wrapped_opt_array_ref.value(); }
constexpr const ArrayRef<T>& value() const& {
return wrapped_opt_array_ref.value();
}
constexpr ArrayRef<T>&& value() && {
return std::move(wrapped_opt_array_ref.value());
}
constexpr const ArrayRef<T>&& value() const&& {
return std::move(wrapped_opt_array_ref.value());
}
template <typename U>
constexpr std::enable_if_t<std::is_convertible_v<U&&, ArrayRef<T>>,
ArrayRef<T>>
value_or(U&& default_value) const& {
return wrapped_opt_array_ref.value_or(std::forward<U>(default_value));
}
template <typename U>
constexpr std::enable_if_t<std::is_convertible_v<U&&, ArrayRef<T>>,
ArrayRef<T>>
value_or(U&& default_value) && {
return wrapped_opt_array_ref.value_or(std::forward<U>(default_value));
}
// Modifiers
constexpr void swap(OptionalArrayRef& other) noexcept {
std::swap(wrapped_opt_array_ref, other.wrapped_opt_array_ref);
}
constexpr void reset() noexcept { wrapped_opt_array_ref.reset(); }
template <typename... Args>
constexpr std::enable_if_t<std::is_constructible_v<ArrayRef<T>, Args&&...>,
ArrayRef<T>&>
emplace(Args&&... args) noexcept(
std::is_nothrow_constructible_v<ArrayRef<T>, Args&&...>) {
return wrapped_opt_array_ref.emplace(std::forward<Args>(args)...);
}
template <typename U, typename... Args>
constexpr ArrayRef<T>& emplace(std::initializer_list<U> il,
Args&&... args) noexcept {
return wrapped_opt_array_ref.emplace(il, std::forward<Args>(args)...);
}
private:
std::optional<ArrayRef<T>> wrapped_opt_array_ref;
};
using OptionalIntArrayRef = OptionalArrayRef<int64_t>;
inline bool operator==(const OptionalIntArrayRef& a1,
const IntArrayRef& other) {
if (!a1.has_value()) {
return false;
}
return a1.value() == other;
}
inline bool operator==(const c10::IntArrayRef& a1,
const c10::OptionalIntArrayRef& a2) {
return a2 == a1;
}
} // namespace c10
namespace at {
using c10::OptionalIntArrayRef;
} // namespace at
namespace torch {
using c10::OptionalIntArrayRef;
} // namespace torch
@@ -0,0 +1,95 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
#include <string_view>
#include <type_traits>
namespace c10 {
namespace util {
class type_index final {
public:
constexpr explicit type_index(uint64_t checksum = 0) : checksum_(checksum) {}
constexpr uint64_t underlyingId() const noexcept { return checksum_; }
friend constexpr bool operator==(type_index lhs, type_index rhs) noexcept {
return lhs.checksum_ == rhs.checksum_;
}
friend constexpr bool operator!=(type_index lhs, type_index rhs) noexcept {
return !(lhs == rhs);
}
friend constexpr bool operator<(type_index lhs, type_index rhs) noexcept {
return lhs.checksum_ < rhs.checksum_;
}
private:
uint64_t checksum_;
};
namespace detail {
constexpr uint64_t kFnvOffsetBasis = 14695981039346656037ULL;
constexpr uint64_t kFnvPrime = 1099511628211ULL;
constexpr uint64_t fnv1a64(const char* data, size_t n) {
uint64_t hash = kFnvOffsetBasis;
for (size_t i = 0; i < n; ++i) {
hash ^= static_cast<uint64_t>(static_cast<unsigned char>(data[i]));
hash *= kFnvPrime;
}
return hash;
}
template <typename T>
constexpr std::string_view type_signature() {
#if defined(_MSC_VER) && !defined(__clang__)
constexpr std::string_view sig = __FUNCSIG__;
#else
constexpr std::string_view sig = __PRETTY_FUNCTION__;
#endif
return sig;
}
template <typename T>
constexpr uint64_t type_index_impl() {
constexpr std::string_view sig = type_signature<T>();
return fnv1a64(sig.data(), sig.size());
}
} // namespace detail
template <typename T>
constexpr type_index get_type_index() {
return type_index(detail::type_index_impl<std::decay_t<T>>());
}
} // namespace util
} // namespace c10
namespace std {
template <>
struct hash<c10::util::type_index> {
size_t operator()(c10::util::type_index v) const noexcept {
return static_cast<size_t>(v.underlyingId());
}
};
} // namespace std
@@ -0,0 +1,139 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <c10/macros/Macros.h>
#include <cstddef>
#include <memory>
#include <utility>
namespace c10 {
using DeleterFnPtr = void (*)(void*);
namespace detail {
// Does not delete anything
inline void deleteNothing(void* /*unused*/) {}
// A detail::UniqueVoidPtr is an owning smart pointer like unique_ptr, but
// with three major differences:
//
// 1) It is specialized to void
//
// 2) It is specialized for a function pointer deleter
// void(void* ctx); i.e., the deleter doesn't take a
// reference to the data, just to a context pointer
// (erased as void*). In fact, internally, this pointer
// is implemented as having an owning reference to
// context, and a non-owning reference to data; this is why
// you release_context(), not release() (the conventional
// API for release() wouldn't give you enough information
// to properly dispose of the object later.)
//
// 3) The deleter is guaranteed to be called when the unique
// pointer is destructed and the context is non-null; this is different
// from std::unique_ptr where the deleter is not called if the
// data pointer is null.
//
// Some of the methods have slightly different types than std::unique_ptr
// to reflect this.
//
class UniqueVoidPtr {
private:
// Lifetime tied to ctx_
void* data_;
std::unique_ptr<void, DeleterFnPtr> ctx_;
public:
UniqueVoidPtr() : data_(nullptr), ctx_(nullptr, &deleteNothing) {}
explicit UniqueVoidPtr(void* data)
: data_(data), ctx_(nullptr, &deleteNothing) {}
UniqueVoidPtr(void* data, void* ctx, DeleterFnPtr ctx_deleter)
: data_(data), ctx_(ctx, ctx_deleter ? ctx_deleter : &deleteNothing) {}
void* operator->() const { return data_; }
void clear() {
ctx_ = nullptr;
data_ = nullptr;
}
void* get() const { return data_; }
bool /* success */ unsafe_reset_data_and_ctx(void* new_data_and_ctx) {
if (__builtin_expect(
static_cast<bool>((ctx_.get_deleter() != &deleteNothing)), 0)) {
return false;
}
// seems quicker than calling the no-op deleter when we reset
(void)ctx_.release();
ctx_.reset(new_data_and_ctx);
data_ = new_data_and_ctx;
return true;
}
void* get_context() const { return ctx_.get(); }
void* release_context() { return ctx_.release(); }
std::unique_ptr<void, DeleterFnPtr>&& move_context() {
return std::move(ctx_);
}
[[nodiscard]] bool compare_exchange_deleter(DeleterFnPtr expected_deleter,
DeleterFnPtr new_deleter) {
if (get_deleter() != expected_deleter) return false;
ctx_ = std::unique_ptr<void, DeleterFnPtr>(ctx_.release(), new_deleter);
return true;
}
template <typename T>
T* cast_context(DeleterFnPtr expected_deleter) const {
if (get_deleter() != expected_deleter) return nullptr;
return static_cast<T*>(get_context());
}
operator bool() const { return data_ || ctx_; }
DeleterFnPtr get_deleter() const { return ctx_.get_deleter(); }
};
// Note [How UniqueVoidPtr is implemented]
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// UniqueVoidPtr solves a common problem for allocators of tensor data, which
// is that the data pointer (e.g., float*) which you are interested in, is not
// the same as the context pointer (e.g., DLManagedTensor) which you need
// to actually deallocate the data. Under a conventional deleter design, you
// have to store extra context in the deleter itself so that you can actually
// delete the right thing. Implementing this with standard C++ is somewhat
// error-prone: if you use a std::unique_ptr to manage tensors, the deleter will
// not be called if the data pointer is nullptr, which can cause a leak if the
// context pointer is non-null (and the deleter is responsible for freeing both
// the data pointer and the context pointer).
//
// So, in our reimplementation of unique_ptr, which just store the context
// directly in the unique pointer, and attach the deleter to the context
// pointer itself. In simple cases, the context pointer is just the pointer
// itself.
inline bool operator==(const UniqueVoidPtr& sp, std::nullptr_t) noexcept {
return !sp;
}
inline bool operator==(std::nullptr_t, const UniqueVoidPtr& sp) noexcept {
return !sp;
}
inline bool operator!=(const UniqueVoidPtr& sp, std::nullptr_t) noexcept {
return sp;
}
inline bool operator!=(std::nullptr_t, const UniqueVoidPtr& sp) noexcept {
return sp;
}
} // namespace detail
} // namespace c10
@@ -0,0 +1,106 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/util/Exception.h>
#include <cstdint>
#include <functional>
#include <iterator>
#include <numeric>
#include <type_traits>
#include <utility>
namespace c10 {
template <typename C,
std::enable_if_t<std::is_integral_v<typename C::value_type>, int> = 0>
inline int64_t sum_integers(const C& container) {
return std::accumulate(
container.begin(), container.end(), static_cast<int64_t>(0));
}
template <typename Iter,
std::enable_if_t<std::is_integral_v<
typename std::iterator_traits<Iter>::value_type>,
int> = 0>
inline int64_t sum_integers(Iter begin, Iter end) {
return std::accumulate(begin, end, static_cast<int64_t>(0));
}
template <typename C,
std::enable_if_t<std::is_integral_v<typename C::value_type>, int> = 0>
inline int64_t multiply_integers(const C& container) {
return std::accumulate(container.begin(),
container.end(),
static_cast<int64_t>(1),
std::multiplies<>());
}
template <typename Iter,
std::enable_if_t<std::is_integral_v<
typename std::iterator_traits<Iter>::value_type>,
int> = 0>
inline int64_t multiply_integers(Iter begin, Iter end) {
return std::accumulate(
begin, end, static_cast<int64_t>(1), std::multiplies<>());
}
template <typename C,
std::enable_if_t<std::is_integral_v<typename C::value_type>, int> = 0>
inline int64_t numelements_from_dim(const int k, const C& dims) {
if (k > static_cast<int>(dims.size())) {
return 1;
} else {
auto cbegin = dims.cbegin();
std::advance(cbegin, k);
return multiply_integers(cbegin, dims.cend());
}
}
template <typename C,
std::enable_if_t<std::is_integral_v<typename C::value_type>, int> = 0>
inline int64_t numelements_to_dim(const int k, const C& dims) {
TORCH_INTERNAL_ASSERT(0 <= k);
TORCH_INTERNAL_ASSERT((unsigned)k <= dims.size());
auto cend = dims.cbegin();
std::advance(cend, k);
return multiply_integers(dims.cbegin(), cend);
}
template <typename C,
std::enable_if_t<std::is_integral_v<typename C::value_type>, int> = 0>
inline int64_t numelements_between_dim(int k, int l, const C& dims) {
TORCH_INTERNAL_ASSERT(0 <= k);
TORCH_INTERNAL_ASSERT(0 <= l);
if (k > l) {
std::swap(k, l);
}
TORCH_INTERNAL_ASSERT((unsigned)l < dims.size());
auto cbegin = dims.cbegin();
auto cend = dims.cbegin();
std::advance(cbegin, k);
std::advance(cend, l);
return multiply_integers(cbegin, cend);
}
} // namespace c10
@@ -0,0 +1,76 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
struct bits1x8 {
constexpr bits1x8() = default;
explicit constexpr bits1x8(uint8_t value) : val_(value) {}
uint8_t val_{0};
};
struct bits2x4 {
constexpr bits2x4() = default;
explicit constexpr bits2x4(uint8_t value) : val_(value) {}
uint8_t val_{0};
};
struct bits4x2 {
constexpr bits4x2() = default;
explicit constexpr bits4x2(uint8_t value) : val_(value) {}
uint8_t val_{0};
};
struct bits8 {
constexpr bits8() = default;
explicit constexpr bits8(uint8_t value) : val_(value) {}
uint8_t val_{0};
};
struct bits16 {
constexpr bits16() = default;
explicit constexpr bits16(uint16_t value) : val_(value) {}
uint16_t val_{0};
};
} // namespace c10
namespace at {
using c10::bits16;
using c10::bits1x8;
using c10::bits2x4;
using c10::bits4x2;
using c10::bits8;
} // namespace at
namespace torch {
using c10::bits16;
using c10::bits1x8;
using c10::bits2x4;
using c10::bits4x2;
using c10::bits8;
} // namespace torch
@@ -0,0 +1,29 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/phi/common/complex.h"
namespace c10 {
template <typename T>
using complex = ::phi::dtype::complex<T>;
} // namespace c10
namespace at {
using c10::complex;
} // namespace at
namespace torch {
using c10::complex;
} // namespace torch
@@ -0,0 +1,442 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from the PyTorch project.
// Licensed under BSD-style license:
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <atomic>
#include <cstdint>
#include <type_traits>
#include <utility>
namespace c10 {
// Forward declarations
class intrusive_ptr_target;
namespace raw {
namespace intrusive_ptr {
inline void incref(intrusive_ptr_target* self);
inline void decref(intrusive_ptr_target* self);
} // namespace intrusive_ptr
namespace weak_intrusive_ptr {
inline void incref(intrusive_ptr_target* self);
inline void decref(intrusive_ptr_target* self);
} // namespace weak_intrusive_ptr
struct DontIncreaseRefcount {};
} // namespace raw
namespace detail {
constexpr uint64_t kImpracticallyHugeReferenceCount = 0x0FFFFFFF;
constexpr uint64_t kImpracticallyHugeWeakReferenceCount =
(kImpracticallyHugeReferenceCount << 32);
constexpr uint64_t kReferenceCountOne = 1;
constexpr uint64_t kWeakReferenceCountOne = (kReferenceCountOne << 32);
constexpr uint64_t kUniqueRef = (kReferenceCountOne | kWeakReferenceCountOne);
inline uint32_t refcount(uint64_t combined_refcount) {
return static_cast<uint32_t>(combined_refcount);
}
inline uint32_t weakcount(uint64_t combined_refcount) {
// Bit 63 is reserved for kHasPyObject in PyTorch (a flag indicating a live
// Python wrapper). This compat layer does not implement the PyObject path,
// so the bit will never be set, but we mask it out here to match PyTorch's
// extraction logic and remain numerically correct if the bit were ever set.
return static_cast<uint32_t>((combined_refcount & ~(uint64_t(1) << 63)) >>
32);
}
inline uint64_t atomic_combined_refcount_increment(
std::atomic<uint64_t>* combined_refcount, uint64_t inc) {
return combined_refcount->fetch_add(inc, std::memory_order_relaxed) + inc;
}
inline uint64_t atomic_combined_refcount_decrement(
std::atomic<uint64_t>* combined_refcount, uint64_t dec) {
return combined_refcount->fetch_sub(dec, std::memory_order_acq_rel) - dec;
}
inline uint32_t atomic_weakcount_increment(
std::atomic<uint64_t>* combined_refcount) {
return weakcount(atomic_combined_refcount_increment(combined_refcount,
kWeakReferenceCountOne));
}
inline uint32_t atomic_weakcount_decrement(
std::atomic<uint64_t>* combined_refcount) {
return weakcount(atomic_combined_refcount_decrement(combined_refcount,
kWeakReferenceCountOne));
}
template <class T>
struct intrusive_target_default_null_type final {
static constexpr T* singleton() noexcept { return nullptr; }
};
} // namespace detail
class intrusive_ptr_target {
public:
intrusive_ptr_target() noexcept : combined_refcount_(0) {}
intrusive_ptr_target(intrusive_ptr_target&& /*other*/) noexcept
: intrusive_ptr_target() {}
intrusive_ptr_target& operator=(intrusive_ptr_target&& /*other*/) noexcept {
return *this;
}
intrusive_ptr_target(const intrusive_ptr_target& /*other*/) noexcept
: intrusive_ptr_target() {}
intrusive_ptr_target& operator=(
const intrusive_ptr_target& /*other*/) noexcept {
return *this;
}
uint32_t refcount() const {
return detail::refcount(combined_refcount_.load(std::memory_order_relaxed));
}
uint32_t weakcount() const {
return detail::weakcount(
combined_refcount_.load(std::memory_order_relaxed));
}
protected:
virtual ~intrusive_ptr_target() = default;
private:
mutable std::atomic<uint64_t> combined_refcount_;
template <typename T, typename NullType>
friend class intrusive_ptr;
template <typename T, typename NullType>
friend class weak_intrusive_ptr;
friend inline void raw::intrusive_ptr::incref(intrusive_ptr_target* self);
friend inline void raw::intrusive_ptr::decref(intrusive_ptr_target* self);
friend inline void raw::weak_intrusive_ptr::incref(
intrusive_ptr_target* self);
friend inline void raw::weak_intrusive_ptr::decref(
intrusive_ptr_target* self);
};
namespace raw {
namespace intrusive_ptr {
inline void incref(intrusive_ptr_target* self) {
if (self) {
detail::atomic_combined_refcount_increment(&self->combined_refcount_,
detail::kReferenceCountOne);
}
}
inline void decref(intrusive_ptr_target* self) {
if (self) {
uint64_t new_count = detail::atomic_combined_refcount_decrement(
&self->combined_refcount_, detail::kReferenceCountOne);
if (detail::refcount(new_count) == 0) {
// All strong references gone; release the implicit weak reference
// (strong refs count as +1 to weakcount per the kUniqueRef invariant).
if (detail::atomic_weakcount_decrement(&self->combined_refcount_) == 0) {
delete self;
}
}
}
}
} // namespace intrusive_ptr
namespace weak_intrusive_ptr {
inline void incref(intrusive_ptr_target* self) {
if (self) {
detail::atomic_weakcount_increment(&self->combined_refcount_);
}
}
inline void decref(intrusive_ptr_target* self) {
if (self) {
if (detail::atomic_weakcount_decrement(&self->combined_refcount_) == 0) {
delete self;
}
}
}
} // namespace weak_intrusive_ptr
} // namespace raw
template <class TTarget, class NullType>
class weak_intrusive_ptr;
template <class TTarget,
class NullType = detail::intrusive_target_default_null_type<TTarget>>
class intrusive_ptr final {
private:
static_assert(
std::is_base_of_v<TTarget,
std::remove_pointer_t<decltype(NullType::singleton())>>,
"NullType::singleton() must return a element_type* pointer");
TTarget* target_;
template <class TTarget2, class NullType2>
friend class intrusive_ptr;
friend class weak_intrusive_ptr<TTarget, NullType>;
void retain_() noexcept {
if (target_ != NullType::singleton()) {
detail::atomic_combined_refcount_increment(&target_->combined_refcount_,
detail::kReferenceCountOne);
}
}
void reset_() noexcept {
if (target_ != NullType::singleton()) {
uint64_t new_count = detail::atomic_combined_refcount_decrement(
&target_->combined_refcount_, detail::kReferenceCountOne);
if (detail::refcount(new_count) == 0) {
// All strong references gone; release the implicit weak reference
// (strong refs count as +1 to weakcount per the kUniqueRef invariant).
if (detail::atomic_weakcount_decrement(&target_->combined_refcount_) ==
0) {
delete target_;
}
}
target_ = NullType::singleton();
}
}
public:
using element_type = TTarget;
using pointer = TTarget*;
intrusive_ptr() noexcept : target_(NullType::singleton()) {}
intrusive_ptr(std::nullptr_t) noexcept : target_(NullType::singleton()) {}
explicit intrusive_ptr(TTarget* raw) : target_(raw) {
if (target_ != NullType::singleton()) {
target_->combined_refcount_.store(detail::kUniqueRef,
std::memory_order_relaxed);
}
}
intrusive_ptr(const intrusive_ptr& rhs) : target_(rhs.target_) { retain_(); }
intrusive_ptr(intrusive_ptr&& rhs) noexcept : target_(rhs.target_) {
rhs.target_ = NullType::singleton();
}
template <typename From, typename FromNullType>
/* implicit */ intrusive_ptr(
const intrusive_ptr<From, FromNullType>& rhs) noexcept
: target_(rhs.target_) {
static_assert(std::is_convertible_v<From*, TTarget*>,
"Source type must be convertible to target type");
retain_();
}
template <typename From, typename FromNullType>
/* implicit */ intrusive_ptr(intrusive_ptr<From, FromNullType>&& rhs) noexcept
: target_(rhs.target_) {
static_assert(std::is_convertible_v<From*, TTarget*>,
"Source type must be convertible to target type");
rhs.target_ = FromNullType::singleton();
}
~intrusive_ptr() { reset_(); }
intrusive_ptr& operator=(const intrusive_ptr& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
retain_();
}
return *this;
}
intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
rhs.target_ = NullType::singleton();
}
return *this;
}
// Takes ownership of a raw pointer without incrementing the refcount.
static intrusive_ptr reclaim(TTarget* raw_ptr) {
intrusive_ptr result;
result.target_ = raw_ptr;
return result;
}
// unsafe_adopt is a PyTorch API compatibility alias for reclaim().
// Both adopt a raw pointer without incrementing the refcount; prefer
// reclaim() in new code.
static intrusive_ptr unsafe_adopt(TTarget* raw_ptr) {
return reclaim(raw_ptr);
}
TTarget* get() const noexcept { return target_; }
TTarget& operator*() const { return *target_; }
TTarget* operator->() const { return target_; }
explicit operator bool() const noexcept {
return target_ != NullType::singleton();
}
uint32_t use_count() const noexcept {
if (target_ == NullType::singleton()) {
return 0;
}
return target_->refcount();
}
bool defined() const noexcept { return target_ != NullType::singleton(); }
bool unique() const noexcept { return use_count() == 1; }
void reset() noexcept { reset_(); }
void swap(intrusive_ptr& other) noexcept {
using std::swap;
swap(target_, other.target_);
}
[[deprecated(
"intrusive_ptr::release is unsafe; use reclaim() or explicit ownership "
"transfer instead")]] TTarget*
release() noexcept {
TTarget* result = target_;
target_ = NullType::singleton();
return result;
}
bool operator==(const intrusive_ptr& rhs) const noexcept {
return target_ == rhs.target_;
}
bool operator!=(const intrusive_ptr& rhs) const noexcept {
return target_ != rhs.target_;
}
bool operator==(std::nullptr_t) const noexcept {
return target_ == NullType::singleton();
}
bool operator!=(std::nullptr_t) const noexcept {
return target_ != NullType::singleton();
}
};
template <class TTarget,
class NullType = detail::intrusive_target_default_null_type<TTarget>>
class weak_intrusive_ptr final {
private:
TTarget* target_;
template <class TTarget2, class NullType2>
friend class weak_intrusive_ptr;
friend class intrusive_ptr<TTarget, NullType>;
void retain_() {
if (target_ != NullType::singleton()) {
detail::atomic_weakcount_increment(&target_->combined_refcount_);
}
}
void reset_() noexcept {
if (target_ != NullType::singleton()) {
if (detail::atomic_weakcount_decrement(&target_->combined_refcount_) ==
0) {
delete target_;
}
target_ = NullType::singleton();
}
}
public:
using element_type = TTarget;
weak_intrusive_ptr() noexcept : target_(NullType::singleton()) {}
weak_intrusive_ptr(const intrusive_ptr<TTarget, NullType>& p)
: target_(p.target_) {
retain_();
}
weak_intrusive_ptr(const weak_intrusive_ptr& rhs) : target_(rhs.target_) {
retain_();
}
weak_intrusive_ptr(weak_intrusive_ptr&& rhs) noexcept : target_(rhs.target_) {
rhs.target_ = NullType::singleton();
}
~weak_intrusive_ptr() { reset_(); }
weak_intrusive_ptr& operator=(const weak_intrusive_ptr& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
retain_();
}
return *this;
}
weak_intrusive_ptr& operator=(weak_intrusive_ptr&& rhs) {
if (this != &rhs) {
reset_();
target_ = rhs.target_;
rhs.target_ = NullType::singleton();
}
return *this;
}
intrusive_ptr<TTarget, NullType> lock() const {
if (target_ == NullType::singleton()) {
return intrusive_ptr<TTarget, NullType>();
}
auto& atomic = target_->combined_refcount_;
uint64_t count = atomic.load(std::memory_order_relaxed);
while (true) {
if (detail::refcount(count) == 0) {
return intrusive_ptr<TTarget, NullType>();
}
if (atomic.compare_exchange_weak(count,
count + detail::kReferenceCountOne,
std::memory_order_acq_rel,
std::memory_order_relaxed)) {
return intrusive_ptr<TTarget, NullType>::unsafe_adopt(target_);
}
}
}
uint32_t use_count() const {
if (target_ == NullType::singleton()) {
return 0;
}
return target_->refcount();
}
bool expired() const { return use_count() == 0; }
void reset() { reset_(); }
};
// Creates a new T with an initial strong refcount of 1.
template <typename T, typename... Args>
intrusive_ptr<T> make_intrusive(Args&&... args) {
return intrusive_ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace c10
@@ -0,0 +1,43 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/**
* qint32 is for signed 32 bit quantized Tensors
*/
struct alignas(4) qint32 {
using underlying = int32_t;
int32_t val_;
qint32() = default;
explicit constexpr qint32(int32_t val) : val_(val) {}
};
} // namespace c10
namespace at {
using c10::qint32;
} // namespace at
namespace torch {
using c10::qint32;
} // namespace torch
@@ -0,0 +1,45 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/**
* This is the data type for quantized Tensors. Right now we only have
* qint8 which is for 8 bit Tensors, and qint32 for 32 bit int Tensors,
* we might have 4 bit, 2 bit or 1 bit data types in the future.
*/
struct alignas(1) qint8 {
using underlying = int8_t;
int8_t val_;
qint8() = default;
explicit constexpr qint8(int8_t val) : val_(val) {}
};
} // namespace c10
namespace at {
using c10::qint8;
} // namespace at
namespace torch {
using c10::qint8;
} // namespace torch
@@ -0,0 +1,44 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/**
* quint2x4 is for un-signed 2 bit quantized Tensors that are packed to byte
* boundary.
*/
struct alignas(1) quint2x4 {
using underlying = uint8_t;
uint8_t val_;
quint2x4() = default;
explicit constexpr quint2x4(uint8_t val) : val_(val) {}
};
} // namespace c10
namespace at {
using c10::quint2x4;
} // namespace at
namespace torch {
using c10::quint2x4;
} // namespace torch
@@ -0,0 +1,44 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/**
* quint4x2 is for un-signed 4 bit quantized Tensors that are packed to byte
* boundary.
*/
struct alignas(1) quint4x2 {
using underlying = uint8_t;
uint8_t val_;
quint4x2() = default;
explicit constexpr quint4x2(uint8_t val) : val_(val) {}
};
} // namespace c10
namespace at {
using c10::quint4x2;
} // namespace at
namespace torch {
using c10::quint4x2;
} // namespace torch
@@ -0,0 +1,43 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <cstdint>
namespace c10 {
/**
* quint8 is for unsigned 8 bit quantized Tensors
*/
struct alignas(1) quint8 {
using underlying = uint8_t;
uint8_t val_;
quint8() = default;
explicit constexpr quint8(uint8_t val) : val_(val) {}
};
} // namespace c10
namespace at {
using c10::quint8;
} // namespace at
namespace torch {
using c10::quint8;
} // namespace torch
@@ -0,0 +1,98 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#include <c10/util/typeid.h>
#include <algorithm>
namespace caffe2 {
std::mutex& TypeMeta::getTypeMetaDatasLock() {
static std::mutex lock;
return lock;
}
uint16_t TypeMeta::nextTypeIndex(
static_cast<uint16_t>(c10::ScalarType::NumOptions));
detail::TypeMetaData* TypeMeta::typeMetaDatas() {
static detail::TypeMetaData instances[kMaxTypeIndex + 1] = {
#define SCALAR_TYPE_META(T, _2, name) \
detail::TypeMetaData(sizeof(T), \
detail::_PickNew<T>(), \
detail::_PickPlacementNew<T>(), \
detail::_PickCopy<T>(), \
detail::_PickPlacementDelete<T>(), \
detail::_PickDelete<T>(), \
TypeIdentifier::Get<T>(), \
#name),
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(SCALAR_TYPE_META)
#undef SCALAR_TYPE_META
// Remaining entries default-initialize to empty TypeMetaData.
};
static std::once_flag init_once;
std::call_once(init_once, [] {
instances[static_cast<uint16_t>(c10::ScalarType::Undefined)] =
detail::TypeMetaData{0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
TypeIdentifier::Get<detail::_Uninitialized>(),
"Undefined"};
});
return instances;
}
uint16_t TypeMeta::existingMetaDataIndexForType(TypeIdentifier identifier) {
auto* meta_datas = typeMetaDatas();
const auto end = meta_datas + nextTypeIndex;
auto it = std::find_if(meta_datas, end, [identifier](const auto& meta_data) {
return meta_data.id_ == identifier;
});
if (it == end) {
return kMaxTypeIndex;
}
return static_cast<uint16_t>(it - meta_datas);
}
CAFFE_DEFINE_KNOWN_TYPE(std::string, std_string)
CAFFE_DEFINE_KNOWN_TYPE(char, char)
CAFFE_DEFINE_KNOWN_TYPE(std::unique_ptr<std::mutex>, std_unique_ptr_std_mutex)
CAFFE_DEFINE_KNOWN_TYPE(std::unique_ptr<std::atomic<bool>>,
std_unique_ptr_std_atomic_bool)
CAFFE_DEFINE_KNOWN_TYPE(std::vector<int32_t>, std_vector_int32_t)
CAFFE_DEFINE_KNOWN_TYPE(std::vector<int64_t>, std_vector_int64_t)
CAFFE_DEFINE_KNOWN_TYPE(std::vector<unsigned long>, // NOLINT(runtime/int)
std_vector_unsigned_long)
CAFFE_DEFINE_KNOWN_TYPE(bool*, bool_ptr)
CAFFE_DEFINE_KNOWN_TYPE(char*, char_ptr)
CAFFE_DEFINE_KNOWN_TYPE(int*, int_ptr)
CAFFE_DEFINE_KNOWN_TYPE(
detail::_guard_long_unique<long>, // NOLINT(runtime/int)
detail_guard_long_unique_long)
CAFFE_DEFINE_KNOWN_TYPE(
detail::_guard_long_unique<std::vector<long>>, // NOLINT(runtime/int)
detail_guard_long_unique_std_vector_long)
CAFFE_DEFINE_KNOWN_TYPE(float*, float_ptr)
CAFFE_DEFINE_KNOWN_TYPE(at::Half*, at_Half)
} // namespace caffe2
@@ -0,0 +1,652 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/macros/Macros.h>
#include <c10/util/BFloat16.h>
#include <c10/util/Exception.h>
#include <c10/util/Float8_e4m3fn.h>
#include <c10/util/Float8_e5m2.h>
#include <c10/util/Half.h>
#include <c10/util/TypeIndex.h>
#include <c10/core/ScalarType.h>
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "paddle/common/enforce.h"
// ---------------------------------------------------------------------------
// Auxiliary macros not provided by the compat Macros.h
// ---------------------------------------------------------------------------
#ifndef C10_LIKELY
#ifdef _MSC_VER
#define C10_LIKELY(val) (val)
#else
#define C10_LIKELY(val) (__builtin_expect(static_cast<bool>(val), 1))
#endif
#endif
#ifndef C10_UNLIKELY
#ifdef _MSC_VER
#define C10_UNLIKELY(val) (val)
#else
#define C10_UNLIKELY(val) (__builtin_expect(static_cast<bool>(val), 0))
#endif
#endif
#ifndef C10_API
#define C10_API PADDLE_API
#endif
#ifndef C10_EXPORT
#ifdef _MSC_VER
#define C10_EXPORT __declspec(dllexport)
#else
#define C10_EXPORT __attribute__((visibility("default")))
#endif
#endif
#ifndef C10_ALWAYS_INLINE
#ifdef _MSC_VER
#define C10_ALWAYS_INLINE __forceinline
#else
#define C10_ALWAYS_INLINE __attribute__((always_inline)) inline
#endif
#endif
#ifndef TORCH_INTERNAL_ASSERT_DEBUG_ONLY
#ifdef NDEBUG
#define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) ((void)0)
#else
#define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) TORCH_INTERNAL_ASSERT(__VA_ARGS__)
#endif
#endif
// ---------------------------------------------------------------------------
// caffe2::TypeIdentifier
// ---------------------------------------------------------------------------
namespace caffe2 {
/**
* A unique run-time type identifier.
*/
class C10_API TypeIdentifier final {
public:
friend std::ostream& operator<<(std::ostream& stream, TypeIdentifier typeId);
friend bool operator<(TypeIdentifier lhs, TypeIdentifier rhs) noexcept;
template <typename T>
static constexpr TypeIdentifier Get() noexcept {
return TypeIdentifier(c10::util::get_type_index<T>());
}
static constexpr TypeIdentifier uninitialized() noexcept {
return TypeIdentifier(c10::util::type_index{0});
}
uint64_t underlyingId() const noexcept { return id_.underlyingId(); }
bool operator==(TypeIdentifier other) const noexcept {
return id_ == other.id_;
}
bool operator!=(TypeIdentifier other) const noexcept {
return id_ != other.id_;
}
private:
constexpr explicit TypeIdentifier(c10::util::type_index id) noexcept
: id_(id) {}
c10::util::type_index id_;
};
inline bool operator<(TypeIdentifier lhs, TypeIdentifier rhs) noexcept {
return lhs.id_ < rhs.id_;
}
inline std::ostream& operator<<(std::ostream& stream, TypeIdentifier typeId) {
return stream << typeId.underlyingId();
}
} // namespace caffe2
// ---------------------------------------------------------------------------
// at::DataType alias
// ---------------------------------------------------------------------------
namespace at {
using DataType = caffe2::TypeIdentifier;
} // namespace at
// ---------------------------------------------------------------------------
// std::hash specialisation so TypeIdentifier can be used in unordered maps
// ---------------------------------------------------------------------------
namespace std {
template <>
struct hash<caffe2::TypeIdentifier> {
std::size_t operator()(caffe2::TypeIdentifier id) const noexcept {
return id.underlyingId();
}
};
} // namespace std
// ---------------------------------------------------------------------------
// caffe2::detail TypeMetaData + helper templates
// ---------------------------------------------------------------------------
namespace caffe2 {
namespace detail {
/**
* Per-type metadata record. One instance lives per registered type.
*/
struct TypeMetaData final {
using New = void*();
using PlacementNew = void(void*, size_t);
using Copy = void(const void*, void*, size_t);
using PlacementDelete = void(void*, size_t);
using Delete = void(void*);
constexpr TypeMetaData() noexcept
: itemsize_(0),
new_(nullptr),
placementNew_(nullptr),
copy_(nullptr),
placementDelete_(nullptr),
delete_(nullptr),
id_(TypeIdentifier::uninitialized()),
name_("nullptr (uninitialized)") {}
constexpr TypeMetaData(size_t itemsize,
New* newFn,
PlacementNew* placementNew,
Copy* copy,
PlacementDelete* placementDelete,
Delete* deleteFn,
TypeIdentifier id,
std::string_view name) noexcept
: itemsize_(itemsize),
new_(newFn),
placementNew_(placementNew),
copy_(copy),
placementDelete_(placementDelete),
delete_(deleteFn),
id_(id),
name_(name) {}
size_t itemsize_;
New* new_;
PlacementNew* placementNew_;
Copy* copy_;
PlacementDelete* placementDelete_;
Delete* delete_;
TypeIdentifier id_;
std::string_view name_;
};
// Error helper keeps this header free of heavy includes.
[[noreturn]] inline void _ThrowRuntimeTypeLogicError(const std::string& msg) {
PADDLE_THROW(common::errors::InvalidArgument(msg));
}
// ---------------------------------------------------------------------------
// Trait: treat reduced-precision scalars as "fundamental" (skip ctor/dtor).
// ---------------------------------------------------------------------------
template <typename T>
struct is_paddle_fundamental : std::is_fundamental<T> {};
template <>
struct is_paddle_fundamental<at::Half> : std::true_type {};
template <>
struct is_paddle_fundamental<at::BFloat16> : std::true_type {};
template <>
struct is_paddle_fundamental<c10::Float8_e4m3fn> : std::true_type {};
template <>
struct is_paddle_fundamental<c10::Float8_e5m2> : std::true_type {};
// ---------------------------------------------------------------------------
// PlacementNew helpers
// ---------------------------------------------------------------------------
template <typename T>
inline void _PlacementNew(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) new (typed_ptr + i) T;
}
template <typename T>
inline void _PlacementNewNotDefault(void* /*ptr*/, size_t /*n*/) {
_ThrowRuntimeTypeLogicError(std::string("Type ") + typeid(T).name() +
" is not default-constructible.");
}
template <typename T,
std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr>
inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() {
return (is_paddle_fundamental<T>::value || std::is_pointer_v<T>)
? nullptr
: &_PlacementNew<T>;
}
template <typename T,
std::enable_if_t<!std::is_default_constructible_v<T>>* = nullptr>
inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() {
return &_PlacementNewNotDefault<T>;
}
// ---------------------------------------------------------------------------
// New helpers
// ---------------------------------------------------------------------------
template <typename T>
inline void* _New() {
return new T;
}
template <typename T>
inline void* _NewNotDefault() {
_ThrowRuntimeTypeLogicError(std::string("Type ") + typeid(T).name() +
" is not default-constructible.");
}
template <typename T,
std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr>
inline constexpr TypeMetaData::New* _PickNew() {
return &_New<T>;
}
template <typename T,
std::enable_if_t<!std::is_default_constructible_v<T>>* = nullptr>
inline constexpr TypeMetaData::New* _PickNew() {
return &_NewNotDefault<T>;
}
// ---------------------------------------------------------------------------
// Copy helpers
// ---------------------------------------------------------------------------
template <typename T>
inline void _Copy(const void* src, void* dst, size_t n) {
const T* typed_src = static_cast<const T*>(src);
T* typed_dst = static_cast<T*>(dst);
for (size_t i = 0; i < n; ++i) typed_dst[i] = typed_src[i];
}
template <typename T>
inline void _CopyNotAllowed(const void* /*src*/, void* /*dst*/, size_t /*n*/) {
_ThrowRuntimeTypeLogicError(std::string("Type ") + typeid(T).name() +
" does not allow assignment.");
}
template <typename T, std::enable_if_t<std::is_copy_assignable_v<T>>* = nullptr>
inline constexpr TypeMetaData::Copy* _PickCopy() {
return (is_paddle_fundamental<T>::value || std::is_pointer_v<T>) ? nullptr
: &_Copy<T>;
}
template <typename T,
std::enable_if_t<!std::is_copy_assignable_v<T>>* = nullptr>
inline constexpr TypeMetaData::Copy* _PickCopy() {
return &_CopyNotAllowed<T>;
}
// ---------------------------------------------------------------------------
// PlacementDelete helpers
// ---------------------------------------------------------------------------
template <typename T>
inline void _PlacementDelete(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) typed_ptr[i].~T();
}
template <typename T>
inline constexpr TypeMetaData::PlacementDelete* _PickPlacementDelete() {
return (is_paddle_fundamental<T>::value || std::is_pointer_v<T>)
? nullptr
: &_PlacementDelete<T>;
}
// ---------------------------------------------------------------------------
// Delete helpers
// ---------------------------------------------------------------------------
template <typename T>
inline void _Delete(void* ptr) {
delete static_cast<T*>(ptr);
}
template <class T>
inline constexpr TypeMetaData::Delete* _PickDelete() noexcept {
return &_Delete<T>;
}
// Sentinel type for uninitialized TypeMeta.
class _Uninitialized final {};
} // namespace detail
// ---------------------------------------------------------------------------
// caffe2::TypeMeta
// ---------------------------------------------------------------------------
/**
* TypeMeta is a thin class that stores the type of a container (e.g. Blob or
* Tensor elements) with a unique run-time id. It mirrors the PyTorch / Caffe2
* TypeMeta API so that code written against libtorch's typeid.h compiles
* against this compat header without changes.
*/
class C10_API TypeMeta final {
public:
using New = detail::TypeMetaData::New;
using PlacementNew = detail::TypeMetaData::PlacementNew;
using Copy = detail::TypeMetaData::Copy;
using PlacementDelete = detail::TypeMetaData::PlacementDelete;
using Delete = detail::TypeMetaData::Delete;
// Default-constructs to "Undefined / uninitialized".
// NOTE: body is defined AFTER the _Uninitialized specialization below
// to avoid "specialization after instantiation" errors.
TypeMeta() noexcept;
~TypeMeta() = default;
TypeMeta(const TypeMeta& src) noexcept = default;
TypeMeta& operator=(const TypeMeta& src) noexcept = default;
TypeMeta(TypeMeta&& src) noexcept = default;
TypeMeta& operator=(TypeMeta&& src) noexcept = default;
inline TypeMeta& operator=(c10::ScalarType scalar_type) noexcept {
index_ = static_cast<uint16_t>(scalar_type);
return *this;
}
// ------------------------------------------------------------------
// Accessors
// ------------------------------------------------------------------
TypeIdentifier id() const noexcept { return data().id_; }
inline bool isScalarType() const noexcept {
return index_ <= static_cast<uint16_t>(c10::ScalarType::Undefined);
}
inline bool isScalarType(c10::ScalarType scalar_type) const noexcept {
return index_ == static_cast<uint16_t>(scalar_type);
}
inline size_t itemsize() const noexcept { return data().itemsize_; }
New* newFn() const noexcept { return data().new_; }
PlacementNew* placementNew() const noexcept { return data().placementNew_; }
Copy* copy() const noexcept { return data().copy_; }
PlacementDelete* placementDelete() const noexcept {
return data().placementDelete_;
}
Delete* deleteFn() const noexcept { return data().delete_; }
std::string_view name() const noexcept { return data().name_; }
friend bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept;
template <typename T>
bool Match() const noexcept {
return *this == Make<T>();
}
// ------------------------------------------------------------------
// Static helpers
// ------------------------------------------------------------------
template <class T>
static constexpr TypeIdentifier Id() noexcept {
return TypeIdentifier::Get<T>();
}
template <class T>
static std::string_view TypeName() noexcept {
return typeid(T).name();
}
template <class T>
static constexpr size_t ItemSize() noexcept {
return sizeof(T);
}
/** Returns a TypeMeta for type T. */
template <typename T>
static TypeMeta Make() {
return TypeMeta(_typeMetaData<T>());
}
/** Convert ScalarType enum → TypeMeta. */
static inline TypeMeta fromScalarType(c10::ScalarType scalar_type) {
const auto index = static_cast<uint16_t>(scalar_type);
TORCH_INTERNAL_ASSERT_DEBUG_ONLY(
index <= static_cast<uint16_t>(c10::ScalarType::Undefined),
"Unrecognized ScalarType");
return TypeMeta(index);
}
/** Convert TypeMeta → ScalarType enum. */
inline c10::ScalarType toScalarType() const {
if (C10_LIKELY(isScalarType())) {
return static_cast<c10::ScalarType>(index_);
}
PADDLE_THROW(common::errors::InvalidArgument(
"Unsupported TypeMeta in toScalarType (index=%d)",
static_cast<int>(index_)));
}
// ------------------------------------------------------------------
// Dynamic type registration (mirrors CAFFE_KNOWN_TYPE machinery)
// ------------------------------------------------------------------
template <class T>
static uint16_t addTypeMetaData() {
const auto identifier = TypeIdentifier::Get<T>();
std::lock_guard<std::mutex> lock(getTypeMetaDatasLock());
// Check whether already registered (e.g. from another DSO).
const uint16_t existing = existingMetaDataIndexForType(identifier);
if (existing != kMaxTypeIndex) return existing;
const uint16_t index = nextTypeIndex++;
TORCH_CHECK(index <= kMaxTypeIndex,
"Maximum number of CAFFE_KNOWN_TYPE declarations exceeded.");
typeMetaDatas()[index] =
detail::TypeMetaData{sizeof(T),
detail::_PickNew<T>(),
detail::_PickPlacementNew<T>(),
detail::_PickCopy<T>(),
detail::_PickPlacementDelete<T>(),
detail::_PickDelete<T>(),
identifier,
typeid(T).name()};
return index;
}
private:
explicit TypeMeta(uint16_t index) noexcept : index_(index) {}
// Maximum number of type-metadata slots (scalar + custom).
static constexpr uint16_t kMaxTypeIndex = 255;
static std::mutex& getTypeMetaDatasLock();
static uint16_t nextTypeIndex;
static detail::TypeMetaData* typeMetaDatas();
static uint16_t existingMetaDataIndexForType(TypeIdentifier identifier);
// Template specialisations return indexes into typeMetaDatas().
// Defined below the class for scalar types; compiled-in for custom types.
template <class T>
static uint16_t _typeMetaData() noexcept;
uint16_t index_;
inline const detail::TypeMetaData& data() const {
return typeMetaDatas()[index_];
}
};
// ---------------------------------------------------------------------------
// Specialisations of TypeMeta::_typeMetaData for ScalarType types
// ---------------------------------------------------------------------------
// 3-argument AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS
#define DEFINE_SCALAR_METADATA_INSTANCE(T, _2, name) \
template <> \
inline constexpr uint16_t TypeMeta::_typeMetaData<T>() noexcept { \
return static_cast<uint16_t>(c10::ScalarType::name); \
}
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(DEFINE_SCALAR_METADATA_INSTANCE)
#undef DEFINE_SCALAR_METADATA_INSTANCE
// _Uninitialized → Undefined slot
template <>
inline constexpr uint16_t
TypeMeta::_typeMetaData<detail::_Uninitialized>() noexcept {
return static_cast<uint16_t>(c10::ScalarType::Undefined);
}
// Default constructor defined here so that _typeMetaData<_Uninitialized> is
// already specialised before it is first called.
inline TypeMeta::TypeMeta() noexcept
: index_(_typeMetaData<detail::_Uninitialized>()) {}
// ---------------------------------------------------------------------------
// Comparison / stream operators
// ---------------------------------------------------------------------------
inline bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
return lhs.index_ == rhs.index_;
}
inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
return !(lhs == rhs);
}
inline std::ostream& operator<<(std::ostream& stream,
caffe2::TypeMeta typeMeta) {
return stream << typeMeta.name();
}
// ---------------------------------------------------------------------------
// CAFFE_KNOWN_TYPE macros (compat versions)
// ---------------------------------------------------------------------------
#if defined(_MSC_VER) || defined(__clang__)
#define EXPORT_IF_NOT_GCC C10_EXPORT
#else
#define EXPORT_IF_NOT_GCC
#endif
#if defined(_MSC_VER)
#define C10_TEMPLATE_API C10_API
#else
#define C10_TEMPLATE_API
#endif
// For use in a .cpp file.
#define CAFFE_KNOWN_TYPE(T) \
template C10_TEMPLATE_API uint16_t TypeMeta::addTypeMetaData<T>(); \
template <> \
EXPORT_IF_NOT_GCC uint16_t TypeMeta::_typeMetaData<T>() noexcept { \
static const uint16_t index = addTypeMetaData<T>(); \
return index; \
}
// For use in a .cpp file when a declaration in the header is provided.
#define CAFFE_DEFINE_KNOWN_TYPE(T, ident) \
template C10_TEMPLATE_API uint16_t TypeMeta::addTypeMetaData<T>(); \
namespace detail { \
EXPORT_IF_NOT_GCC extern const uint16_t ident##_metadata_index = \
TypeMeta::addTypeMetaData<T>(); \
} /* namespace detail */
// Declaration counterpart: provides an inline fast-path via a detail var.
// NOTE: On MSVC, directly referencing cross-DLL const data symbols is fragile
// and can cause unresolved externals during libpaddle linking. Use a
// function-local static cache there and keep non-MSVC behavior aligned with
// upstream declare/define model.
#if defined(_MSC_VER)
#define CAFFE_DECLARE_KNOWN_TYPE(T, ident) \
extern template C10_API uint16_t TypeMeta::addTypeMetaData<T>(); \
namespace detail { \
extern C10_API const uint16_t ident##_metadata_index; \
} /* namespace detail */ \
template <> \
C10_ALWAYS_INLINE uint16_t TypeMeta::_typeMetaData<T>() noexcept { \
static const uint16_t index = addTypeMetaData<T>(); \
return index; \
}
#else
#define CAFFE_DECLARE_KNOWN_TYPE(T, ident) \
extern template uint16_t TypeMeta::addTypeMetaData<T>(); \
namespace detail { \
extern C10_API const uint16_t ident##_metadata_index; \
} /* namespace detail */ \
template <> \
EXPORT_IF_NOT_GCC C10_ALWAYS_INLINE uint16_t \
TypeMeta::_typeMetaData<T>() noexcept { \
return detail::ident##_metadata_index; \
}
#endif
// Header-safe variant: lazy static, no external .cpp needed.
#define CAFFE_KNOWN_TYPE_NOEXPORT(T) \
template <> \
inline uint16_t TypeMeta::_typeMetaData<T>() noexcept { \
static const uint16_t index = addTypeMetaData<T>(); \
return index; \
}
// ---------------------------------------------------------------------------
// Built-in known types
// ---------------------------------------------------------------------------
namespace detail {
template <class T>
class _guard_long_unique_dummy final {};
template <class T>
using _guard_long_unique =
std::conditional_t<std::is_same_v<long, int32_t> || // NOLINT(runtime/int)
std::is_same_v<long, // NOLINT(runtime/int)
int64_t>,
_guard_long_unique_dummy<T>,
T>;
} // namespace detail
CAFFE_DECLARE_KNOWN_TYPE(std::string, std_string)
CAFFE_DECLARE_KNOWN_TYPE(char, char)
CAFFE_DECLARE_KNOWN_TYPE(std::unique_ptr<std::mutex>, std_unique_ptr_std_mutex)
CAFFE_DECLARE_KNOWN_TYPE(std::unique_ptr<std::atomic<bool>>,
std_unique_ptr_std_atomic_bool)
CAFFE_DECLARE_KNOWN_TYPE(std::vector<int32_t>, std_vector_int32_t)
CAFFE_DECLARE_KNOWN_TYPE(std::vector<int64_t>, std_vector_int64_t)
CAFFE_DECLARE_KNOWN_TYPE(std::vector<unsigned long>, // NOLINT(runtime/int)
std_vector_unsigned_long)
CAFFE_DECLARE_KNOWN_TYPE(bool*, bool_ptr)
CAFFE_DECLARE_KNOWN_TYPE(char*, char_ptr)
CAFFE_DECLARE_KNOWN_TYPE(int*, int_ptr)
CAFFE_DECLARE_KNOWN_TYPE(
detail::_guard_long_unique<long>, // NOLINT(runtime/int)
detail_guard_long_unique_long)
CAFFE_DECLARE_KNOWN_TYPE(
detail::_guard_long_unique<std::vector<long>>, // NOLINT(runtime/int)
detail_guard_long_unique_std_vector_long)
CAFFE_DECLARE_KNOWN_TYPE(float*, float_ptr)
CAFFE_DECLARE_KNOWN_TYPE(at::Half*, at_Half)
} // namespace caffe2