/// <summary> /// Find IL offsets at which basic blocks begin. /// </summary> private static HashSet <int> GetBasicBlockStarts(MethodIL il) { ILReader reader = new ILReader(il.GetILBytes()); HashSet <int> bbStarts = new HashSet <int>(); bbStarts.Add(0); while (reader.HasNext) { ILOpcode opc = reader.ReadILOpcode(); if (opc.IsBranch()) { int tar = reader.ReadBranchDestination(opc); bbStarts.Add(tar); // Conditional branches can fall through. if (!opc.IsUnconditionalBranch()) { bbStarts.Add(reader.Offset); } } else if (opc == ILOpcode.switch_) { uint numCases = reader.ReadILUInt32(); int jmpBase = reader.Offset + checked ((int)(numCases * 4)); // Default case is at jmpBase. bbStarts.Add(jmpBase); for (uint i = 0; i < numCases; i++) { int caseOfs = jmpBase + (int)reader.ReadILUInt32(); bbStarts.Add(caseOfs); } } else if (opc == ILOpcode.ret || opc == ILOpcode.endfinally || opc == ILOpcode.endfilter || opc == ILOpcode.throw_ || opc == ILOpcode.rethrow) { if (reader.HasNext) { bbStarts.Add(reader.Offset); } } else { reader.Skip(opc); } } foreach (ILExceptionRegion ehRegion in il.GetExceptionRegions()) { bbStarts.Add(ehRegion.TryOffset); bbStarts.Add(ehRegion.TryOffset + ehRegion.TryLength); bbStarts.Add(ehRegion.HandlerOffset); bbStarts.Add(ehRegion.HandlerOffset + ehRegion.HandlerLength); if (ehRegion.Kind.HasFlag(ILExceptionRegionKind.Filter)) { bbStarts.Add(ehRegion.FilterOffset); } } return(bbStarts); }
public bool TryReadLdcI4(out int value) { ILOpcode opcode = _reader.PeekILOpcode(); if (opcode == ILOpcode.ldc_i4) // ldc.i4 { _reader.ReadILOpcode(); value = unchecked ((int)_reader.ReadILUInt32()); return(true); } if ((opcode >= ILOpcode.ldc_i4_m1) && (opcode <= ILOpcode.ldc_i4_8)) // ldc.m1 to ldc.i4.8 { _reader.ReadILOpcode(); value = -1 + ((int)opcode) - 0x15; return(true); } if (opcode == ILOpcode.ldc_i4_s) // ldc.i4.s { _reader.ReadILOpcode(); value = (int)unchecked ((sbyte)_reader.ReadILByte()); return(true); } value = 0; return(false); }
public static HashSet <int> ComputeBranchTargets(this MethodIL methodBody) { HashSet <int> branchTargets = new HashSet <int>(); var reader = new ILReader(methodBody.GetILBytes()); while (reader.HasNext) { ILOpcode opcode = reader.ReadILOpcode(); if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un) { branchTargets.Add(reader.ReadBranchDestination(opcode)); } else if (opcode == ILOpcode.switch_) { uint count = reader.ReadILUInt32(); int jmpBase = reader.Offset + (int)(4 * count); for (uint i = 0; i < count; i++) { branchTargets.Add((int)reader.ReadILUInt32() + jmpBase); } } else { reader.Skip(opcode); } } foreach (ILExceptionRegion einfo in methodBody.GetExceptionRegions()) { if (einfo.Kind == ILExceptionRegionKind.Filter) { branchTargets.Add(einfo.FilterOffset); } branchTargets.Add(einfo.HandlerOffset); } return(branchTargets); }
private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, int argIndex, out int constant) { if ((flags[offset] & OpcodeFlags.BasicBlockStart) != 0) { constant = 0; return(false); } for (int currentOffset = offset - 1; currentOffset >= 0; currentOffset--) { if ((flags[currentOffset] & OpcodeFlags.InstructionStart) == 0) { continue; } ILReader reader = new ILReader(body, currentOffset); ILOpcode opcode = reader.ReadILOpcode(); if (opcode == ILOpcode.call || opcode == ILOpcode.callvirt) { MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); if (argIndex == 0) { BodySubstitution substitution = GetSubstitution(method); if (substitution != null && substitution.Value is int && (opcode != ILOpcode.callvirt || !method.IsVirtual)) { constant = (int)substitution.Value; return(true); } else { constant = 0; return(false); } } argIndex--; if (method.Signature.Length > 0 || !method.Signature.IsStatic) { // We don't know how to skip over the parameters break; } } else if (opcode == ILOpcode.ldsfld) { FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); if (argIndex == 0) { object substitution = GetSubstitution(field); if (substitution is int) { constant = (int)substitution; return(true); } else { constant = 0; return(false); } } argIndex--; } else if (opcode >= ILOpcode.ldc_i4_0 && opcode <= ILOpcode.ldc_i4_8) { if (argIndex == 0) { constant = opcode - ILOpcode.ldc_i4_0; return(true); } argIndex--; } else if (opcode == ILOpcode.ldc_i4) { if (argIndex == 0) { constant = (int)reader.ReadILUInt32(); return(true); } argIndex--; } else if (opcode == ILOpcode.ldc_i4_s) { if (argIndex == 0) { constant = (int)(sbyte)reader.ReadILByte(); return(true); } argIndex--; } else if ((opcode == ILOpcode.ldloc || opcode == ILOpcode.ldloc_s || (opcode >= ILOpcode.ldloc_0 && opcode <= ILOpcode.ldloc_3)) && ((flags[currentOffset] & OpcodeFlags.BasicBlockStart) == 0)) { // Paired stloc/ldloc that the C# compiler generates in debug code? int locIndex = opcode switch { ILOpcode.ldloc => reader.ReadILUInt16(), ILOpcode.ldloc_s => reader.ReadILByte(), _ => opcode - ILOpcode.ldloc_0, }; for (int potentialStlocOffset = currentOffset - 1; potentialStlocOffset >= 0; potentialStlocOffset--) { if ((flags[potentialStlocOffset] & OpcodeFlags.InstructionStart) == 0) { continue; } 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)) && otherOpcode switch { ILOpcode.stloc => nestedReader.ReadILUInt16(), ILOpcode.stloc_s => nestedReader.ReadILByte(), _ => otherOpcode - ILOpcode.stloc_0, } == locIndex)
public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) { // This attempts to find all basic blocks that are unreachable after applying the substitutions. // // On a high level, we first find all the basic blocks and instruction boundaries in the IL stream. // This is tracked in a sidecar `flags` array that has flags for each byte of the IL stream. // // Once we have all the basic blocks and instruction boundaries, we do a marking phase to mark // the reachable blocks. We use substitutions to tell us what's unreachable. We consider conditional // branches "interesting" and whenever we see one, we seek backwards in the IL instruction stream // to find the instruction that feeds it. We make sure we don't cross the basic block boundary while // doing that. If the conditional instruction is fed by known values (either through the substitutions // or because it's an IL constant), we simulate the result of the comparison and only mark // the taken branch. We also mark any associated EH regions. // // The "seek backwards to find what feeds the comparison" only works for a couple known instructions // (load constant, call). It can't e.g. skip over arguments to the call. // // Last step is a sweep - we replace the tail of all unreachable blocks with "br $-2" // and nop out the rest. If the basic block is smaller than 2 bytes, we don't touch it. // We also eliminate any EH records that correspond to the stubbed out basic block. Debug.Assert(method.GetMethodILDefinition() == method); ILExceptionRegion[] ehRegions = method.GetExceptionRegions(); byte[] methodBytes = method.GetILBytes(); OpcodeFlags[] flags = new OpcodeFlags[methodBytes.Length]; // Offset 0 is the first basic block Stack <int> offsetsToVisit = new Stack <int>(); offsetsToVisit.Push(0); // Basic blocks also start around EH regions foreach (ILExceptionRegion ehRegion in ehRegions) { if (ehRegion.Kind == ILExceptionRegionKind.Filter) { offsetsToVisit.Push(ehRegion.FilterOffset); } offsetsToVisit.Push(ehRegion.HandlerOffset); } // Identify basic blocks and instruction boundaries while (offsetsToVisit.TryPop(out int offset)) { // If this was already visited, we're done if (flags[offset] != 0) { // Also mark as basic block start in case this was a target of a backwards branch. flags[offset] |= OpcodeFlags.BasicBlockStart; continue; } flags[offset] |= OpcodeFlags.BasicBlockStart; // Read until we reach the end of the basic block ILReader reader = new ILReader(methodBytes, offset); while (reader.HasNext) { offset = reader.Offset; flags[offset] |= OpcodeFlags.InstructionStart; ILOpcode opcode = reader.ReadILOpcode(); if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un || opcode == ILOpcode.leave || opcode == ILOpcode.leave_s) { int destination = reader.ReadBranchDestination(opcode); offsetsToVisit.Push(destination); if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s && opcode != ILOpcode.br && opcode != ILOpcode.br_s) { // Branches not tested for above are conditional and the flow falls through. offsetsToVisit.Push(reader.Offset); } flags[offset] |= OpcodeFlags.EndBasicBlock; } else if (opcode == ILOpcode.ret || opcode == ILOpcode.endfilter || opcode == ILOpcode.endfinally || opcode == ILOpcode.throw_ || opcode == ILOpcode.rethrow || opcode == ILOpcode.jmp) { // Ends basic block. flags[offset] |= OpcodeFlags.EndBasicBlock; reader.Skip(opcode); } else if (opcode == ILOpcode.switch_) { uint count = reader.ReadILUInt32(); int jmpBase = reader.Offset + (int)(4 * count); for (uint i = 0; i < count; i++) { int destination = (int)reader.ReadILUInt32() + jmpBase; offsetsToVisit.Push(destination); } // We fall through to the next basic block. offsetsToVisit.Push(reader.Offset); flags[offset] |= OpcodeFlags.EndBasicBlock; } else { reader.Skip(opcode); } if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0) { if (reader.HasNext) { // If the bytes following this basic block are not reachable from anywhere, // the sweeping step would consider them to be part of the last instruction // of the current basic block because of how instruction boundaries are identified. // We wouldn't NOP them out if the current basic block is reachable. // // That's a problem for RyuJIT because RyuJIT looks at these bytes for... reasons. // // We can just do the same thing as RyuJIT and consider those a basic block. offsetsToVisit.Push(reader.Offset); } break; } } } // Mark all reachable basic blocks // // We also do another round of basic block marking to mark beginning of visible basic blocks // after dead branch elimination. This allows us to limit the number of potential small basic blocks // that are not interesting (because no code jumps to them anymore), but could prevent us from // finishing the process. Unreachable basic blocks smaller than 2 bytes abort the substitution // inlining process because we can't neutralize them (turn them into an infinite loop). offsetsToVisit.Push(0); while (offsetsToVisit.TryPop(out int offset)) { // Mark as a basic block visible after constant propagation. flags[offset] |= OpcodeFlags.VisibleBasicBlockStart; // If this was already marked, we're done. if ((flags[offset] & OpcodeFlags.Mark) != 0) { continue; } ILReader reader = new ILReader(methodBytes, offset); while (reader.HasNext) { offset = reader.Offset; flags[offset] |= OpcodeFlags.Mark; ILOpcode opcode = reader.ReadILOpcode(); // Mark any applicable EH blocks foreach (ILExceptionRegion ehRegion in ehRegions) { int delta = offset - ehRegion.TryOffset; if (delta >= 0 && delta < ehRegion.TryLength) { if (ehRegion.Kind == ILExceptionRegionKind.Filter) { offsetsToVisit.Push(ehRegion.FilterOffset); } offsetsToVisit.Push(ehRegion.HandlerOffset); // RyuJIT is going to look at this basic block even though it's unreachable. // Consider it visible so that we replace the tail with an endless loop. int handlerEnd = ehRegion.HandlerOffset + ehRegion.HandlerLength; if (handlerEnd < flags.Length) { flags[handlerEnd] |= OpcodeFlags.VisibleBasicBlockStart; } } } // All branches are relevant to basic block tracking if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s || opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s) { int destination = reader.ReadBranchDestination(opcode); if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int constant)) { // Can't get the constant - both branches are live. offsetsToVisit.Push(destination); offsetsToVisit.Push(reader.Offset); } else if ((constant == 0 && (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s)) || (constant != 0 && (opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s))) { // Only the "branch taken" is live. // The fallthrough marks the beginning of a visible (but not live) basic block. offsetsToVisit.Push(destination); flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; } else { // Only fallthrough is live. // The "brach taken" marks the beginning of a visible (but not live) basic block. flags[destination] |= OpcodeFlags.VisibleBasicBlockStart; offsetsToVisit.Push(reader.Offset); } } else if (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s || 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)) { // Can't get the constant - both branches are live. offsetsToVisit.Push(destination); offsetsToVisit.Push(reader.Offset); } else if ((left == right && (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s) || (left != right) && (opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s))) { // Only the "branch taken" is live. // The fallthrough marks the beginning of a visible (but not live) basic block. offsetsToVisit.Push(destination); flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; } else { // Only fallthrough is live. // The "brach taken" marks the beginning of a visible (but not live) basic block. flags[destination] |= OpcodeFlags.VisibleBasicBlockStart; offsetsToVisit.Push(reader.Offset); } } else if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un || opcode == ILOpcode.leave || opcode == ILOpcode.leave_s) { int destination = reader.ReadBranchDestination(opcode); offsetsToVisit.Push(destination); if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s && opcode != ILOpcode.br && opcode != ILOpcode.br_s) { // Branches not tested for above are conditional and the flow falls through. offsetsToVisit.Push(reader.Offset); } else { // RyuJIT is going to look at this basic block even though it's unreachable. // Consider it visible so that we replace the tail with an endless loop. if (reader.HasNext) { flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; } } } else if (opcode == ILOpcode.switch_) { uint count = reader.ReadILUInt32(); int jmpBase = reader.Offset + (int)(4 * count); for (uint i = 0; i < count; i++) { int destination = (int)reader.ReadILUInt32() + jmpBase; offsetsToVisit.Push(destination); } offsetsToVisit.Push(reader.Offset); } else if (opcode == ILOpcode.ret || opcode == ILOpcode.endfilter || opcode == ILOpcode.endfinally || opcode == ILOpcode.throw_ || opcode == ILOpcode.rethrow || opcode == ILOpcode.jmp) { reader.Skip(opcode); // RyuJIT is going to look at this basic block even though it's unreachable. // Consider it visible so that we replace the tail with an endless loop. if (reader.HasNext) { flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; } } else { reader.Skip(opcode); } if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0) { break; } } } // Now sweep unreachable basic blocks by replacing them with nops bool hasUnmarkedIntructions = false; foreach (var flag in flags) { if ((flag & OpcodeFlags.InstructionStart) != 0 && (flag & OpcodeFlags.Mark) == 0) { hasUnmarkedIntructions = true; } } if (!hasUnmarkedIntructions) { return(method); } byte[] newBody = (byte[])methodBytes.Clone(); int position = 0; while (position < newBody.Length) { Debug.Assert((flags[position] & OpcodeFlags.InstructionStart) != 0); Debug.Assert((flags[position] & OpcodeFlags.VisibleBasicBlockStart) != 0); bool erase = (flags[position] & OpcodeFlags.Mark) == 0; int basicBlockStart = position; do { if (erase) { newBody[position] = (byte)ILOpCode.Nop; } position++; } while (position < newBody.Length && (flags[position] & OpcodeFlags.VisibleBasicBlockStart) == 0); // If we had to nop out this basic block, we need to neutralize it by appending // an infinite loop ("br $-2"). // We append instead of prepend because RyuJIT's importer has trouble with junk unreachable bytes. if (erase) { if (position - basicBlockStart < 2) { // We cannot neutralize the basic block, so better leave the method alone. // The control would fall through to the next basic block. return(method); } newBody[position - 2] = (byte)ILOpCode.Br_s; newBody[position - 1] = unchecked ((byte)-2); } } // EH regions with unmarked handlers belong to unmarked basic blocks // Need to eliminate them because they're not usable. ArrayBuilder <ILExceptionRegion> newEHRegions = new ArrayBuilder <ILExceptionRegion>(); foreach (ILExceptionRegion ehRegion in ehRegions) { if ((flags[ehRegion.HandlerOffset] & OpcodeFlags.Mark) != 0) { newEHRegions.Add(ehRegion); } } // Existing debug information might not match new instruction boundaries (plus there's little point // in generating debug information for NOPs) - generate new debug information by filtering // out the sequence points associated with nopped out instructions. MethodDebugInformation debugInfo = method.GetDebugInfo(); IEnumerable <ILSequencePoint> oldSequencePoints = debugInfo?.GetSequencePoints(); if (oldSequencePoints != null) { ArrayBuilder <ILSequencePoint> sequencePoints = new ArrayBuilder <ILSequencePoint>(); foreach (var sequencePoint in oldSequencePoints) { if (sequencePoint.Offset < flags.Length && (flags[sequencePoint.Offset] & OpcodeFlags.Mark) != 0) { sequencePoints.Add(sequencePoint); } } debugInfo = new SubstitutedDebugInformation(debugInfo, sequencePoints.ToArray()); } return(new SubstitutedMethodIL(method, newBody, newEHRegions.ToArray(), debugInfo)); }
public static FlowGraph Create(MethodIL il) { HashSet <int> bbStarts = GetBasicBlockStarts(il); List <BasicBlock> bbs = new List <BasicBlock>(); void AddBB(int start, int count) { if (count > 0) { bbs.Add(new BasicBlock(start, count)); } } int prevStart = 0; foreach (int ofs in bbStarts.OrderBy(o => o)) { AddBB(prevStart, ofs - prevStart); prevStart = ofs; } AddBB(prevStart, il.GetILBytes().Length - prevStart); FlowGraph fg = new FlowGraph(bbs); // We know where each basic block starts now. Proceed by linking them together. ILReader reader = new ILReader(il.GetILBytes()); foreach (BasicBlock bb in bbs) { reader.Seek(bb.Start); while (reader.HasNext) { Debug.Assert(fg.Lookup(reader.Offset) == bb); ILOpcode opc = reader.ReadILOpcode(); if (opc.IsBranch()) { int tar = reader.ReadBranchDestination(opc); bb.Targets.Add(fg.Lookup(tar)); if (!opc.IsUnconditionalBranch()) { bb.Targets.Add(fg.Lookup(reader.Offset)); } break; } if (opc == ILOpcode.switch_) { uint numCases = reader.ReadILUInt32(); int jmpBase = reader.Offset + checked ((int)(numCases * 4)); bb.Targets.Add(fg.Lookup(jmpBase)); for (uint i = 0; i < numCases; i++) { int caseOfs = jmpBase + (int)reader.ReadILUInt32(); bb.Targets.Add(fg.Lookup(caseOfs)); } break; } if (opc == ILOpcode.ret || opc == ILOpcode.endfinally || opc == ILOpcode.endfilter || opc == ILOpcode.throw_ || opc == ILOpcode.rethrow) { break; } reader.Skip(opc); // Check fall through if (reader.HasNext) { BasicBlock nextBB = fg.Lookup(reader.Offset); if (nextBB != bb) { // Falling through bb.Targets.Add(nextBB); break; } } } } foreach (BasicBlock bb in bbs) { foreach (BasicBlock tar in bb.Targets) { tar.Sources.Add(bb); } } return(fg); }
/// <summary> /// Parse MIbcGroup method and return enumerable of MethodProfileData /// /// Like the AssemblyDictionary method, data is encoded via IL instructions. The format is /// /// ldtoken methodInProfileData /// Any series of instructions that does not include pop. Expansion data is encoded via ldstr "id" /// followed by a expansion specific sequence of il opcodes. /// pop /// {Repeat N times for N methods described} /// /// Extensions supported with current parser: /// /// ldstr "ExclusiveWeight" /// Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight /// /// ldstr "WeightedCallData" /// ldc.i4 <Count of methods called> /// Repeat <Count of methods called times> /// ldtoken <Method called from this method> /// ldc.i4 <Weight associated with calling the <Method called from this method>> /// /// This format is designed to be extensible to hold more data as we add new per method profile data without breaking existing parsers. /// </summary> static IEnumerable <MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaMethod method) { EcmaMethodIL ilBody = EcmaMethodIL.Create(method); MetadataLoaderForPgoData metadataLoader = new MetadataLoaderForPgoData(ilBody); ILReader ilReader = new ILReader(ilBody.GetILBytes()); object methodInProgress = null; object metadataNotResolvable = new object(); object metadataObject = null; MibcGroupParseState state = MibcGroupParseState.LookingForNextMethod; int intValue = 0; int weightedCallGraphSize = 0; int profileEntryFound = 0; double exclusiveWeight = 0; Dictionary <MethodDesc, int> weights = null; bool processIntValue = false; List <long> instrumentationDataLongs = null; PgoSchemaElem[] pgoSchemaData = null; while (ilReader.HasNext) { ILOpcode opcode = ilReader.ReadILOpcode(); processIntValue = false; switch (opcode) { case ILOpcode.ldtoken: { int token = ilReader.ReadILToken(); if (state == MibcGroupParseState.ProcessingInstrumentationData) { instrumentationDataLongs.Add(token); } else { metadataObject = null; try { metadataObject = ilBody.GetObject(token); } catch (TypeSystemException) { // The method being referred to may be missing. In that situation, // use the metadataNotResolvable sentinel to indicate that this record should be ignored metadataObject = metadataNotResolvable; } switch (state) { case MibcGroupParseState.ProcessingCallgraphToken: state = MibcGroupParseState.ProcessingCallgraphWeight; break; case MibcGroupParseState.LookingForNextMethod: methodInProgress = metadataObject; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } } } break; case ILOpcode.ldc_r4: { float fltValue = ilReader.ReadILFloat(); switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = fltValue; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } break; } case ILOpcode.ldc_r8: { double dblValue = ilReader.ReadILDouble(); switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = dblValue; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } break; } case ILOpcode.ldc_i4_0: intValue = 0; processIntValue = true; break; case ILOpcode.ldc_i4_1: intValue = 1; processIntValue = true; break; case ILOpcode.ldc_i4_2: intValue = 2; processIntValue = true; break; case ILOpcode.ldc_i4_3: intValue = 3; processIntValue = true; break; case ILOpcode.ldc_i4_4: intValue = 4; processIntValue = true; break; case ILOpcode.ldc_i4_5: intValue = 5; processIntValue = true; break; case ILOpcode.ldc_i4_6: intValue = 6; processIntValue = true; break; case ILOpcode.ldc_i4_7: intValue = 7; processIntValue = true; break; case ILOpcode.ldc_i4_8: intValue = 8; processIntValue = true; break; case ILOpcode.ldc_i4_m1: intValue = -1; processIntValue = true; break; case ILOpcode.ldc_i4_s: intValue = (sbyte)ilReader.ReadILByte(); processIntValue = true; break; case ILOpcode.ldc_i4: intValue = (int)ilReader.ReadILUInt32(); processIntValue = true; break; case ILOpcode.ldc_i8: if (state == MibcGroupParseState.ProcessingInstrumentationData) { instrumentationDataLongs.Add((long)ilReader.ReadILUInt64()); } break; case ILOpcode.ldstr: { int userStringToken = ilReader.ReadILToken(); string optionalDataName = (string)ilBody.GetObject(userStringToken); switch (optionalDataName) { case "ExclusiveWeight": state = MibcGroupParseState.ProcessingExclusiveWeight; break; case "WeightedCallData": state = MibcGroupParseState.ProcessingCallgraphCount; break; case "InstrumentationDataStart": state = MibcGroupParseState.ProcessingInstrumentationData; instrumentationDataLongs = new List <long>(); break; case "InstrumentationDataEnd": if (instrumentationDataLongs != null) { instrumentationDataLongs.Add(2); // MarshalMask 2 (Type) instrumentationDataLongs.Add(0); // PgoInstrumentationKind.Done (0) pgoSchemaData = PgoProcessor.ParsePgoData <TypeSystemEntityOrUnknown>(metadataLoader, instrumentationDataLongs, false).ToArray(); } state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } } break; case ILOpcode.pop: if (methodInProgress != metadataNotResolvable) { profileEntryFound++; if (exclusiveWeight == 0) { // If no exclusive weight is found assign a non zero value that assumes the order in the pgo file is significant. exclusiveWeight = Math.Min(1000000.0 - profileEntryFound, 0.0) / 1000000.0; } MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF, pgoSchemaData); state = MibcGroupParseState.LookingForNextMethod; exclusiveWeight = 0; weights = null; instrumentationDataLongs = null; pgoSchemaData = null; yield return(mibcData); } methodInProgress = null; break; default: state = MibcGroupParseState.LookingForOptionalData; ilReader.Skip(opcode); break; } if (processIntValue) { switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = intValue; state = MibcGroupParseState.LookingForOptionalData; break; case MibcGroupParseState.ProcessingCallgraphCount: weightedCallGraphSize = intValue; weights = new Dictionary <MethodDesc, int>(); if (weightedCallGraphSize > 0) { state = MibcGroupParseState.ProcessingCallgraphToken; } else { state = MibcGroupParseState.LookingForOptionalData; } break; case MibcGroupParseState.ProcessingCallgraphWeight: if (metadataObject != metadataNotResolvable) { weights.Add((MethodDesc)metadataObject, intValue); } weightedCallGraphSize--; if (weightedCallGraphSize > 0) { state = MibcGroupParseState.ProcessingCallgraphToken; } else { state = MibcGroupParseState.LookingForOptionalData; } break; case MibcGroupParseState.ProcessingInstrumentationData: instrumentationDataLongs.Add(intValue); break; default: state = MibcGroupParseState.LookingForOptionalData; instrumentationDataLongs = null; break; } } } }
public void Scan(MethodIL methodBody) { 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); MethodReturnValue = null; ILReader reader = new ILReader(methodBody.GetILBytes()); while (reader.HasNext) { int curBasicBlock = blockIterator.MoveNext(reader.Offset); if (knownStacks.ContainsKey(reader.Offset)) { if (currentStack == null) { // The stack copy constructor reverses the stack currentStack = new Stack <StackSlot>(knownStacks[reader.Offset].Reverse()); } else { currentStack = MergeStack(currentStack, knownStacks[reader.Offset]); } } if (currentStack == null) { 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.ldftn: case ILOpcode.sizeof_: case ILOpcode.ldc_i8: case ILOpcode.ldc_r4: case ILOpcode.ldc_r8: PushUnknown(currentStack); reader.Skip(opcode); 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;