diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs
index 272b9bce17eff..8c60e22e20290 100644
--- a/crates/bevy_ecs/src/system/exclusive_function_system.rs
+++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs
@@ -6,7 +6,7 @@ use crate::{
     schedule::{SystemLabel, SystemLabelId},
     system::{
         check_system_change_tick, AsSystemLabel, ExclusiveSystemParam, ExclusiveSystemParamItem,
-        IntoSystem, System, SystemMeta, SystemTypeIdLabel,
+        In, InputMarker, IntoSystem, System, SystemMeta, SystemTypeIdLabel,
     },
     world::{World, WorldId},
 };
@@ -19,7 +19,7 @@ use std::{borrow::Cow, marker::PhantomData};
 /// [`ExclusiveSystemParam`]s.
 ///
 /// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
-pub struct ExclusiveFunctionSystem
+pub struct ExclusiveFunctionSystem
 where
     Param: ExclusiveSystemParam,
 {
@@ -28,18 +28,21 @@ where
     system_meta: SystemMeta,
     world_id: Option,
     // NOTE: PhantomData T> gives this safe Send/Sync impls
-    marker: PhantomData Marker>,
+    marker: PhantomData (Out, Marker)>,
 }
 
 pub struct IsExclusiveFunctionSystem;
 
-impl IntoSystem<(), (), (IsExclusiveFunctionSystem, Param, Marker)> for F
+impl IntoSystem
+    for F
 where
+    In: 'static,
+    Out: 'static,
     Param: ExclusiveSystemParam + 'static,
     Marker: 'static,
-    F: ExclusiveSystemParamFunction + Send + Sync + 'static,
+    F: ExclusiveSystemParamFunction + Send + Sync + 'static,
 {
-    type System = ExclusiveFunctionSystem;
+    type System = ExclusiveFunctionSystem;
     fn into_system(func: Self) -> Self::System {
         ExclusiveFunctionSystem {
             func,
@@ -53,14 +56,16 @@ where
 
 const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
 
-impl System for ExclusiveFunctionSystem
+impl System for ExclusiveFunctionSystem
 where
+    In: 'static,
+    Out: 'static,
     Param: ExclusiveSystemParam + 'static,
     Marker: 'static,
-    F: ExclusiveSystemParamFunction + Send + Sync + 'static,
+    F: ExclusiveSystemParamFunction + Send + Sync + 'static,
 {
-    type In = ();
-    type Out = ();
+    type In = In;
+    type Out = Out;
 
     #[inline]
     fn name(&self) -> Cow<'static, str> {
@@ -90,7 +95,7 @@ where
         panic!("Cannot run exclusive systems with a shared World reference");
     }
 
-    fn run(&mut self, _input: Self::In, world: &mut World) -> Self::Out {
+    fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
         let saved_last_tick = world.last_change_tick;
         world.last_change_tick = self.system_meta.last_change_tick;
 
@@ -98,12 +103,14 @@ where
             self.param_state.as_mut().expect(PARAM_MESSAGE),
             &self.system_meta,
         );
-        self.func.run(world, params);
+        let out = self.func.run(world, input, params);
 
         let change_tick = world.change_tick.get_mut();
         self.system_meta.last_change_tick = *change_tick;
         *change_tick += 1;
         world.last_change_tick = saved_last_tick;
+
+        out
     }
 
     #[inline]
@@ -147,8 +154,11 @@ where
     }
 }
 
