diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 4e30aadbb5b..6cde67ccd64 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -179,6 +179,10 @@
Internal changes ⚙️
+* An xDSL `Universe` containing all custom dialects and passes has been registered as an entry point, allowing
+ usage of PennyLane's dialects and passes with xDSL's command-line tools
+ [(#8372)](https://github.com/PennyLaneAI/pennylane/pull/8372)
+
* Fix all NumPy 1.X `DeprecationWarnings` in our source code.
[(#8497)](https://github.com/PennyLaneAI/pennylane/pull/8497)
diff --git a/pennylane/compiler/python_compiler/universe.py b/pennylane/compiler/python_compiler/universe.py
new file mode 100644
index 00000000000..fec6306945a
--- /dev/null
+++ b/pennylane/compiler/python_compiler/universe.py
@@ -0,0 +1,57 @@
+# Copyright 2025 Xanadu Quantum Technologies Inc.
+
+# 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.
+"""xDSL universe for containing all dialects and passes."""
+
+xdsl_available = True
+
+try:
+ from xdsl.passes import ModulePass
+ from xdsl.universe import Universe
+except (ImportError, ModuleNotFoundError):
+ xdsl_available = False # pragma: no cover
+
+# We must check that xDSL is installed because we're adding an entry point to
+# PennyLane that references this file, and we must ensure that PennyLane can
+# be installed in environments where xDSL is not installed.
+XDSL_UNIVERSE = None
+
+if xdsl_available:
+ # pylint: disable=import-outside-toplevel
+ from . import dialects, transforms
+
+ shared_dialects = ("stablehlo", "transform")
+
+ # Create a map from dialect names to dialect classes. Dialects that are already
+ # provided by xDSL cannot be loaded into the multiverse, so we don't add them to
+ # our universe.
+ names_to_dialects = {
+ d.name: d
+ for name in dialects.__all__
+ if (d := getattr(dialects, name)).name not in shared_dialects
+ }
+
+ # Create a map from pass names to their respective ModulePass. The transforms module
+ # contains PassDispatcher instances as well as ModulePasses. We only want to collect
+ # the ModulePasses. We cannot use issubclass with instances, which is why we first
+ # check if isinstance(transform, type).
+ names_to_passes = {
+ t.name: t
+ for name in transforms.__all__
+ if isinstance((t := getattr(transforms, name)), type) and issubclass(t, ModulePass)
+ }
+
+ # The Universe is used to expose custom dialects and transforms to xDSL. It is
+ # specified as an entry point in PennyLane's pyproject.toml file, which makes
+ # it available to look up by xDSL for tools such as xdsl-opt, xdsl-gui, etc.
+ XDSL_UNIVERSE = Universe(all_dialects=names_to_dialects, all_passes=names_to_passes)
diff --git a/pyproject.toml b/pyproject.toml
index 79b5ef5958a..0c631af5118 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -68,6 +68,9 @@ pl-device-test = "pennylane.devices.tests:cli"
"default.qutrit.mixed" = "pennylane.devices.default_qutrit_mixed:DefaultQutritMixed"
"default.tensor" = "pennylane.devices.default_tensor:DefaultTensor"
+[project.entry-points."xdsl.universe"]
+"pennylane-xdsl-universe" = "pennylane.compiler.python_compiler.universe:XDSL_UNIVERSE"
+
[project.entry-points."console_scripts"]
"pl-device-test" = "pennylane.devices.tests:cli"
diff --git a/tests/python_compiler/test_universe.py b/tests/python_compiler/test_universe.py
new file mode 100644
index 00000000000..b598eb01927
--- /dev/null
+++ b/tests/python_compiler/test_universe.py
@@ -0,0 +1,60 @@
+# Copyright 2025 Xanadu Quantum Technologies Inc.
+
+# 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.
+"""Unit tests for the xDSL universe."""
+
+import pytest
+
+pytestmark = pytest.mark.external
+pytest.importorskip("xdsl")
+
+# pylint: disable=wrong-import-position
+from xdsl.passes import ModulePass
+from xdsl.universe import Universe as xUniverse
+
+from pennylane.compiler.python_compiler import dialects, transforms
+from pennylane.compiler.python_compiler.universe import XDSL_UNIVERSE, shared_dialects
+
+all_dialects = tuple(getattr(dialects, name) for name in dialects.__all__)
+all_transforms = tuple(
+ transform
+ for name in transforms.__all__
+ if isinstance((transform := getattr(transforms, name)), type)
+ and issubclass(transform, ModulePass)
+)
+
+
+def test_correct_universe():
+ """Test that all the available dialects and transforms are available in the universe."""
+ for d in all_dialects:
+ if d.name not in shared_dialects:
+ assert d.name in XDSL_UNIVERSE.all_dialects
+ assert XDSL_UNIVERSE.all_dialects[d.name] == d
+
+ for t in all_transforms:
+ assert t.name in XDSL_UNIVERSE.all_passes
+ assert XDSL_UNIVERSE.all_passes[t.name] == t
+
+
+def test_correct_multiverse():
+ """Test that all the available dialects and transforms are available in the multiverse."""
+ multiverse = xUniverse.get_multiverse()
+
+ for d in all_dialects:
+ assert d.name in multiverse.all_dialects
+ if d.name not in shared_dialects:
+ assert multiverse.all_dialects[d.name] == d
+
+ for t in all_transforms:
+ assert t.name in multiverse.all_passes
+ assert multiverse.all_passes[t.name] == t