in src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs [346:851]
protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interproceduralState)
{
MethodDesc thisMethod = methodBody.OwningMethod;
ValueBasicBlockPair?[] locals = new ValueBasicBlockPair?[methodBody.GetLocals().Length];
Dictionary<int, Stack<StackSlot>> knownStacks = new Dictionary<int, Stack<StackSlot>>();
Stack<StackSlot>? currentStack = new Stack<StackSlot>(methodBody.MaxStack);
ScanExceptionInformation(knownStacks, methodBody);
BasicBlockIterator blockIterator = new BasicBlockIterator(methodBody);
ReturnValue = default(MultiValue);
ILReader reader = new ILReader(methodBody.GetILBytes());
while (reader.HasNext)
{
ValidateNoReferenceToReference(locals, methodBody, reader.Offset);
int curBasicBlock = blockIterator.MoveNext(reader.Offset);
if (knownStacks.TryGetValue(reader.Offset, out var knownStack))
{
if (currentStack == null)
{
// The stack copy constructor reverses the stack
currentStack = new Stack<StackSlot>(knownStack.Reverse());
}
else
{
currentStack = MergeStack(currentStack, knownStack);
}
}
currentStack ??= new Stack<StackSlot>(methodBody.MaxStack);
int offset = reader.Offset;
ILOpcode opcode = reader.ReadILOpcode();
switch (opcode)
{
case ILOpcode.add:
case ILOpcode.add_ovf:
case ILOpcode.add_ovf_un:
case ILOpcode.and:
case ILOpcode.div:
case ILOpcode.div_un:
case ILOpcode.mul:
case ILOpcode.mul_ovf:
case ILOpcode.mul_ovf_un:
case ILOpcode.or:
case ILOpcode.rem:
case ILOpcode.rem_un:
case ILOpcode.sub:
case ILOpcode.sub_ovf:
case ILOpcode.sub_ovf_un:
case ILOpcode.xor:
case ILOpcode.cgt:
case ILOpcode.cgt_un:
case ILOpcode.clt:
case ILOpcode.clt_un:
case ILOpcode.shl:
case ILOpcode.shr:
case ILOpcode.shr_un:
case ILOpcode.ceq:
PopUnknown(currentStack, 2, methodBody, offset);
PushUnknown(currentStack);
reader.Skip(opcode);
break;
case ILOpcode.dup:
currentStack.Push(currentStack.Peek());
break;
case ILOpcode.ldnull:
currentStack.Push(new StackSlot(NullValue.Instance));
break;
case ILOpcode.ldc_i4_0:
case ILOpcode.ldc_i4_1:
case ILOpcode.ldc_i4_2:
case ILOpcode.ldc_i4_3:
case ILOpcode.ldc_i4_4:
case ILOpcode.ldc_i4_5:
case ILOpcode.ldc_i4_6:
case ILOpcode.ldc_i4_7:
case ILOpcode.ldc_i4_8:
{
int value = opcode - ILOpcode.ldc_i4_0;
ConstIntValue civ = new ConstIntValue(value);
StackSlot slot = new StackSlot(civ);
currentStack.Push(slot);
}
break;
case ILOpcode.ldc_i4_m1:
{
ConstIntValue civ = new ConstIntValue(-1);
StackSlot slot = new StackSlot(civ);
currentStack.Push(slot);
}
break;
case ILOpcode.ldc_i4:
{
int value = (int)reader.ReadILUInt32();
ConstIntValue civ = new ConstIntValue(value);
StackSlot slot = new StackSlot(civ);
currentStack.Push(slot);
}
break;
case ILOpcode.ldc_i4_s:
{
int value = (sbyte)reader.ReadILByte();
ConstIntValue civ = new ConstIntValue(value);
StackSlot slot = new StackSlot(civ);
currentStack.Push(slot);
}
break;
case ILOpcode.arglist:
case ILOpcode.sizeof_:
case ILOpcode.ldc_i8:
case ILOpcode.ldc_r4:
case ILOpcode.ldc_r8:
PushUnknown(currentStack);
reader.Skip(opcode);
break;
case ILOpcode.ldftn:
{
if (methodBody.GetObject(reader.ReadILToken()) is MethodDesc methodOperand)
{
HandleMethodTokenAccess(methodBody, offset, methodOperand);
TrackNestedFunctionReference(methodOperand, ref interproceduralState);
}
PushUnknown(currentStack);
}
break;
case ILOpcode.ldarg:
case ILOpcode.ldarg_0:
case ILOpcode.ldarg_1:
case ILOpcode.ldarg_2:
case ILOpcode.ldarg_3:
case ILOpcode.ldarg_s:
case ILOpcode.ldarga:
case ILOpcode.ldarga_s:
ScanLdarg(opcode, opcode switch
{
ILOpcode.ldarg => reader.ReadILUInt16(),
ILOpcode.ldarga => reader.ReadILUInt16(),
ILOpcode.ldarg_s => reader.ReadILByte(),
ILOpcode.ldarga_s => reader.ReadILByte(),
_ => opcode - ILOpcode.ldarg_0
}, currentStack, thisMethod);
break;
case ILOpcode.ldloc:
case ILOpcode.ldloc_0:
case ILOpcode.ldloc_1:
case ILOpcode.ldloc_2:
case ILOpcode.ldloc_3:
case ILOpcode.ldloc_s:
case ILOpcode.ldloca:
case ILOpcode.ldloca_s:
ScanLdloc(methodBody, opcode, opcode switch
{
ILOpcode.ldloc => reader.ReadILUInt16(),
ILOpcode.ldloca => reader.ReadILUInt16(),
ILOpcode.ldloc_s => reader.ReadILByte(),
ILOpcode.ldloca_s => reader.ReadILByte(),
_ => opcode - ILOpcode.ldloc_0
}, currentStack, locals);
ValidateNoReferenceToReference(locals, methodBody, offset);
break;
case ILOpcode.ldstr:
{
StackSlot slot = new StackSlot(new KnownStringValue((string)methodBody.GetObject(reader.ReadILToken())));
currentStack.Push(slot);
}
break;
case ILOpcode.ldtoken:
object obj = methodBody.GetObject(reader.ReadILToken());
ScanLdtoken(methodBody, offset, obj, currentStack);
break;
case ILOpcode.ldvirtftn:
{
if (methodBody.GetObject(reader.ReadILToken()) is MethodDesc methodOperand)
{
HandleMethodTokenAccess(methodBody, offset, methodOperand);
}
PopUnknown(currentStack, 1, methodBody, offset);
PushUnknown(currentStack);
}
break;
case ILOpcode.ldind_i:
case ILOpcode.ldind_i1:
case ILOpcode.ldind_i2:
case ILOpcode.ldind_i4:
case ILOpcode.ldind_i8:
case ILOpcode.ldind_r4:
case ILOpcode.ldind_r8:
case ILOpcode.ldind_u1:
case ILOpcode.ldind_u2:
case ILOpcode.ldind_u4:
case ILOpcode.ldlen:
case ILOpcode.localloc:
case ILOpcode.refanytype:
case ILOpcode.refanyval:
case ILOpcode.conv_i1:
case ILOpcode.conv_i2:
case ILOpcode.conv_i4:
case ILOpcode.conv_ovf_i1:
case ILOpcode.conv_ovf_i1_un:
case ILOpcode.conv_ovf_i2:
case ILOpcode.conv_ovf_i2_un:
case ILOpcode.conv_ovf_i4:
case ILOpcode.conv_ovf_i4_un:
case ILOpcode.conv_ovf_u:
case ILOpcode.conv_ovf_u_un:
case ILOpcode.conv_ovf_u1:
case ILOpcode.conv_ovf_u1_un:
case ILOpcode.conv_ovf_u2:
case ILOpcode.conv_ovf_u2_un:
case ILOpcode.conv_ovf_u4:
case ILOpcode.conv_ovf_u4_un:
case ILOpcode.conv_u1:
case ILOpcode.conv_u2:
case ILOpcode.conv_u4:
case ILOpcode.conv_i8:
case ILOpcode.conv_ovf_i8:
case ILOpcode.conv_ovf_i8_un:
case ILOpcode.conv_ovf_u8:
case ILOpcode.conv_ovf_u8_un:
case ILOpcode.conv_u8:
case ILOpcode.conv_i:
case ILOpcode.conv_ovf_i:
case ILOpcode.conv_ovf_i_un:
case ILOpcode.conv_u:
case ILOpcode.conv_r_un:
case ILOpcode.conv_r4:
case ILOpcode.conv_r8:
case ILOpcode.ldind_ref:
case ILOpcode.ldobj:
case ILOpcode.mkrefany:
case ILOpcode.unbox:
case ILOpcode.unbox_any:
case ILOpcode.box:
case ILOpcode.neg:
case ILOpcode.not:
PopUnknown(currentStack, 1, methodBody, offset);
PushUnknown(currentStack);
reader.Skip(opcode);
break;
case ILOpcode.isinst:
case ILOpcode.castclass:
// We can consider a NOP because the value doesn't change.
// It might change to NULL, but for the purposes of dataflow analysis
// it doesn't hurt much.
reader.Skip(opcode);
break;
case ILOpcode.ldfld:
case ILOpcode.ldsfld:
case ILOpcode.ldflda:
case ILOpcode.ldsflda:
ScanLdfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack, ref interproceduralState);
break;
case ILOpcode.newarr:
{
StackSlot count = PopUnknown(currentStack, 1, methodBody, offset);
var arrayElement = (TypeDesc)methodBody.GetObject(reader.ReadILToken());
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElement)));
}
break;
case ILOpcode.stelem_i:
case ILOpcode.stelem_i1:
case ILOpcode.stelem_i2:
case ILOpcode.stelem_i4:
case ILOpcode.stelem_i8:
case ILOpcode.stelem_r4:
case ILOpcode.stelem_r8:
case ILOpcode.stelem:
case ILOpcode.stelem_ref:
ScanStelem(offset, currentStack, methodBody, curBasicBlock);
reader.Skip(opcode);
break;
case ILOpcode.ldelem_i:
case ILOpcode.ldelem_i1:
case ILOpcode.ldelem_i2:
case ILOpcode.ldelem_i4:
case ILOpcode.ldelem_i8:
case ILOpcode.ldelem_r4:
case ILOpcode.ldelem_r8:
case ILOpcode.ldelem_u1:
case ILOpcode.ldelem_u2:
case ILOpcode.ldelem_u4:
case ILOpcode.ldelem:
case ILOpcode.ldelem_ref:
case ILOpcode.ldelema:
ScanLdelem(opcode, offset, currentStack, methodBody, curBasicBlock);
reader.Skip(opcode);
break;
case ILOpcode.cpblk:
case ILOpcode.initblk:
PopUnknown(currentStack, 3, methodBody, offset);
reader.Skip(opcode);
break;
case ILOpcode.stfld:
case ILOpcode.stsfld:
ScanStfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack, locals, ref interproceduralState);
break;
case ILOpcode.cpobj:
PopUnknown(currentStack, 2, methodBody, offset);
reader.Skip(opcode);
break;
case ILOpcode.stind_i:
case ILOpcode.stind_i1:
case ILOpcode.stind_i2:
case ILOpcode.stind_i4:
case ILOpcode.stind_i8:
case ILOpcode.stind_r4:
case ILOpcode.stind_r8:
case ILOpcode.stind_ref:
case ILOpcode.stobj:
ScanIndirectStore(methodBody, offset, currentStack, locals, curBasicBlock, ref interproceduralState);
ValidateNoReferenceToReference(locals, methodBody, offset);
reader.Skip(opcode);
break;
case ILOpcode.initobj:
case ILOpcode.pop:
PopUnknown(currentStack, 1, methodBody, offset);
reader.Skip(opcode);
break;
case ILOpcode.starg:
case ILOpcode.starg_s:
ScanStarg(methodBody, offset, opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte(), currentStack);
break;
case ILOpcode.stloc:
case ILOpcode.stloc_s:
case ILOpcode.stloc_0:
case ILOpcode.stloc_1:
case ILOpcode.stloc_2:
case ILOpcode.stloc_3:
ScanStloc(methodBody, offset, opcode switch
{
ILOpcode.stloc => reader.ReadILUInt16(),
ILOpcode.stloc_s => reader.ReadILByte(),
_ => opcode - ILOpcode.stloc_0,
}, currentStack, locals, curBasicBlock);
ValidateNoReferenceToReference(locals, methodBody, offset);
break;
case ILOpcode.constrained:
case ILOpcode.no:
case ILOpcode.readonly_:
case ILOpcode.tail:
case ILOpcode.unaligned:
case ILOpcode.volatile_:
reader.Skip(opcode);
break;
case ILOpcode.brfalse:
case ILOpcode.brfalse_s:
case ILOpcode.brtrue:
case ILOpcode.brtrue_s:
PopUnknown(currentStack, 1, methodBody, offset);
NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
break;
case ILOpcode.calli:
{
var signature = (MethodSignature)methodBody.GetObject(reader.ReadILToken());
if (!signature.IsStatic)
{
PopUnknown(currentStack, 1, methodBody, offset);
}
// Pop arguments
if (signature.Length > 0)
PopUnknown(currentStack, signature.Length, methodBody, offset);
// Pop function pointer
PopUnknown(currentStack, 1, methodBody, offset);
// Push return value
if (!signature.ReturnType.IsVoid)
PushUnknown(currentStack);
}
break;
case ILOpcode.call:
case ILOpcode.callvirt:
case ILOpcode.newobj:
{
MethodDesc methodOperand = (MethodDesc)methodBody.GetObject(reader.ReadILToken());
TrackNestedFunctionReference(methodOperand, ref interproceduralState);
HandleCall(methodBody, opcode, offset, methodOperand, currentStack, locals, ref interproceduralState, curBasicBlock);
ValidateNoReferenceToReference(locals, methodBody, offset);
}
break;
case ILOpcode.jmp:
// Not generated by mainstream compilers
reader.Skip(opcode);
break;
case ILOpcode.br:
case ILOpcode.br_s:
NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
ClearStack(ref currentStack);
break;
case ILOpcode.leave:
case ILOpcode.leave_s:
ClearStack(ref currentStack);
NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), new Stack<StackSlot>(methodBody.MaxStack));
break;
case ILOpcode.endfilter:
case ILOpcode.endfinally:
case ILOpcode.rethrow:
case ILOpcode.throw_:
ClearStack(ref currentStack);
break;
case ILOpcode.ret:
{
bool hasReturnValue = !methodBody.OwningMethod.Signature.ReturnType.IsVoid;
if (currentStack.Count != (hasReturnValue ? 1 : 0))
{
WarnAboutInvalidILInMethod(methodBody, offset);
}
if (hasReturnValue)
{
StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset);
// If the return value is a reference, treat it as the value itself for now
// We can handle ref return values better later
ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(methodBody, offset, retValue.Value, locals, ref interproceduralState));
ValidateNoReferenceToReference(locals, methodBody, offset);
}
ClearStack(ref currentStack);
break;
}
case ILOpcode.switch_:
{
PopUnknown(currentStack, 1, methodBody, offset);
uint count = reader.ReadILUInt32();
int jmpBase = reader.Offset + (int)(4 * count);
for (uint i = 0; i < count; i++)
{
NewKnownStack(knownStacks, (int)reader.ReadILUInt32() + jmpBase, currentStack);
}
break;
}
case ILOpcode.beq:
case ILOpcode.beq_s:
case ILOpcode.bne_un:
case ILOpcode.bne_un_s:
case ILOpcode.bge:
case ILOpcode.bge_s:
case ILOpcode.bge_un:
case ILOpcode.bge_un_s:
case ILOpcode.bgt:
case ILOpcode.bgt_s:
case ILOpcode.bgt_un:
case ILOpcode.bgt_un_s:
case ILOpcode.ble:
case ILOpcode.ble_s:
case ILOpcode.ble_un:
case ILOpcode.ble_un_s:
case ILOpcode.blt:
case ILOpcode.blt_s:
case ILOpcode.blt_un:
case ILOpcode.blt_un_s:
PopUnknown(currentStack, 2, methodBody, offset);
NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack);
break;
default:
reader.Skip(opcode);
break;
}
}
}