diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md index 6d084f06a97dc..2977aeb122a79 100644 --- a/docs/OperatorKernels.md +++ b/docs/OperatorKernels.md @@ -703,6 +703,7 @@ The **OpSet Version** column uses the following notation: |||14|**T** = tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float), tensor(float16)| |||[9, 13]|**T** = tensor(double), tensor(float), tensor(float16)| |||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| +|BitwiseXor|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| |Cast|*in* input:**T1**
*out* output:**T2**|28+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| |||[25, 27]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| |||[23, 24]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| diff --git a/onnxruntime/core/providers/cuda/cuda_execution_provider.cc b/onnxruntime/core/providers/cuda/cuda_execution_provider.cc index 67bf96d8f3199..bc5ef55374df9 100755 --- a/onnxruntime/core/providers/cuda/cuda_execution_provider.cc +++ b/onnxruntime/core/providers/cuda/cuda_execution_provider.cc @@ -721,6 +721,14 @@ class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kO class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 7, bool, And); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 7, bool, Or); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 7, bool, Xor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, uint8_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, uint16_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, uint32_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, uint64_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, int8_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, int16_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, int32_t, BitwiseXor); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 18, int64_t, BitwiseXor); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 6, 7, Sum); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 8, 12, Sum); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCudaExecutionProvider, kOnnxDomain, 6, 11, Max); @@ -2050,6 +2058,14 @@ static Status RegisterCudaKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, diff --git a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.cc b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.cc index 0340f3b4c7e8b..04f1d5ad42a38 100644 --- a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.cc +++ b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.cc @@ -292,6 +292,14 @@ BINARY_OP_REGISTER_VERSIONED_CLASS_HFD(Pow, Pow_7, 7, 11) BINARY_LOGICALOP_TYPED(And, 7, bool) BINARY_LOGICALOP_TYPED(Or, 7, bool) BINARY_LOGICALOP_TYPED(Xor, 7, bool) +BINARY_OP_TYPED(BitwiseXor, 18, uint8_t) +BINARY_OP_TYPED(BitwiseXor, 18, uint16_t) +BINARY_OP_TYPED(BitwiseXor, 18, uint32_t) +BINARY_OP_TYPED(BitwiseXor, 18, uint64_t) +BINARY_OP_TYPED(BitwiseXor, 18, int8_t) +BINARY_OP_TYPED(BitwiseXor, 18, int16_t) +BINARY_OP_TYPED(BitwiseXor, 18, int32_t) +BINARY_OP_TYPED(BitwiseXor, 18, int64_t) BINARY_OP_VERSIONED_HFD(PRelu, 7, 8) BINARY_OP_VERSIONED_HFD(PRelu, 9, 15) // Opset-16 adds BFloat16 to allowed types for the PRelu operator diff --git a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.h b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.h index 337c7909f46e9..18020cd5ed43d 100644 --- a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.h +++ b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops.h @@ -200,6 +200,13 @@ class Xor final : public BinaryElementwise { Status ComputeInternal(OpKernelContext* context) const override; }; +template +class BitwiseXor final : public BinaryElementwise { + public: + BitwiseXor(const OpKernelInfo& info) : BinaryElementwise(info) {} + Status ComputeInternal(OpKernelContext* context) const override; +}; + // PRelu is activation function, but it's closer to binary elementwise ops in implementation template class PRelu final : public BinaryElementwise { diff --git a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.cu b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.cu index 2b08614b1e9c4..283f459002b83 100644 --- a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.cu +++ b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.cu @@ -159,6 +159,14 @@ SPECIALIZED_BINARY_ELEMENTWISE_IMPL_HFD(Pow_7) SPECIALIZED_BINARY_ELEMENTWISE_IMPL(And, bool) SPECIALIZED_BINARY_ELEMENTWISE_IMPL(Or, bool) SPECIALIZED_BINARY_ELEMENTWISE_IMPL(Xor, bool) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, uint8_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, uint16_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, uint32_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, uint64_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, int8_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, int16_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, int32_t) +SPECIALIZED_BINARY_ELEMENTWISE_IMPL(BitwiseXor, int64_t) SPECIALIZED_BINARY_ELEMENTWISE_IMPL_HFD(PRelu) SPECIALIZED_BINARY_ELEMENTWISE_IMPL_UZILHFD(Max) SPECIALIZED_BINARY_ELEMENTWISE_IMPL_UZILHFD(Min) diff --git a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.h b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.h index 5311f73f01a78..dd778ccb15be2 100644 --- a/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.h +++ b/onnxruntime/core/providers/cuda/math/binary_elementwise_ops_impl.h @@ -24,6 +24,7 @@ namespace cuda { BINARY_OP_NAME_EXPR(And, (a & b)) \ BINARY_OP_NAME_EXPR(Or, (a | b)) \ BINARY_OP_NAME_EXPR(Xor, (a ^ b)) \ + BINARY_OP_NAME_EXPR(BitwiseXor, (a ^ b)) \ BINARY_OP_NAME_EXPR(PRelu, (a > (T)0 ? a : a * b)) \ BINARY_OP_NAME_EXPR(Max, _Max(a, b)) \ BINARY_OP_NAME_EXPR(Min, _Min(a, b)) \ diff --git a/onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc b/onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc index 703c4fc04b74b..cb2ca610703b1 100644 --- a/onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc +++ b/onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc @@ -12,6 +12,7 @@ #include "test/common/trt_op_test_utils.h" #include "test/common/random_generator.h" #include "core/util/math.h" +#include "core/session/onnxruntime_session_options_config_keys.h" #include #include #include @@ -19,7 +20,6 @@ #ifdef USE_DML #include "core/providers/dml/dml_session_options_config_keys.h" -#include "core/session/onnxruntime_session_options_config_keys.h" #endif namespace onnxruntime { @@ -5321,6 +5321,79 @@ TEST(MathOpTest, BitwiseXor_multidirectional_broadcastBA) { test.Run(); } +#ifdef USE_CUDA +template +class CudaBitwiseXorTest : public ::testing::Test {}; + +using BitwiseXorIntegerTypes = ::testing::Types; +TYPED_TEST_SUITE(CudaBitwiseXorTest, BitwiseXorIntegerTypes); + +template +void RunCudaBitwiseXor(const std::vector& lhs_shape, const std::vector& lhs, + const std::vector& rhs_shape, const std::vector& rhs, + const std::vector& output_shape, const std::vector& expected) { + for (int opset : {18, 24}) { + auto cuda_ep = DefaultCudaExecutionProvider(); + if (!cuda_ep) { + GTEST_SKIP() << "CUDA EP is unavailable"; + } + OpTester test("BitwiseXor", opset); + test.AddInput("A", lhs_shape, lhs); + test.AddInput("B", rhs_shape, rhs); + test.AddOutput("C", output_shape, expected); + SessionOptions options; + // A correct CPU fallback must not hide a missing CUDA registration. + ASSERT_STATUS_OK(options.config_options.AddConfigEntry(kOrtSessionOptionsDisableCPUEPFallback, "1")); + test.Config(options).ConfigEp(std::move(cuda_ep)).RunWithConfig(); + } +} + +TYPED_TEST(CudaBitwiseXorTest, IntegerBoundaries) { + using T = TypeParam; + const T low = std::numeric_limits::min(); + const T high = std::numeric_limits::max(); + const T all_bits = static_cast(~T{0}); + RunCudaBitwiseXor({7}, {low, high, all_bits, low, high, T{0}, T{1}}, + {7}, {high, high, T{0}, low, T{0}, all_bits, all_bits}, + {7}, {all_bits, T{0}, all_bits, T{0}, high, all_bits, static_cast(all_bits ^ T{1})}); +} + +TYPED_TEST(CudaBitwiseXorTest, ScalarBroadcastAndTail) { + using T = TypeParam; + std::vector input(259); + std::vector expected(259); + const T mask = std::numeric_limits::max(); + for (size_t i = 0; i < input.size(); ++i) { + input[i] = static_cast(i % 127); + expected[i] = static_cast(input[i] ^ mask); + } + RunCudaBitwiseXor({}, {mask}, {259}, input, {259}, expected); + RunCudaBitwiseXor({259}, input, {}, {mask}, {259}, expected); + RunCudaBitwiseXor({}, {T{7}}, {}, {T{3}}, {}, {T{4}}); +} + +TYPED_TEST(CudaBitwiseXorTest, MultidirectionalBroadcast) { + using T = TypeParam; + RunCudaBitwiseXor({2, 1, 2}, {10, 11, 12, 13}, {1, 3, 1}, {15, 7, 3}, + {2, 3, 2}, {5, 4, 13, 12, 9, 8, 3, 2, 11, 10, 15, 14}); + RunCudaBitwiseXor({1, 3, 1}, {15, 7, 3}, {2, 1, 2}, {10, 11, 12, 13}, + {2, 3, 2}, {5, 4, 13, 12, 9, 8, 3, 2, 11, 10, 15, 14}); +} + +TYPED_TEST(CudaBitwiseXorTest, EmptyBroadcast) { + using T = TypeParam; + RunCudaBitwiseXor({0, 3}, {}, {1, 3}, {1, 2, 3}, {0, 3}, {}); + RunCudaBitwiseXor({}, {T{1}}, {2, 0, 3}, {}, {2, 0, 3}, {}); +} + +TEST(MathOpTest, BitwiseXor_CUDA_Int64RngState) { + const std::vector state{0, 1, 2047, 2048, 2049, 1073741824, 2147483647}; + RunCudaBitwiseXor({7}, state, {7}, {0, 0, 0, 1, 1, 524288, 1048575}, + {7}, {0, 1, 2047, 2049, 2048, 1074266112, 2146435072}); +} +#endif + TEST(MathOpTest, BitwiseNot) { OpTester test("BitwiseNot", 18); std::vector dims{3};