From d2634478781be741c872f33c7793797c081dc16f Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Wed, 27 Jan 2021 10:24:31 +0100
Subject: [PATCH] Replace EmptySinglePair with SmallVec

---
 Cargo.lock           |   1 +
 Cargo.toml           |   1 +
 src/abi/comments.rs  |  17 ++++--
 src/abi/mod.rs       |   7 ++-
 src/abi/pass_mode.rs | 128 ++++++++++++++-----------------------------
 src/abi/returning.rs |  12 ++--
 6 files changed, 66 insertions(+), 100 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 431e8068696..5495cfa5eaa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -333,6 +333,7 @@ dependencies = [
  "indexmap",
  "libloading",
  "object",
+ "smallvec",
  "target-lexicon",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 4558da2de73..3820fce6d1e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ object = { version = "0.22.0", default-features = false, features = ["std", "rea
 ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
 indexmap = "1.0.2"
 libloading = { version = "0.6.0", optional = true }
+smallvec = "1.6.1"
 
 # Uncomment to use local checkout of cranelift
 #[patch."https://github.com/bytecodealliance/wasmtime/"]
diff --git a/src/abi/comments.rs b/src/abi/comments.rs
index 41cb4c627f8..9aab45b62e2 100644
--- a/src/abi/comments.rs
+++ b/src/abi/comments.rs
@@ -8,7 +8,6 @@ use rustc_target::abi::call::PassMode;
 
 use cranelift_codegen::entity::EntityRef;
 
-use crate::abi::pass_mode::*;
 use crate::prelude::*;
 
 pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) {
@@ -22,7 +21,7 @@ pub(super) fn add_arg_comment<'tcx>(
     kind: &str,
     local: Option<mir::Local>,
     local_field: Option<usize>,
-    params: EmptySinglePair<Value>,
+    params: &[Value],
     arg_abi_mode: PassMode,
     arg_layout: TyAndLayout<'tcx>,
 ) {
@@ -38,9 +37,17 @@ pub(super) fn add_arg_comment<'tcx>(
     };
 
     let params = match params {
-        Empty => Cow::Borrowed("-"),
-        Single(param) => Cow::Owned(format!("= {:?}", param)),
-        Pair(param_a, param_b) => Cow::Owned(format!("= {:?}, {:?}", param_a, param_b)),
+        [] => Cow::Borrowed("-"),
+        [param] => Cow::Owned(format!("= {:?}", param)),
+        [param_a, param_b] => Cow::Owned(format!("= {:?},{:?}", param_a, param_b)),
+        params => Cow::Owned(format!(
+            "= {}",
+            params
+                .iter()
+                .map(ToString::to_string)
+                .collect::<Vec<_>>()
+                .join(",")
+        )),
     };
 
     let pass_mode = format!("{:?}", arg_abi_mode);
diff --git a/src/abi/mod.rs b/src/abi/mod.rs
index 55ebd39e3f1..bc35ca2de40 100644
--- a/src/abi/mod.rs
+++ b/src/abi/mod.rs
@@ -11,6 +11,7 @@ use rustc_target::abi::call::{Conv, FnAbi};
 use rustc_target::spec::abi::Abi;
 
 use cranelift_codegen::ir::AbiParam;
+use smallvec::smallvec;
 
 use self::pass_mode::*;
 use crate::prelude::*;
@@ -534,7 +535,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
                 );
             }
             let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx);
-            (Some(method), Single(ptr))
+            (Some(method), smallvec![ptr])
         }
 
         // Normal call
@@ -542,7 +543,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
             None,
             args.get(0)
                 .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0]))
-                .unwrap_or(Empty),
+                .unwrap_or(smallvec![]),
         ),
 
         // Indirect call
@@ -557,7 +558,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
                 Some(func),
                 args.get(0)
                     .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0]))
-                    .unwrap_or(Empty),
+                    .unwrap_or(smallvec![]),
             )
         }
     };
diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs
index e2b78bfeac0..e047ddcebc9 100644
--- a/src/abi/pass_mode.rs
+++ b/src/abi/pass_mode.rs
@@ -5,78 +5,24 @@ use crate::value_and_place::assert_assignable;
 
 use cranelift_codegen::ir::ArgumentPurpose;
 use rustc_target::abi::call::{ArgAbi, PassMode};
