Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract shared IL pattern analysis to a class #103701

Merged
merged 14 commits into from
Jul 1, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,10 @@ private static RuntimeTypeHandle GetRuntimeTypeHandle(IntPtr pEEType)
{
return new RuntimeTypeHandle(pEEType);
}

private static Type GetRuntimeType(IntPtr pEEType)
{
return Type.GetTypeFromHandle(new RuntimeTypeHandle(pEEType));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
}
}

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

bool hasGetResourceStringCall = false;

// Mark all reachable basic blocks
Expand Down Expand Up @@ -297,7 +299,7 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
|| opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s)
{
int destination = reader.ReadBranchDestination(opcode);
if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int constant))
if (!TryGetConstantArgument(analyzer, offset, 0, out int constant))
{
// Can't get the constant - both branches are live.
offsetsToVisit.Push(destination);
Expand All @@ -323,8 +325,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
|| opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s)
{
int destination = reader.ReadBranchDestination(opcode);
if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int left)
|| !TryGetConstantArgument(method, methodBytes, flags, offset, 1, out int right))
if (!TryGetConstantArgument(analyzer, offset, 0, out int left)
|| !TryGetConstantArgument(analyzer, offset, 1, out int right))
{
// Can't get the constant - both branches are live.
offsetsToVisit.Push(destination);
Expand Down Expand Up @@ -654,24 +656,24 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method)
return new SubstitutedMethodIL(method.GetMethodILDefinition(), newBody, newEHRegions.ToArray(), debugInfo, newStrings.ToArray());
}

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

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

