diff --git a/src/System.Collections/src/System/Collections/Generic/Stack.cs b/src/System.Collections/src/System/Collections/Generic/Stack.cs index e84e35b366c7..e474794e94db 100644 --- a/src/System.Collections/src/System/Collections/Generic/Stack.cs +++ b/src/System.Collections/src/System/Collections/Generic/Stack.cs @@ -202,22 +202,28 @@ public void TrimExcess() // is empty, Peek throws an InvalidOperationException. public T Peek() { - if (_size == 0) + int size = _size - 1; + T[] array = _array; + + if ((uint)size >= (uint)array.Length) { ThrowForEmptyStack(); } - return _array[_size - 1]; + return array[size]; } public bool TryPeek(out T result) { - if (_size == 0) + int size = _size - 1; + T[] array = _array; + + if ((uint)size >= (uint)array.Length) { - result = default(T); + result = default; return false; } - result = _array[_size - 1]; + result = array[size]; return true; } @@ -225,33 +231,44 @@ public bool TryPeek(out T result) // throws an InvalidOperationException. public T Pop() { - if (_size == 0) + int size = _size - 1; + T[] array = _array; + + // if (_size == 0) is equivalent to if (size == -1), and this case + // is covered with (uint)size, thus allowing bounds check elimination + // https://github.com/dotnet/coreclr/pull/9773 + if ((uint)size >= (uint)array.Length) { ThrowForEmptyStack(); } _version++; - T item = _array[--_size]; + _size = size; + T item = array[size]; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - _array[_size] = default(T); // Free memory quicker. + array[size] = default; // Free memory quicker. } return item; } public bool TryPop(out T result) { - if (_size == 0) + int size = _size - 1; + T[] array = _array; + + if ((uint)size >= (uint)array.Length) { - result = default(T); + result = default; return false; } _version++; - result = _array[--_size]; + _size = size; + result = array[size]; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - _array[_size] = default(T); // Free memory quicker. + array[size] = default; // Free memory quicker. } return true; } @@ -259,12 +276,29 @@ public bool TryPop(out T result) // Pushes an item to the top of the stack. public void Push(T item) { - if (_size == _array.Length) + int size = _size; + T[] array = _array; + + if ((uint)size < (uint)array.Length) { - Array.Resize(ref _array, (_array.Length == 0) ? DefaultCapacity : 2 * _array.Length); + array[size] = item; + _version++; + _size = size + 1; } - _array[_size++] = item; + else + { + PushWithResize(item); + } + } + + // Non-inline from Stack.Push to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private void PushWithResize(T item) + { + Array.Resize(ref _array, (_array.Length == 0) ? DefaultCapacity : 2 * _array.Length); + _array[_size] = item; _version++; + _size++; } // Copies the Stack to an array, in the same order Pop would return the items.