-pub(super) use EmptySinglePair::*;
-
-#[derive(Copy, Clone, Debug)]
-pub(super) enum EmptySinglePair<T> {
-    Empty,
-    Single(T),
-    Pair(T, T),
-}
-
-impl<T> EmptySinglePair<T> {
-    pub(super) fn into_iter(self) -> EmptySinglePairIter<T> {
-        EmptySinglePairIter(self)
-    }
-
-    pub(super) fn map<U>(self, mut f: impl FnMut(T) -> U) -> EmptySinglePair<U> {
-        match self {
-            Empty => Empty,
-            Single(v) => Single(f(v)),
-            Pair(a, b) => Pair(f(a), f(b)),
-        }
-    }
-}
-
-pub(super) struct EmptySinglePairIter<T>(EmptySinglePair<T>);
-
-impl<T> Iterator for EmptySinglePairIter<T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<T> {
-        match std::mem::replace(&mut self.0, Empty) {
-            Empty => None,
-            Single(v) => Some(v),
-            Pair(a, b) => {
-                self.0 = Single(b);
-                Some(a)
-            }
-        }
-    }
-}
-
-impl<T: std::fmt::Debug> EmptySinglePair<T> {
-    pub(super) fn assert_single(self) -> T {
-        match self {
-            Single(v) => v,
-            _ => panic!("Called assert_single on {:?}", self),
-        }
-    }
-
-    pub(super) fn assert_pair(self) -> (T, T) {
-        match self {
-            Pair(a, b) => (a, b),
-            _ => panic!("Called assert_pair on {:?}", self),
-        }
-    }
-}
+use smallvec::{smallvec, SmallVec};
 
 pub(super) trait ArgAbiExt<'tcx> {
-    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> EmptySinglePair<AbiParam>;
+    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
     fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
 }
 
 impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
-    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> EmptySinglePair<AbiParam> {
+    fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> {
         match self.mode {
-            PassMode::Ignore => EmptySinglePair::Empty,
+            PassMode::Ignore => smallvec![],
             PassMode::Direct(_) => match &self.layout.abi {
                 Abi::Scalar(scalar) => {
-                    EmptySinglePair::Single(AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())))
+                    smallvec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))]
                 }
                 Abi::Vector { .. } => {
                     let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
-                    EmptySinglePair::Single(AbiParam::new(vector_ty))
+                    smallvec![AbiParam::new(vector_ty)]
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
@@ -84,11 +30,11 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 Abi::ScalarPair(a, b) => {
                     let a = scalar_to_clif_type(tcx, a.clone());
                     let b = scalar_to_clif_type(tcx, b.clone());
-                    EmptySinglePair::Pair(AbiParam::new(a), AbiParam::new(b))
+                    smallvec![AbiParam::new(a), AbiParam::new(b)]
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            PassMode::Cast(_) => EmptySinglePair::Single(AbiParam::new(pointer_ty(tcx))),
+            PassMode::Cast(_) => smallvec![AbiParam::new(pointer_ty(tcx))],
             PassMode::Indirect {
                 attrs: _,
                 extra_attrs: None,
@@ -96,12 +42,12 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
             } => {
                 if on_stack {
                     let size = u32::try_from(self.layout.size.bytes()).unwrap();
-                    EmptySinglePair::Single(AbiParam::special(
+                    smallvec![AbiParam::special(
                         pointer_ty(tcx),
                         ArgumentPurpose::StructArgument(size),
-                    ))
+                    )]
                 } else {
-                    EmptySinglePair::Single(AbiParam::new(pointer_ty(tcx)))
+                    smallvec![AbiParam::new(pointer_ty(tcx))]
                 }
             }
             PassMode::Indirect {
@@ -110,10 +56,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 on_stack,
             } => {
                 assert!(!on_stack);
-                EmptySinglePair::Pair(
+                smallvec![
                     AbiParam::new(pointer_ty(tcx)),
                     AbiParam::new(pointer_ty(tcx)),
-                )
+                ]
             }
         }
     }
@@ -176,18 +122,18 @@ pub(super) fn adjust_arg_for_abi<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
     arg: CValue<'tcx>,
     arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
-) -> EmptySinglePair<Value> {
+) -> SmallVec<[Value; 2]> {
     assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty);
     match arg_abi.mode {
-        PassMode::Ignore => Empty,
-        PassMode::Direct(_) => Single(arg.load_scalar(fx)),
+        PassMode::Ignore => smallvec![],
+        PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
         PassMode::Pair(_, _) => {
             let (a, b) = arg.load_scalar_pair(fx);
-            Pair(a, b)
+            smallvec![a, b]
         }
         PassMode::Cast(_) | PassMode::Indirect { .. } => match arg.force_stack(fx) {
-            (ptr, None) => Single(ptr.get_addr(fx)),
-            (ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta),
+            (ptr, None) => smallvec![ptr.get_addr(fx)],
+            (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta],
         },
     }
 }
@@ -202,8 +148,10 @@ pub(super) fn cvalue_for_param<'tcx>(
     arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
 ) -> Option<CValue<'tcx>> {
     let clif_types = arg_abi.get_abi_param(fx.tcx);
