Browse Source

#3075: Add NextSetBit operation to BitSet to avoid looking at every store bit individually in ReachingDefinitionsVisitor.GetStores()

pull/3111/head
Siegfried Pammer 2 years ago
parent
commit
09691bd27e
  1. 16
      ICSharpCode.Decompiler.Tests/Util/BitSetTests.cs
  2. 17
      ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 50
      ICSharpCode.Decompiler/Util/BitOperations.cs
  5. 58
      ICSharpCode.Decompiler/Util/BitSet.cs

16
ICSharpCode.Decompiler.Tests/Util/BitSetTests.cs

@ -67,5 +67,21 @@ namespace ICSharpCode.Decompiler.Tests.Util @@ -67,5 +67,21 @@ namespace ICSharpCode.Decompiler.Tests.Util
bitset[200] = false;
Assert.IsFalse(bitset.All(1, 299));
}
[Test]
public void NextBitSet()
{
var bitset = new BitSet(300);
bitset.Set(0);
bitset.Set(2);
bitset.Set(3);
bitset.Set(130);
bitset.Set(135);
bitset.Set(150);
bitset.Set(190);
Assert.That(bitset.SetBits(0, 300), Is.EqualTo(new[] { 0, 2, 3, 130, 135, 150, 190 }));
Assert.That(bitset.SetBits(1, 5), Is.EqualTo(new[] { 2, 3 }));
Assert.That(bitset.SetBits(5, 132), Is.EqualTo(new[] { 130 }));
}
}
}

17
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs

@ -185,6 +185,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -185,6 +185,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
return bits[storeIndex];
}
public int NextReachingStore(int startIndex, int endIndex)
{
return bits.NextSetBit(startIndex, endIndex);
}
public void SetStore(int storeIndex)
{
Debug.Assert(storeIndex >= FirstStoreIndex);
@ -421,14 +426,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -421,14 +426,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected IEnumerable<ILInstruction> GetStores(State state, ILVariable v)
{
Debug.Assert(v.Function == scope && analyzedVariables[v.IndexInFunction]);
int startIndex = firstStoreIndexForVariable[v.IndexInFunction] + 1;
int endIndex = firstStoreIndexForVariable[v.IndexInFunction + 1];
for (int si = firstStoreIndexForVariable[v.IndexInFunction] + 1; si < endIndex; si++)
while (startIndex < endIndex)
{
if (state.IsReachingStore(si))
int nextReachingStore = state.NextReachingStore(startIndex, endIndex);
if (nextReachingStore == -1)
{
Debug.Assert(((IInstructionWithVariableOperand)allStores[si]).Variable == v);
yield return allStores[si];
break;
}
Debug.Assert(((IInstructionWithVariableOperand)allStores[nextReachingStore]).Variable == v);
yield return allStores[nextReachingStore];
startIndex = nextReachingStore + 1;
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -140,6 +140,7 @@ @@ -140,6 +140,7 @@
<Compile Include="Metadata\ReferenceLoadInfo.cs" />
<Compile Include="Properties\DecompilerVersionInfo.cs" />
<Compile Include="TypeSystem\ITypeDefinitionOrUnknown.cs" />
<Compile Include="Util\BitOperations.cs" />
<Compile Include="Util\Index.cs" />
<None Include="Properties\DecompilerVersionInfo.template.cs" />
<Compile Include="Semantics\OutVarResolveResult.cs" />

50
ICSharpCode.Decompiler/Util/BitOperations.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ICSharpCode.Decompiler.Util
{
internal class BitOperations
{
private static ReadOnlySpan<byte> TrailingZeroCountDeBruijn => new byte[32]
{
00, 01, 28, 02, 29, 14, 24, 03,
30, 22, 20, 15, 25, 17, 04, 08,
31, 27, 13, 23, 21, 19, 16, 07,
26, 12, 18, 06, 11, 05, 10, 09
};
public static int TrailingZeroCount(uint value)
{
// Unguarded fallback contract is 0->0, BSF contract is 0->undefined
if (value == 0)
{
return 32;
}
unchecked
{
// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
return Unsafe.AddByteOffset(
// Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u
ref MemoryMarshal.GetReference(TrailingZeroCountDeBruijn),
// uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
(IntPtr)(int)(((value & (uint)-(int)value) * 0x077CB531u) >> 27)); // Multi-cast mitigates redundant conv.u8
}
}
public static int TrailingZeroCount(ulong value)
{
unchecked
{
uint lo = (uint)value;
if (lo == 0)
{
return 32 + TrailingZeroCount((uint)(value >> 32));
}
return TrailingZeroCount(lo);
}
}
}
}

58
ICSharpCode.Decompiler/Util/BitSet.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
@ -268,6 +269,63 @@ namespace ICSharpCode.Decompiler.Util @@ -268,6 +269,63 @@ namespace ICSharpCode.Decompiler.Util
}
}
public int NextSetBit(int startIndex, int endIndex)
{
Debug.Assert(startIndex <= endIndex);
if (startIndex >= endIndex)
{
return -1;
}
int startWordIndex = WordIndex(startIndex);
int endWordIndex = WordIndex(endIndex - 1);
ulong startMask = Mask << startIndex;
ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64)))
if (startWordIndex == endWordIndex)
{
ulong maskedWord = words[startWordIndex] & startMask & endMask;
if (maskedWord != 0)
{
return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord);
}
}
else
{
ulong maskedWord = words[startWordIndex] & startMask;
if (maskedWord != 0)
{
return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord);
}
for (int i = startWordIndex + 1; i < endWordIndex; i++)
{
maskedWord = words[i];
if (maskedWord != 0)
{
return i * 64 + BitOperations.TrailingZeroCount(maskedWord);
}
}
maskedWord = words[endWordIndex] & endMask;
if (maskedWord != 0)
{
return endWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord);
}
}
return -1;
}
public IEnumerable<int> SetBits(int startIndex, int endIndex)
{
while (true)
{
int next = NextSetBit(startIndex, endIndex);
if (next == -1)
break;
yield return next;
startIndex = next + 1;
}
}
public void ReplaceWith(BitSet incoming)
{
Debug.Assert(words.Length == incoming.words.Length);

Loading…
Cancel
Save