Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
  • Loading branch information
MichalStrehovsky committed Jun 21, 2024
commit e84d58c036b4b5a6669a0e0988d32518d562568d
4 changes: 4 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ private void MarkBasicBlock(BasicBlock basicBlock)
}
}

partial void StartImportingInstruction(ILOpcode opcode);

private void ImportBasicBlock(BasicBlock basicBlock)
{
_currentBasicBlock = basicBlock;
Expand All @@ -325,6 +327,8 @@ private void ImportBasicBlock(BasicBlock basicBlock)

ILOpcode opCode = (ILOpcode)ReadILByte();

StartImportingInstruction(opCode);

again:
switch (opCode)
{
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/tools/Common/TypeSystem/IL/ILReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public ILOpcode ReadILOpcode()
return opcode;
}

public ILOpcode PeekILOpcode()
public readonly ILOpcode PeekILOpcode()
{
ILOpcode opcode = (ILOpcode)_ilBytes[_currentOffset];
if (opcode == ILOpcode.prefix1)
Expand All @@ -113,6 +113,14 @@ public ILOpcode PeekILOpcode()
return opcode;
}

public readonly int PeekILToken()
{
if (!BinaryPrimitives.TryReadInt32LittleEndian(_ilBytes.Slice(_currentOffset), out int value))
ThrowHelper.ThrowInvalidProgramException();

return value;
}

public void Skip(ILOpcode opcode)
{
if (!opcode.IsValid())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,6 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
}
}

var analyzer = new ILPatternAnalyzer<ILPatternAnalyzerState>(new ILPatternAnalyzerState(flags), method, methodBytes);

bool hasGetResourceStringCall = false;

// Mark all reachable basic blocks
Expand All @@ -254,13 +252,17 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
if ((flags[offset] & OpcodeFlags.Mark) != 0)
continue;

TypeEqualityPatternAnalyzer typeEqualityAnalyzer = default;

ILReader reader = new ILReader(methodBytes, offset);
while (reader.HasNext)
{
offset = reader.Offset;
flags[offset] |= OpcodeFlags.Mark;
ILOpcode opcode = reader.ReadILOpcode();

typeEqualityAnalyzer.Advance(opcode, reader, method);

// Mark any applicable EH blocks
foreach (ILExceptionRegion ehRegion in ehRegions)
{
Expand Down Expand Up @@ -299,7 +301,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
|| opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s)
{
int destination = reader.ReadBranchDestination(opcode);
if (!TryGetConstantArgument(analyzer, offset, 0, out int constant))
if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int constant)
&& !TryExpandTypeEquality(typeEqualityAnalyzer, method, out constant))
{
// Can't get the constant - both branches are live.
offsetsToVisit.Push(destination);
Expand All @@ -325,8 +328,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
|| opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s)
{
int destination = reader.ReadBranchDestination(opcode);
if (!TryGetConstantArgument(analyzer, offset, 0, out int left)
|| !TryGetConstantArgument(analyzer, offset, 1, out int right))
if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int left)
|| !TryGetConstantArgument(method, methodBytes, flags, offset, 1, out int right))
{
// Can't get the constant - both branches are live.
offsetsToVisit.Push(destination);
Expand Down Expand Up @@ -656,24 +659,24 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
return new SubstitutedMethodIL(method.GetMethodILDefinition(), newBody, newEHRegions.ToArray(), debugInfo, newStrings.ToArray());
}

