未验证 提交 59e21bf2 编写于 作者: N Neal Gafter 提交者: GitHub

Simplify the representation of `NullableWalker.LocalState` to a `BitVector` (#34769)

Fixes #34766

Previously, every instance of `LocalState` required allocation.  Now we use `BitVector`, which stores state information inline in the bits of a primitive.

With the previous implementation of `BitVector` (32 bits inline), only about 1.3% of our nullable tests require allocating any memory at all for nullable states.

However, I've updated `BitVector` to have 64 bits inline, and now only three of our tests require allocating memory for states.  Those three tests heavily exercise tuple conversions.
上级 1ca1a4cb
......@@ -1066,22 +1066,20 @@ private TypeSymbol GetSlotType(int slot)
protected override LocalState TopState()
{
var state = new LocalState(reachable: true, new ArrayBuilder<NullableFlowState>(nextVariableSlot));
var state = LocalState.ReachableState(capacity: nextVariableSlot);
Populate(ref state, start: 0);
return state;
}
protected override LocalState UnreachableState()
{
return new LocalState(reachable: false, null);
return LocalState.UnreachableState;
}
protected override LocalState ReachableBottomState()
{
// Create a reachable state in which all variables are known to be non-null.
var builder = new ArrayBuilder<NullableFlowState>(nextVariableSlot);
builder.AddMany(NullableFlowState.NotNull, nextVariableSlot);
return new LocalState(reachable: true, builder);
return LocalState.ReachableState(capacity: nextVariableSlot);
}
private void EnterParameters()
......@@ -5852,17 +5850,7 @@ protected override void Meet(ref LocalState self, ref LocalState other)
Normalize(ref other);
}
for (int slot = 1; slot < self.Capacity; slot++)
{
NullableFlowState selfState = self[slot];
NullableFlowState otherState = other[slot];
NullableFlowState union = selfState.Meet(otherState);
if (selfState != union)
{
self[slot] = union;
}
}
self.Meet(in other);
}
protected override bool Join(ref LocalState self, ref LocalState other)
......@@ -5876,26 +5864,13 @@ protected override bool Join(ref LocalState self, ref LocalState other)
return true;
}
bool result = false;
if (self.Capacity != other.Capacity)
{
Normalize(ref self);
Normalize(ref other);
}
for (int slot = 1; slot < self.Capacity; slot++)
{
NullableFlowState selfAnnotation = self[slot];
NullableFlowState intersection = selfAnnotation.Join(other[slot]);
if (selfAnnotation != intersection)
{
self[slot] = intersection;
result = true;
}
}
return result;
return self.Join(in other);
}
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
......@@ -5905,79 +5880,55 @@ internal class LocalState : ILocalState
internal struct LocalState : ILocalState
#endif
{
private ArrayBuilder<NullableFlowState> _state;
public bool Reachable { get; }
// The representation of a state is a bit vector. We map false<->NotNull and true<->MayBeNull.
// Slot 0 is used to represent whether the state is reachable (true) or not.
private BitVector _state;
internal LocalState(bool reachable, ArrayBuilder<NullableFlowState> state)
{
this.Reachable = reachable;
this._state = state;
}
private LocalState(BitVector state) => this._state = state;
internal int Capacity => _state?.Count ?? 0;
public bool Reachable => _state[0];
internal void EnsureCapacity(int capacity)
public static LocalState ReachableState(int capacity)
{
if (!Reachable)
{
return;
}
if (capacity < 1)
capacity = 1;
if (_state == null)
{
_state = new ArrayBuilder<NullableFlowState>(capacity);
}
BitVector state = BitVector.Create(capacity);
state[0] = true;
return new LocalState(state);
}
if (_state.Count < capacity)
public static LocalState UnreachableState
{
get
{
_state.Count = capacity;
BitVector state = BitVector.Create(1);
state[0] = false;
return new LocalState(state);
}
}
internal NullableFlowState this[int slot]
public int Capacity => _state.Capacity;
public void EnsureCapacity(int capacity) => _state.EnsureCapacity(capacity);
public NullableFlowState this[int slot]
{
get
{
if (slot < Capacity && this.Reachable)
{
return _state[slot];
}
get => (slot < Capacity && this.Reachable && _state[slot]) ? NullableFlowState.MaybeNull : NullableFlowState.NotNull;
return NullableFlowState.NotNull;
}
set
{
if (this.Reachable)
{
// All variables are be considered not null in unreachable code.
// Moreover, no states should be modified in unreachable code, as there is only one unreachable state.
EnsureCapacity(slot + 1);
_state[slot] = value;
}
}
// No states should be modified in unreachable code, as there is only one unreachable state.
set => _ = !this.Reachable || (_state[slot] = (value == NullableFlowState.MaybeNull));
}
/// <summary>
/// Produce a duplicate of this flow analysis state.
/// </summary>
/// <returns></returns>
public LocalState Clone()
{
ArrayBuilder<NullableFlowState> clone;
public LocalState Clone() => new LocalState(_state.Clone());
if (_state == null)
{
clone = null;
}
else
{
clone = new ArrayBuilder<NullableFlowState>(_state.Count);
clone.Count = 0;
clone.AddRange(_state);
}
public bool Join(in LocalState other) => _state.UnionWith(in other._state);
return new LocalState(Reachable, clone);
}
public bool Meet(in LocalState other) => _state.IntersectWith(in other._state);
internal string GetDebuggerDisplay()
{
......@@ -5985,23 +5936,7 @@ internal string GetDebuggerDisplay()
var builder = pooledBuilder.Builder;
builder.Append(" ");
for (int i = this.Capacity - 1; i >= 0; i--)
{
string append;
switch (_state[i])
{
case NullableFlowState.NotNull:
append = "!";
break;
case NullableFlowState.MaybeNull:
append = "?";
break;
default:
throw ExceptionUtilities.UnexpectedValue(_state[i]);
}
builder.Append(append);
}
builder.Append(_state[i] ? '?' : '!');
return pooledBuilder.ToStringAndFree();
}
......
......@@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using Roslyn.Utilities;
using Word = System.UInt32;
using Word = System.UInt64;
namespace Microsoft.CodeAnalysis
{
......@@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis
internal struct BitVector : IEquatable<BitVector>
{
private const Word ZeroWord = 0;
private const int Log2BitsPerWord = 5;
private const int Log2BitsPerWord = 6;
public const int BitsPerWord = 1 << Log2BitsPerWord;
......@@ -38,10 +38,11 @@ private BitVector(Word bits0, Word[] bits, int capacity)
public bool Equals(BitVector other)
{
// Bit arrays only equal if their underlying sets are of the same size.
// Bit arrays only equal if their underlying sets are of the same size
return _capacity == other._capacity
// and have the same set of bits set
&& _bits0 == other._bits0
&& _bits.ValueEquals(other._bits);
&& _bits.AsSpan().SequenceEqual(other._bits.AsSpan());
}
public override bool Equals(object obj)
......@@ -108,7 +109,7 @@ public IEnumerable<Word> Words()
yield return _bits0;
}
for (int i = 0; i < _bits?.Length; i++)
for (int i = 0, n = _bits?.Length ?? 0; i < n; i++)
{
yield return _bits[i];
}
......
......@@ -24,29 +24,6 @@ internal static T[] Copy<T>(this T[] array, int start, int length)
return newArray;
}
internal static bool ValueEquals(this uint[] array, uint[] other)
{
if (array == other)
{
return true;
}
if (array == null || other == null || array.Length != other.Length)
{
return false;
}
for (int i = 0; i < array.Length; i++)
{
if (array[i] != other[i])
{
return false;
}
}
return true;
}
internal static T[] InsertAt<T>(this T[] array, int position, T item)
{
T[] newArray = new T[array.Length + 1];
......
......@@ -443,12 +443,15 @@ private Array ReadPrimitiveTypeArrayElements(Type type, EncodingKind kind, int l
private bool[] ReadBooleanArrayElements(bool[] array)
{
// Confirm the type to be read below is ulong
Debug.Assert(BitVector.BitsPerWord == 64);
var wordLength = BitVector.WordsRequired(array.Length);
var count = 0;
for (var i = 0; i < wordLength; i++)
{
var word = _reader.ReadUInt32();
var word = _reader.ReadUInt64();
for (var p = 0; p < BitVector.BitsPerWord; p++)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册