Repository: aptos-labs/aptos-core
File: third_party/move/move-vm/runtime/src/interpreter.rs (Rust)

Code before:
    types: Vec<Type>,
}

impl Debug for Stack {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(
            f,
            "values = \n  {}\n types = \n  {}\n",
            self.value.iter().map(|v| format!("{:?}", v)).join(", "),
            self.types.iter().map(|v| format!("{:?}", v)).join(", "),
        )
    }
}

impl Stack {
    /// Create a new empty operand stack.
    fn new() -> Self {
        Stack {
            value: vec![],
            types: vec![],
        }
    }

    /// Push a `Value` on the stack if the max stack size has not been reached. Abort execution
    /// otherwise.
    // note(maksim): increases function size 25%, DOES NOT improve performance, do not inline
    #[cfg_attr(feature = "force-inline", inline(always))]
    fn push(&mut self, value: Value) -> PartialVMResult<()> {
        if self.value.len() < OPERAND_STACK_SIZE_LIMIT {
            self.value.push(value);
            Ok(())
        } else {
            Err(PartialVMError::new(StatusCode::EXECUTION_STACK_OVERFLOW))
        }
    }

    /// Pop a `Value` off the stack or abort execution if the stack is empty.
    #[inline]
    fn pop(&mut self) -> PartialVMResult<Value> {
        self.value
            .pop()
            .ok_or_else(|| PartialVMError::new(StatusCode::EMPTY_VALUE_STACK))
    }

    /// Pop a `Value` of a given type off the stack. Abort if the value is not of the given
    /// type or if the stack is empty.
    // Note(inline): expensive to inline, adds a few seconds of compile time
    // Note(maksim): do not inline this, it bloats interpreter loop 20% and does not adds enough perf
    // to justify this, let `value_as()` be inlined instead
    #[cfg_attr(feature = "force-inline", inline(always))]
    fn pop_as<T>(&mut self) -> PartialVMResult<T>

Review comment (suggestion):
Interesting. Supposedly `push` is called very frequently from the interpreter loop. I'm also having a hard time imagining this expanding into a lot of code as it's a simple push (25% overall sounds insane)

Might be worth digging deeper and figure out what code it pulls in. Could it be the vector growth code? If that's the case, then we may be able to replace the vector with simple stack data structure that never grows. 

Code after:
            "values = \n  {}\n types = \n  {}\n",
            self.value.iter().map(|v| format!("{:?}", v)).join(", "),
            self.types.iter().map(|v| format!("{:?}", v)).join(", "),
        )
    }
}

impl Stack {
    /// Create a new empty operand stack.
    fn new() -> Self {
        Stack {
            value: vec![],
            types: vec![],
        }
    }

    /// Push a `Value` on the stack if the max stack size has not been reached. Abort execution
    /// otherwise.
    // note(inline): increases function size 25%, DOES NOT improve performance, do not inline.
    fn push(&mut self, value: Value) -> PartialVMResult<()> {
        if self.value.len() < OPERAND_STACK_SIZE_LIMIT {
            self.value.push(value);
            Ok(())
        } else {
            Err(PartialVMError::new(StatusCode::EXECUTION_STACK_OVERFLOW))
        }
    }

    /// Pop a `Value` off the stack or abort execution if the stack is empty.
    #[inline]
    fn pop(&mut self) -> PartialVMResult<Value> {
        self.value
            .pop()
            .ok_or_else(|| PartialVMError::new(StatusCode::EMPTY_VALUE_STACK))
    }

    /// Pop a `Value` of a given type off the stack. Abort if the value is not of the given
    /// type or if the stack is empty.
    // note(inline): do not inline this, it bloats interpreter loop 20% and does not adds enough perf to justify,
    // instead we're inlining `value_as()` and all VM casts.
    fn pop_as<T>(&mut self) -> PartialVMResult<T>
    where
        Value: VMValueCast<T>,
    {
        self.pop()?.value_as()
    }

    /// Pop n values off the stack.
    fn popn(&mut self, n: u16) -> PartialVMResult<Vec<Value>> {
        let remaining_stack_size = self
            .value