private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, int argIndex, out int constant)
private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, int argIndex, out int constant)
{
if (analyzer.State.IsBasicBlockStart(offset))
if ((flags[offset] & OpcodeFlags.BasicBlockStart) != 0)
{
constant = 0;
return false;
}

for (int currentOffset = offset - 1; currentOffset >= 0; currentOffset--)
{
if (!analyzer.State.IsInstructionStart(currentOffset))
if ((flags[currentOffset] & OpcodeFlags.InstructionStart) == 0)
continue;

ILReader reader = new ILReader(analyzer.ILBytes, currentOffset);
ILReader reader = new ILReader(body, currentOffset);
ILOpcode opcode = reader.ReadILOpcode();
if (opcode == ILOpcode.call || opcode == ILOpcode.callvirt)
{
MethodDesc method = (MethodDesc)analyzer.Method.GetObject(reader.ReadILToken());
MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken());
if (argIndex == 0)
{
BodySubstitution substitution = _substitutionProvider.GetSubstitution(method);
Expand All @@ -683,17 +686,10 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an
constant = (int)substitution.Value;
return true;
}
else if (method.IsIntrinsic && method.Name is "op_Inequality" or "op_Equality"
&& method.OwningType is MetadataType mdType
&& mdType.Name == "Type" && mdType.Namespace == "System" && mdType.Module == mdType.Context.SystemModule
&& TryExpandTypeEquality(analyzer, currentOffset, method.Name, out constant))
{
return true;
}
else if (method.IsIntrinsic && method.Name is "get_IsValueType" or "get_IsEnum"
&& method.OwningType is MetadataType mdt
&& mdt.Name == "Type" && mdt.Namespace == "System" && mdt.Module == mdt.Context.SystemModule
&& TryExpandTypeIs(analyzer, currentOffset, method.Name, out constant))
&& TryExpandTypeIs(methodIL, body, flags, currentOffset, method.Name, out constant))
{
return true;
}
Expand All @@ -714,7 +710,7 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an
}
else if (opcode == ILOpcode.ldsfld)
{
FieldDesc field = (FieldDesc)analyzer.Method.GetObject(reader.ReadILToken());
FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
if (argIndex == 0)
{
object substitution = _substitutionProvider.GetSubstitution(field);
Expand Down Expand Up @@ -764,7 +760,7 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an
}
else if ((opcode == ILOpcode.ldloc || opcode == ILOpcode.ldloc_s ||
(opcode >= ILOpcode.ldloc_0 && opcode <= ILOpcode.ldloc_3)) &&
!analyzer.State.IsBasicBlockStart(currentOffset))
((flags[currentOffset] & OpcodeFlags.BasicBlockStart) == 0))
{
// Paired stloc/ldloc that the C# compiler generates in debug code?
int locIndex = opcode switch
Expand All @@ -776,10 +772,10 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an

for (int potentialStlocOffset = currentOffset - 1; potentialStlocOffset >= 0; potentialStlocOffset--)
{
if (!analyzer.State.IsInstructionStart(potentialStlocOffset))
if ((flags[potentialStlocOffset] & OpcodeFlags.InstructionStart) == 0)
continue;

ILReader nestedReader = new ILReader(analyzer.ILBytes, potentialStlocOffset);
ILReader nestedReader = new ILReader(body, potentialStlocOffset);
ILOpcode otherOpcode = nestedReader.ReadILOpcode();
if ((otherOpcode == ILOpcode.stloc || otherOpcode == ILOpcode.stloc_s ||
(otherOpcode >= ILOpcode.stloc_0 && otherOpcode <= ILOpcode.stloc_3))
Expand All @@ -805,8 +801,8 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an
{
if (argIndex == 0)
{
if (!TryGetConstantArgument(analyzer, currentOffset, 0, out int left)
|| !TryGetConstantArgument(analyzer, currentOffset, 1, out int right))
if (!TryGetConstantArgument(methodIL, body, flags, currentOffset, 0, out int left)
|| !TryGetConstantArgument(methodIL, body, flags, currentOffset, 1, out int right))
{
constant = 0;
return false;
Expand All @@ -828,15 +824,15 @@ private bool TryGetConstantArgument(ILPatternAnalyzer<ILPatternAnalyzerState> an
return false;
}

if (analyzer.State.IsBasicBlockStart(currentOffset))
if ((flags[currentOffset] & OpcodeFlags.BasicBlockStart) != 0)
break;
}

constant = 0;
return false;
}

private static bool TryExpandTypeIs(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, string name, out int constant)
private static bool TryExpandTypeIs(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, string name, out int constant)
{
// We expect to see a sequence:
// ldtoken Foo
Expand All @@ -847,16 +843,16 @@ private static bool TryExpandTypeIs(ILPatternAnalyzer<ILPatternAnalyzerState> an
if (offset < SequenceLength)
return false;

if (!analyzer.State.IsInstructionStart(offset - SequenceLength))
if ((flags[offset - SequenceLength] & OpcodeFlags.InstructionStart) == 0)
return false;

ILReader reader = new ILReader(analyzer.ILBytes, offset - SequenceLength);
ILReader reader = new ILReader(body, offset - SequenceLength);

TypeDesc type = ReadLdToken(ref reader, analyzer);
TypeDesc type = ReadLdToken(ref reader, methodIL, flags);
if (type == null)
return false;

if (!ReadGetTypeFromHandle(ref reader, analyzer))
if (!ReadGetTypeFromHandle(ref reader, methodIL, flags))
return false;

// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
Expand All @@ -875,103 +871,92 @@ private static bool TryExpandTypeIs(ILPatternAnalyzer<ILPatternAnalyzerState> an
return true;
}

private bool TryExpandTypeEquality(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, string op, out int constant)
{
if (TryExpandTypeEquality_TokenToken(analyzer, offset, out constant)
|| TryExpandTypeEquality_TokenOther(analyzer, offset, out constant))
{
if (op == "op_Inequality")
constant ^= 1;

return true;
}

return false;
}

private static bool TryExpandTypeEquality_TokenToken(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, out int constant)
private bool TryExpandTypeEquality(in TypeEqualityPatternAnalyzer analyzer, MethodIL methodIL, out int constant)
{
constant = 0;

if (!analyzer.TryAnalyzeTypeEquality_TokenToken(offset, offsetIsAtTypeEquals: true, out TypeDesc type1, out TypeDesc type2))
if (!analyzer.IsTypeEqualityCheck)
return false;

// No value in making this work for definitions
if (type1.IsGenericDefinition || type2.IsGenericDefinition)
return false;
if (analyzer.IsTwoTokens)
{
var type1 = (TypeDesc)methodIL.GetObject(analyzer.Token1);
var type2 = (TypeDesc)methodIL.GetObject(analyzer.Token2);

// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
// Unfortunately this means dataflow will still see code that the rest of the system
// might have optimized away. It should not be a problem in practice.
if (type1.ContainsSignatureVariables() || type2.ContainsSignatureVariables())
return false;
// No value in making this work for definitions
if (type1.IsGenericDefinition || type2.IsGenericDefinition)
return false;

bool? equality = TypeExtensions.CompareTypesForEquality(type1, type2);
if (!equality.HasValue)
return false;
// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
// Unfortunately this means dataflow will still see code that the rest of the system
// might have optimized away. It should not be a problem in practice.
if (type1.ContainsSignatureVariables() || type2.ContainsSignatureVariables())
return false;

constant = equality.Value ? 1 : 0;
bool? equality = TypeExtensions.CompareTypesForEquality(type1, type2);
if (!equality.HasValue)
return false;

return true;
}
constant = equality.Value ? 1 : 0;
}
else
{
var knownType = (TypeDesc)methodIL.GetObject(analyzer.Token1);

private bool TryExpandTypeEquality_TokenOther(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, out int constant)
{
constant = 0;
// No value in making this work for definitions
if (knownType.IsGenericDefinition)
return false;

if (!analyzer.TryAnalyzeTypeEquality_TokenOther(offset, offsetIsAtTypeEquals: true, out TypeDesc knownType))
return false;
// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
// Unfortunately this means dataflow will still see code that the rest of the system
// might have optimized away. It should not be a problem in practice.
if (knownType.ContainsSignatureVariables())
return false;

// No value in making this work for definitions
if (knownType.IsGenericDefinition)
return false;
if (knownType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
return false;

// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
// Unfortunately this means dataflow will still see code that the rest of the system
// might have optimized away. It should not be a problem in practice.
if (knownType.ContainsSignatureVariables())
return false;
// We don't track types without a constructed MethodTable very well.
if (!ConstructedEETypeNode.CreationAllowed(knownType))
return false;

if (knownType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
return false;
if (_devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(knownType.NormalizeInstantiation()))
return false;

// We don't track types without a constructed MethodTable very well.
if (!ConstructedEETypeNode.CreationAllowed(knownType))
return false;
constant = 0;
}

if (_devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(knownType.NormalizeInstantiation()))
return false;
if (analyzer.IsInequality)
constant ^= 1;

constant = 0;
return true;
}

private static TypeDesc ReadLdToken(ref ILReader reader, ILPatternAnalyzer<ILPatternAnalyzerState> analyzer)
private static TypeDesc ReadLdToken(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags)
{
ILOpcode opcode = reader.ReadILOpcode();
if (opcode != ILOpcode.ldtoken)
return null;

TypeDesc t = (TypeDesc)analyzer.Method.GetObject(reader.ReadILToken());
TypeDesc t = (TypeDesc)methodIL.GetObject(reader.ReadILToken());

if (analyzer.State.IsBasicBlockStart(reader.Offset))
if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0)
return null;

return t;
}

private static bool ReadGetTypeFromHandle(ref ILReader reader, ILPatternAnalyzer<ILPatternAnalyzerState> analyzer)
private static bool ReadGetTypeFromHandle(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags)
{
ILOpcode opcode = reader.ReadILOpcode();
if (opcode != ILOpcode.call)
return false;

MethodDesc method = (MethodDesc)analyzer.Method.GetObject(reader.ReadILToken());
MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken());

if (!method.IsIntrinsic || method.Name != "GetTypeFromHandle")
return false;

if (analyzer.State.IsBasicBlockStart(reader.Offset))
if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0)
return false;

return true;
Expand Down Expand Up @@ -1036,15 +1021,6 @@ public SubstitutedDebugInformation(MethodDebugInformation originalDebugInformati
public override IEnumerable<ILSequencePoint> GetSequencePoints() => _sequencePoints;
}

private struct ILPatternAnalyzerState : ILPatternAnalyzerTraits
{
private readonly OpcodeFlags[] _flags;
public ILPatternAnalyzerState(OpcodeFlags[] flags) => _flags = flags;

public bool IsBasicBlockStart(int offset) => (_flags[offset] & OpcodeFlags.BasicBlockStart) != 0;
public bool IsInstructionStart(int offset) => (_flags[offset] & OpcodeFlags.InstructionStart) != 0;
}

private const int TokenTypeString = 0x70; // CorTokenType for strings
}
}
Loading