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};