-    let block_params =
-        clif_types.map(|abi_param| fx.bcx.append_block_param(start_block, abi_param.value_type));
+    let block_params = clif_types
+        .into_iter()
+        .map(|abi_param| fx.bcx.append_block_param(start_block, abi_param.value_type))
+        .collect::<SmallVec<[_; 2]>>();
 
     #[cfg(debug_assertions)]
     crate::abi::comments::add_arg_comment(
@@ -211,7 +159,7 @@ pub(super) fn cvalue_for_param<'tcx>(
         "arg",
         local,
         local_field,
-        block_params,
+        &block_params,
         arg_abi.mode,
         arg_abi.layout,
     );
@@ -219,30 +167,38 @@ pub(super) fn cvalue_for_param<'tcx>(
     match arg_abi.mode {
         PassMode::Ignore => None,
         PassMode::Direct(_) => {
-            Some(CValue::by_val(block_params.assert_single(), arg_abi.layout))
+            assert_eq!(block_params.len(), 1, "{:?}", block_params);
+            Some(CValue::by_val(block_params[0], arg_abi.layout))
         }
         PassMode::Pair(_, _) => {
-            let (a, b) = block_params.assert_pair();
-            Some(CValue::by_val_pair(a, b, arg_abi.layout))
+            assert_eq!(block_params.len(), 2, "{:?}", block_params);
+            Some(CValue::by_val_pair(
+                block_params[0],
+                block_params[1],
+                arg_abi.layout,
+            ))
         }
         PassMode::Cast(_)
         | PassMode::Indirect {
             attrs: _,
             extra_attrs: None,
             on_stack: _,
-        } => Some(CValue::by_ref(
-            Pointer::new(block_params.assert_single()),
-            arg_abi.layout,
-        )),
+        } => {
+            assert_eq!(block_params.len(), 1, "{:?}", block_params);
+            Some(CValue::by_ref(
+                Pointer::new(block_params[0]),
+                arg_abi.layout,
+            ))
+        }
         PassMode::Indirect {
             attrs: _,
             extra_attrs: Some(_),
             on_stack: _,
         } => {
-            let (ptr, meta) = block_params.assert_pair();
+            assert_eq!(block_params.len(), 2, "{:?}", block_params);
             Some(CValue::by_ref_unsized(
-                Pointer::new(ptr),
-                meta,
+                Pointer::new(block_params[0]),
+                block_params[1],
                 arg_abi.layout,
             ))
         }
diff --git a/src/abi/returning.rs b/src/abi/returning.rs
index d7a82e0c377..8376f845734 100644
--- a/src/abi/returning.rs
+++ b/src/abi/returning.rs
@@ -1,10 +1,10 @@
 //! Return value handling
 
-use crate::abi::pass_mode::*;
 use crate::prelude::*;
 
 use rustc_middle::ty::layout::FnAbiExt;
 use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
+use smallvec::{SmallVec, smallvec};
 
 /// Can the given type be returned into an ssa var or does it need to be returned on the stack.
 pub(crate) fn can_return_to_ssa_var<'tcx>(
@@ -62,10 +62,10 @@ pub(super) fn codegen_return_param<'tcx>(
     ssa_analyzed: &rustc_index::vec::IndexVec<Local, crate::analyze::SsaKind>,
     start_block: Block,
 ) -> CPlace<'tcx> {
-    let (ret_place, ret_param) = match fx.fn_abi.as_ref().unwrap().ret.mode {
+    let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode {
         PassMode::Ignore => (
             CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout),
-            Empty,
+            smallvec![],
         ),
         PassMode::Direct(_) | PassMode::Pair(_, _) => {
             let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa;
@@ -76,7 +76,7 @@ pub(super) fn codegen_return_param<'tcx>(
                     fx.fn_abi.as_ref().unwrap().ret.layout,
                     is_ssa,
                 ),
-                Empty,
+                smallvec![],
             )
         }
         PassMode::Cast(_)
@@ -91,7 +91,7 @@ pub(super) fn codegen_return_param<'tcx>(
                     Pointer::new(ret_param),
                     fx.fn_abi.as_ref().unwrap().ret.layout,
                 ),
-                Single(ret_param),
+                smallvec![ret_param],
             )
         }
         PassMode::Indirect {
@@ -110,7 +110,7 @@ pub(super) fn codegen_return_param<'tcx>(
         "ret",
         Some(RETURN_PLACE),
         None,
-        ret_param,
+        &ret_param,
         fx.fn_abi.as_ref().unwrap().ret.mode,
         fx.fn_abi.as_ref().unwrap().ret.layout,
     );