ILReader reader = new ILReader(body, currentOffset);
ILReader reader = new ILReader(analyzer.ILBytes, currentOffset);
ILOpcode opcode = reader.ReadILOpcode();
if (opcode == ILOpcode.call || opcode == ILOpcode.callvirt)
{
MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken());
MethodDesc method = (MethodDesc)analyzer.Method.GetObject(reader.ReadILToken());
if (argIndex == 0)
{
BodySubstitution substitution = _substitutionProvider.GetSubstitution(method);
Expand All @@ -684,14 +686,14 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[
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(methodIL, body, flags, currentOffset, method.Name, out constant))
&& 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(methodIL, body, flags, currentOffset, method.Name, out constant))
&& TryExpandTypeIs(analyzer, currentOffset, method.Name, out constant))
{
return true;
}
Expand All @@ -712,7 +714,7 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[
}
else if (opcode == ILOpcode.ldsfld)
{
FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
FieldDesc field = (FieldDesc)analyzer.Method.GetObject(reader.ReadILToken());
if (argIndex == 0)
{
object substitution = _substitutionProvider.GetSubstitution(field);
Expand Down Expand Up @@ -762,7 +764,7 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[
}
else if ((opcode == ILOpcode.ldloc || opcode == ILOpcode.ldloc_s ||
(opcode >= ILOpcode.ldloc_0 && opcode <= ILOpcode.ldloc_3)) &&
((flags[currentOffset] & OpcodeFlags.BasicBlockStart) == 0))
!analyzer.State.IsBasicBlockStart(currentOffset))
{
// Paired stloc/ldloc that the C# compiler generates in debug code?
int locIndex = opcode switch
Expand All @@ -774,10 +776,10 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[

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

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

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

constant = 0;
return false;
}

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

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

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

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

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

// Dataflow runs on top of uninstantiated IL and we can't answer some questions there.
Expand All @@ -873,15 +875,10 @@ private static bool TryExpandTypeIs(MethodIL methodIL, byte[] body, OpcodeFlags[
return true;
}

private bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, string op, out int constant)
private bool TryExpandTypeEquality(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, string op, out int constant)
{
if (TryExpandTypeEquality_TokenToken(methodIL, body, flags, offset, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 1, expectGetType: false, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 2, expectGetType: false, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 3, expectGetType: false, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 1, expectGetType: true, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 2, expectGetType: true, out constant)
|| TryExpandTypeEquality_TokenOther(methodIL, body, flags, offset, 3, expectGetType: true, out constant))
if (TryExpandTypeEquality_TokenToken(analyzer, offset, out constant)
|| TryExpandTypeEquality_TokenOther(analyzer, offset, out constant))
{
if (op == "op_Inequality")
constant ^= 1;
Expand All @@ -892,36 +889,11 @@ private bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, OpcodeFlags[]
return false;
}

private static bool TryExpandTypeEquality_TokenToken(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, out int constant)
private static bool TryExpandTypeEquality_TokenToken(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, out int constant)
{
// We expect to see a sequence:
// ldtoken Foo
// call GetTypeFromHandle
// ldtoken Bar
// call GetTypeFromHandle
// -> offset points here
constant = 0;
const int SequenceLength = 20;
if (offset < SequenceLength)
return false;

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

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

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

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

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

if (!ReadGetTypeFromHandle(ref reader, methodIL, flags))
if (!analyzer.TryAnalyzeTypeEquality_TokenToken(offset, offsetIsAtTypeEquals: true, out TypeDesc type1, out TypeDesc type2))
return false;

// No value in making this work for definitions
Expand All @@ -943,84 +915,13 @@ private static bool TryExpandTypeEquality_TokenToken(MethodIL methodIL, byte[] b
return true;
}

private bool TryExpandTypeEquality_TokenOther(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, int ldInstructionSize, bool expectGetType, out int constant)
private bool TryExpandTypeEquality_TokenOther(ILPatternAnalyzer<ILPatternAnalyzerState> analyzer, int offset, out int constant)
{
// We expect to see a sequence:
// ldtoken Foo
// call GetTypeFromHandle
// ldloc.X/ldloc_s X/ldarg.X/ldarg_s X
// [optional] call Object.GetType
// -> offset points here
//
// The ldtoken part can potentially be in the second argument position

constant = 0;
int sequenceLength = 5 + 5 + ldInstructionSize + (expectGetType ? 5 : 0);
if (offset < sequenceLength)
return false;

if ((flags[offset - sequenceLength] & OpcodeFlags.InstructionStart) == 0)
return false;

ILReader reader = new ILReader(body, offset - sequenceLength);

TypeDesc knownType = null;

// Is the ldtoken in the first position?
if (reader.PeekILOpcode() == ILOpcode.ldtoken)
{
knownType = ReadLdToken(ref reader, methodIL, flags);
if (knownType == null)
return false;

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

ILOpcode opcode = reader.ReadILOpcode();
if (ldInstructionSize == 1 && opcode is (>= ILOpcode.ldloc_0 and <= ILOpcode.ldloc_3) or (>= ILOpcode.ldarg_0 and <= ILOpcode.ldarg_3))
{
// Nothing to read
}
else if (ldInstructionSize == 2 && opcode is ILOpcode.ldloc_s or ILOpcode.ldarg_s)
{
reader.ReadILByte();
}
else if (ldInstructionSize == 3 && opcode is ILOpcode.ldloc or ILOpcode.ldarg)
{
reader.ReadILUInt16();
}
else
{
return false;
}

if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0)
if (!analyzer.TryAnalyzeTypeEquality_TokenOther(offset, offsetIsAtTypeEquals: true, out TypeDesc knownType))
return false;

if (expectGetType)
{
if (reader.ReadILOpcode() is not ILOpcode.callvirt and not ILOpcode.call)
return false;

// We don't actually mind if this is not Object.GetType
reader.ReadILToken();

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

// If the ldtoken wasn't in the first position, it must be in the other
if (knownType == null)
{
knownType = ReadLdToken(ref reader, methodIL, flags);
if (knownType == null)
return false;

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

// No value in making this work for definitions
if (knownType.IsGenericDefinition)
return false;
Expand All @@ -1045,32 +946,32 @@ private bool TryExpandTypeEquality_TokenOther(MethodIL methodIL, byte[] body, Op
return true;
}

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

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

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

return t;
}

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

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

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

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

return true;
Expand Down Expand Up @@ -1135,6 +1036,15 @@ 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
Loading