in src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs [278:702]
protected virtual void Scan (MethodIL methodIL, ref InterproceduralState interproceduralState)
{
MethodBody methodBody = methodIL.Body;
MethodDefinition thisMethod = methodBody.Method;
LocalVariableStore locals = new (methodIL.Variables.Count);
Dictionary<int, Stack<StackSlot>> knownStacks = new Dictionary<int, Stack<StackSlot>> ();
Stack<StackSlot>? currentStack = new Stack<StackSlot> (methodBody.MaxStackSize);
ScanExceptionInformation (knownStacks, methodIL);
BasicBlockIterator blockIterator = new BasicBlockIterator (methodIL);
ReturnValue = default;
foreach (Instruction operation in methodIL.Instructions) {
int curBasicBlock = blockIterator.MoveNext (operation);
if (knownStacks.TryGetValue (operation.Offset, out Stack<StackSlot>? knownValue)) {
if (currentStack == null) {
// The stack copy constructor reverses the stack
currentStack = new Stack<StackSlot> (knownValue.Reverse ());
} else {
currentStack = MergeStack (currentStack, knownValue);
}
}
currentStack ??= new Stack<StackSlot> (methodBody.MaxStackSize);
switch (operation.OpCode.Code) {
case Code.Add:
case Code.Add_Ovf:
case Code.Add_Ovf_Un:
case Code.And:
case Code.Div:
case Code.Div_Un:
case Code.Mul:
case Code.Mul_Ovf:
case Code.Mul_Ovf_Un:
case Code.Or:
case Code.Rem:
case Code.Rem_Un:
case Code.Sub:
case Code.Sub_Ovf:
case Code.Sub_Ovf_Un:
case Code.Xor:
case Code.Cgt:
case Code.Cgt_Un:
case Code.Clt:
case Code.Clt_Un:
case Code.Shl:
case Code.Shr:
case Code.Shr_Un:
case Code.Ceq:
PopUnknown (currentStack, 2, methodBody, operation.Offset);
PushUnknown (currentStack);
break;
case Code.Dup:
currentStack.Push (currentStack.Peek ());
break;
case Code.Ldnull:
currentStack.Push (new StackSlot (NullValue.Instance));
break;
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8: {
int value = operation.OpCode.Code - Code.Ldc_I4_0;
ConstIntValue civ = new ConstIntValue (value);
StackSlot slot = new StackSlot (civ);
currentStack.Push (slot);
}
break;
case Code.Ldc_I4_M1: {
ConstIntValue civ = new ConstIntValue (-1);
StackSlot slot = new StackSlot (civ);
currentStack.Push (slot);
}
break;
case Code.Ldc_I4: {
int value = (int) operation.Operand;
ConstIntValue civ = new ConstIntValue (value);
StackSlot slot = new StackSlot (civ);
currentStack.Push (slot);
}
break;
case Code.Ldc_I4_S: {
int value = (sbyte) operation.Operand;
ConstIntValue civ = new ConstIntValue (value);
StackSlot slot = new StackSlot (civ);
currentStack.Push (slot);
}
break;
case Code.Arglist:
case Code.Sizeof:
case Code.Ldc_I8:
case Code.Ldc_R4:
case Code.Ldc_R8:
PushUnknown (currentStack);
break;
case Code.Ldftn:
TrackNestedFunctionReference ((MethodReference) operation.Operand, ref interproceduralState);
PushUnknown (currentStack);
break;
case Code.Ldarg:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
case Code.Ldarg_S:
case Code.Ldarga:
case Code.Ldarga_S:
ScanLdarg (operation, currentStack, thisMethod);
break;
case Code.Ldloc:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
case Code.Ldloc_S:
case Code.Ldloca:
case Code.Ldloca_S:
ScanLdloc (operation, currentStack, methodIL, locals);
ValidateNoReferenceToReference (locals, methodBody.Method, operation.Offset);
break;
case Code.Ldstr: {
StackSlot slot = new StackSlot (new KnownStringValue ((string) operation.Operand));
currentStack.Push (slot);
}
break;
case Code.Ldtoken:
ScanLdtoken (operation, currentStack);
break;
case Code.Ldind_I:
case Code.Ldind_I1:
case Code.Ldind_I2:
case Code.Ldind_I4:
case Code.Ldind_I8:
case Code.Ldind_R4:
case Code.Ldind_R8:
case Code.Ldind_U1:
case Code.Ldind_U2:
case Code.Ldind_U4:
case Code.Ldlen:
case Code.Ldvirtftn:
case Code.Localloc:
case Code.Refanytype:
case Code.Refanyval:
case Code.Conv_I1:
case Code.Conv_I2:
case Code.Conv_I4:
case Code.Conv_Ovf_I1:
case Code.Conv_Ovf_I1_Un:
case Code.Conv_Ovf_I2:
case Code.Conv_Ovf_I2_Un:
case Code.Conv_Ovf_I4:
case Code.Conv_Ovf_I4_Un:
case Code.Conv_Ovf_U:
case Code.Conv_Ovf_U_Un:
case Code.Conv_Ovf_U1:
case Code.Conv_Ovf_U1_Un:
case Code.Conv_Ovf_U2:
case Code.Conv_Ovf_U2_Un:
case Code.Conv_Ovf_U4:
case Code.Conv_Ovf_U4_Un:
case Code.Conv_U1:
case Code.Conv_U2:
case Code.Conv_U4:
case Code.Conv_I8:
case Code.Conv_Ovf_I8:
case Code.Conv_Ovf_I8_Un:
case Code.Conv_Ovf_U8:
case Code.Conv_Ovf_U8_Un:
case Code.Conv_U8:
case Code.Conv_I:
case Code.Conv_Ovf_I:
case Code.Conv_Ovf_I_Un:
case Code.Conv_U:
case Code.Conv_R_Un:
case Code.Conv_R4:
case Code.Conv_R8:
case Code.Ldind_Ref:
case Code.Ldobj:
case Code.Mkrefany:
case Code.Unbox:
case Code.Unbox_Any:
case Code.Box:
case Code.Neg:
case Code.Not:
PopUnknown (currentStack, 1, methodBody, operation.Offset);
PushUnknown (currentStack);
break;
case Code.Isinst:
case Code.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.
break;
case Code.Ldfld:
case Code.Ldsfld:
case Code.Ldflda:
case Code.Ldsflda:
ScanLdfld (operation, currentStack, methodBody, ref interproceduralState);
break;
case Code.Newarr: {
StackSlot count = PopUnknown (currentStack, 1, methodBody, operation.Offset);
currentStack.Push (new StackSlot (ArrayValue.Create (count.Value, (TypeReference) operation.Operand)));
}
break;
case Code.Stelem_I:
case Code.Stelem_I1:
case Code.Stelem_I2:
case Code.Stelem_I4:
case Code.Stelem_I8:
case Code.Stelem_R4:
case Code.Stelem_R8:
case Code.Stelem_Any:
case Code.Stelem_Ref:
ScanStelem (operation, currentStack, methodBody, curBasicBlock);
break;
case Code.Ldelem_I:
case Code.Ldelem_I1:
case Code.Ldelem_I2:
case Code.Ldelem_I4:
case Code.Ldelem_I8:
case Code.Ldelem_R4:
case Code.Ldelem_R8:
case Code.Ldelem_U1:
case Code.Ldelem_U2:
case Code.Ldelem_U4:
case Code.Ldelem_Any:
case Code.Ldelem_Ref:
case Code.Ldelema:
ScanLdelem (operation, currentStack, methodBody, curBasicBlock);
break;
case Code.Cpblk:
case Code.Initblk:
PopUnknown (currentStack, 3, methodBody, operation.Offset);
break;
case Code.Stfld:
case Code.Stsfld:
ScanStfld (operation, currentStack, thisMethod, methodBody, locals, ref interproceduralState);
break;
case Code.Cpobj:
PopUnknown (currentStack, 2, methodBody, operation.Offset);
break;
case Code.Stind_I:
case Code.Stind_I1:
case Code.Stind_I2:
case Code.Stind_I4:
case Code.Stind_I8:
case Code.Stind_R4:
case Code.Stind_R8:
case Code.Stind_Ref:
case Code.Stobj:
ScanIndirectStore (operation, currentStack, methodBody, locals, curBasicBlock, ref interproceduralState);
ValidateNoReferenceToReference (locals, methodBody.Method, operation.Offset);
break;
case Code.Initobj:
case Code.Pop:
PopUnknown (currentStack, 1, methodBody, operation.Offset);
break;
case Code.Starg:
case Code.Starg_S:
ScanStarg (operation, currentStack, thisMethod, methodBody);
break;
case Code.Stloc:
case Code.Stloc_S:
case Code.Stloc_0:
case Code.Stloc_1:
case Code.Stloc_2:
case Code.Stloc_3:
ScanStloc (operation, currentStack, methodIL, locals, curBasicBlock);
ValidateNoReferenceToReference (locals, methodBody.Method, operation.Offset);
break;
case Code.Constrained:
case Code.No:
case Code.Readonly:
case Code.Tail:
case Code.Unaligned:
case Code.Volatile:
break;
case Code.Brfalse:
case Code.Brfalse_S:
case Code.Brtrue:
case Code.Brtrue_S:
PopUnknown (currentStack, 1, methodBody, operation.Offset);
NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack);
break;
case Code.Calli: {
var signature = (CallSite) operation.Operand;
if (signature.HasThis && !signature.ExplicitThis) {
PopUnknown (currentStack, 1, methodBody, operation.Offset);
}
// Pop arguments
if (signature.Parameters.Count > 0)
PopUnknown (currentStack, signature.Parameters.Count, methodBody, operation.Offset);
// Pop function pointer
PopUnknown (currentStack, 1, methodBody, operation.Offset);
if (!signature.ReturnsVoid ())
PushUnknown (currentStack);
}
break;
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
TrackNestedFunctionReference ((MethodReference) operation.Operand, ref interproceduralState);
HandleCall (methodBody, operation, currentStack, locals, ref interproceduralState, curBasicBlock);
ValidateNoReferenceToReference (locals, methodBody.Method, operation.Offset);
break;
case Code.Jmp:
// Not generated by mainstream compilers
break;
case Code.Br:
case Code.Br_S:
NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack);
ClearStack (ref currentStack);
break;
case Code.Leave:
case Code.Leave_S:
ClearStack (ref currentStack);
NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, new Stack<StackSlot> (methodBody.MaxStackSize));
break;
case Code.Endfilter:
case Code.Endfinally:
case Code.Rethrow:
case Code.Throw:
ClearStack (ref currentStack);
break;
case Code.Ret: {
bool hasReturnValue = !methodBody.Method.ReturnsVoid ();
if (currentStack.Count != (hasReturnValue ? 1 : 0)) {
WarnAboutInvalidILInMethod (methodBody, operation.Offset);
}
if (hasReturnValue) {
StackSlot retValue = PopUnknown (currentStack, 1, methodBody, operation.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 (retValue.Value, locals, ref interproceduralState));
ValidateNoReferenceToReference (locals, methodBody.Method, operation.Offset);
}
ClearStack (ref currentStack);
break;
}
case Code.Switch: {
PopUnknown (currentStack, 1, methodBody, operation.Offset);
Instruction[] targets = (Instruction[]) operation.Operand;
foreach (Instruction target in targets) {
NewKnownStack (knownStacks, target.Offset, currentStack);
}
break;
}
case Code.Beq:
case Code.Beq_S:
case Code.Bne_Un:
case Code.Bne_Un_S:
case Code.Bge:
case Code.Bge_S:
case Code.Bge_Un:
case Code.Bge_Un_S:
case Code.Bgt:
case Code.Bgt_S:
case Code.Bgt_Un:
case Code.Bgt_Un_S:
case Code.Ble:
case Code.Ble_S:
case Code.Ble_Un:
case Code.Ble_Un_S:
case Code.Blt:
case Code.Blt_S:
case Code.Blt_Un:
case Code.Blt_Un_S:
PopUnknown (currentStack, 2, methodBody, operation.Offset);
NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack);
break;
}
}
}