-impl>
-    AsSystemLabel<(Param, Marker, IsExclusiveFunctionSystem)> for T
+impl AsSystemLabel<(In, Out, Param, Marker, IsExclusiveFunctionSystem)>
+    for T
+where
+    Param: ExclusiveSystemParam,
+    T: ExclusiveSystemParamFunction,
 {
     #[inline]
     fn as_system_label(&self) -> SystemLabelId {
@@ -160,38 +170,70 @@ impl:
+pub trait ExclusiveSystemParamFunction:
     Send + Sync + 'static
 {
-    fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem);
+    fn run(
+        &mut self,
+        world: &mut World,
+        input: In,
+        param_value: ExclusiveSystemParamItem,
+    ) -> Out;
 }
 
 macro_rules! impl_exclusive_system_function {
     ($($param: ident),*) => {
         #[allow(non_snake_case)]
-        impl ExclusiveSystemParamFunction<($($param,)*), ()> for Func
+        impl ExclusiveSystemParamFunction<(), Out, ($($param,)*), ()> for Func
         where
         for <'a> &'a mut Func:
-                FnMut(&mut World, $($param),*) +
-                FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*)
+                FnMut(&mut World, $($param),*) -> Out +
+                FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
+            Out: 'static,
         {
             #[inline]
-            fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem< ($($param,)*)>) {
+            fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
                 // Yes, this is strange, but `rustc` fails to compile this impl
                 // without using this function. It fails to recognise that `func`
                 // is a function, potentially because of the multiple impls of `FnMut`
                 #[allow(clippy::too_many_arguments)]
-                fn call_inner<$($param,)*>(
-                    mut f: impl FnMut(&mut World, $($param,)*),
+                fn call_inner(
+                    mut f: impl FnMut(&mut World, $($param,)*) -> Out,
                     world: &mut World,
                     $($param: $param,)*
-                ) {
+                ) -> Out {
                     f(world, $($param,)*)
                 }
                 let ($($param,)*) = param_value;
                 call_inner(self, world, $($param),*)
             }
         }
+        #[allow(non_snake_case)]
+        impl ExclusiveSystemParamFunction for Func
+        where
+        for <'a> &'a mut Func:
+                FnMut(In, &mut World, $($param),*) -> Out +
+                FnMut(In, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
+            Out: 'static,
+        {
+            #[inline]
+            fn run(&mut self, world: &mut World, input: Input, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
+                // Yes, this is strange, but `rustc` fails to compile this impl
+                // without using this function. It fails to recognise that `func`
+                // is a function, potentially because of the multiple impls of `FnMut`
+                #[allow(clippy::too_many_arguments)]
+                fn call_inner(
+                    mut f: impl FnMut(In, &mut World, $($param,)*) -> Out,
+                    input: Input,
+                    world: &mut World,
+                    $($param: $param,)*
+                ) -> Out {
+                    f(In(input), world, $($param,)*)
+                }
+                let ($($param,)*) = param_value;
+                call_inner(self, input, world, $($param),*)
+            }
+        }
     };
 }
 // Note that we rely on the highest impl to be <= the highest order of the tuple impls
diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs
index f99ae67b15a4a..050edf39b31b8 100644
--- a/crates/bevy_ecs/src/system/function_system.rs
+++ b/crates/bevy_ecs/src/system/function_system.rs
@@ -313,7 +313,7 @@ where
     world_id: Option,
     archetype_generation: ArchetypeGeneration,
     // NOTE: PhantomData T> gives this safe Send/Sync impls
-    marker: PhantomData (In, Out, Marker)>,
+    marker: PhantomData (Out, Marker)>,
 }
 
 pub struct IsFunctionSystem;
diff --git a/crates/bevy_ecs/src/system/system_piping.rs b/crates/bevy_ecs/src/system/system_piping.rs
index dd343461b9e5f..0af8caa5eed6e 100644
--- a/crates/bevy_ecs/src/system/system_piping.rs
+++ b/crates/bevy_ecs/src/system/system_piping.rs
@@ -433,8 +433,15 @@ pub mod adapter {
             unimplemented!()
         }
 
+        /// Mocks an exclusive system that takes an input and returns an output.
+        fn exclusive_in_out(_: In, _: &mut World) -> B {
+            unimplemented!()
+        }
+
         assert_is_system(returning::>.pipe(unwrap));
         assert_is_system(returning::