public OpcodeTraits(OpcodeFlags flags, String mnemonic, byte opcode, bool extended) { this.flags = flags; this.mnemonic = mnemonic; this.opcode = opcode; this.extended = extended; }
public void Add( OpcodeKind kind, byte number, string name, OpcodeFlags flags = OpcodeFlags.None) { opcodes[((int)kind * 32) + number] = new Opcode(kind, number, name, flags); }
internal Opcode(OpcodeID id) { this.opcodeID = id; this.flags = OpcodeFlags.Single; #if DEBUG this.uniqueID = Opcode.NextUniqueId(); #endif }
internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id) { this.criteria = criteria; base.flags |= flags | OpcodeFlags.Select; if (criteria.IsCompressable && ((base.flags & OpcodeFlags.InitialSelect) == OpcodeFlags.None)) { base.flags |= OpcodeFlags.CompressableSelect; } }
internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id) { this.criteria = criteria; base.flags |= flags | OpcodeFlags.Select; if (criteria.IsCompressable && ((base.flags & OpcodeFlags.InitialSelect) == OpcodeFlags.None)) { base.flags |= OpcodeFlags.CompressableSelect; } }
internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id) { this.criteria = criteria; this.flags |= (flags | OpcodeFlags.Select); if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect))) { this.flags |= OpcodeFlags.CompressableSelect; } }
public Opcode(Opcodes opcode, OpcodeFlags flags, uint16_t opA, uint16_t opB) { this.opcode = opcode; operandA = opA; operandB = opB; this.flags = flags; ptr = new SmartPointer(0, 8); }
public void FromBinary(byte[] value) { if (value.Length != 8) { throw new IndexOutOfRangeException(); } opcode = (Opcodes)BitConverter.ToUInt16(value, 0); flags = (OpcodeFlags)BitConverter.ToUInt16(value, 2); operandA = new uint16_t(BitConverter.ToUInt16(value, 4)); operandB = new uint16_t(BitConverter.ToUInt16(value, 6)); }
private static void AddOpcode( OpcodeKind kind, byte number, string name, OpcodeFlags flags = OpcodeFlags.None, byte fromVersion = 1, byte toVersion = 8) { for (byte v = fromVersion; v <= toVersion; v++) { opcodeTables[v - 1].Add(kind, number, name, flags); } }
internal Opcode(OpcodeKind kind, byte number, string name, OpcodeFlags flags) { this.Kind = kind; this.Number = number; this.Name = name; this.HasStoreVariable = (flags & OpcodeFlags.Store) != 0; this.HasBranch = (flags & OpcodeFlags.Branch) != 0; this.HasZText = (flags & OpcodeFlags.ZText) != 0; this.IsCall = (flags & OpcodeFlags.Call) != 0; this.IsDoubleVariable = (flags & OpcodeFlags.DoubleVar) != 0; this.IsFirstOpByRef = (flags & OpcodeFlags.FirstOpByRef) != 0; this.IsJump = kind == OpcodeKind.OneOp && number == 0x0c; this.IsQuit = kind == OpcodeKind.ZeroOp && number == 0x0a; this.IsReturn = (flags & OpcodeFlags.Return) != 0; }
internal SelectOpcode(OpcodeID id, NodeSelectCriteria criteria, OpcodeFlags flags) : base(id) { this.criteria = criteria; this.flags |= (flags | OpcodeFlags.Select); if (criteria.IsCompressable && (0 == (this.flags & OpcodeFlags.InitialSelect))) { this.flags |= OpcodeFlags.CompressableSelect; } }
Opcodes ParseOpcode(string operation, OpcodeFlags flags) { Func <bool> no = () => flags == OpcodeFlags.NoOperands; Func <bool> ra = () => flags.HasFlag(OpcodeFlags.Register1); Func <bool> rb = () => flags.HasFlag(OpcodeFlags.Register2); Func <bool> la = () => flags.HasFlag(OpcodeFlags.Literal1); Func <bool> lb = () => flags.HasFlag(OpcodeFlags.Literal2); Func <Opcodes, Opcodes, Opcodes, Opcodes, Opcodes> RR_LR_RL_LL = (rr, lr, rl, ll) => { if (ra() & rb()) { return(rr); } else if (ra() & lb()) { return(rl); } else if (la() & rb()) { return(lr); } else if (la() & la()) { return(ll); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes, Opcodes> RR_LR = (rr, lr) => { if (ra() & rb()) { return(rr); } if (la() & rb()) { return(lr); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes, Opcodes> RR_RL = (r, l) => { if (ra() & rb()) { return(r); } else if (ra() & lb()) { return(l); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes> NoOps = o => { if (no()) { return(o); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes> LR = l => { if (lb() | ra()) { InvalidOperands(); } if (la() & rb()) { return(l); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes, Opcodes> R_L = (r, l) => { if (rb() | lb()) { InvalidOperands(); } if (ra()) { return(r); } else if (la()) { return(l); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes> R = (r) => { if (rb() | lb() | la()) { InvalidOperands(); } if (ra()) { return(r); } else { InvalidOperands(); return(Opcodes.nop); } }; Func <Opcodes, Opcodes> L = (l) => { if (rb() | lb() | ra()) { InvalidOperands(); } if (la()) { return(l); } else { InvalidOperands(); return(Opcodes.nop); } }; switch (operation) { case "nop": return(NoOps(Opcodes.nop)); case "read": return(RR_RL(Opcodes.readr, Opcodes.readl)); case "write": return(RR_LR_RL_LL(Opcodes.writerr, Opcodes.writelr, Opcodes.writerl, Opcodes.writell)); case "push": return(R_L(Opcodes.pushr, Opcodes.pushl)); case "mov": return(RR_RL(Opcodes.movr, Opcodes.movl)); case "add": return(RR_RL(Opcodes.addr, Opcodes.addl)); case "sub": return(RR_RL(Opcodes.subr, Opcodes.subl)); case "mult": return(RR_RL(Opcodes.multr, Opcodes.multl)); case "div": return(RR_RL(Opcodes.divr, Opcodes.divl)); case "mod": return(RR_RL(Opcodes.modr, Opcodes.modl)); case "and": return(RR_RL(Opcodes.andr, Opcodes.andl)); case "or": return(RR_RL(Opcodes.orr, Opcodes.orl)); case "xor": return(RR_RL(Opcodes.xorr, Opcodes.xorl)); case "not": return(R(Opcodes.not)); case "neg": return(R(Opcodes.neg)); case "pop": return(R(Opcodes.pop)); case "pusha": return(NoOps(Opcodes.pusha)); case "popa": return(NoOps(Opcodes.popa)); case "hlt": return(NoOps(Opcodes.hlt)); case "int": return(R_L(Opcodes.intr, Opcodes.intl)); case "call": return(R_L(Opcodes.callr, Opcodes.calll)); case "jmp": return(R_L(Opcodes.jmpr, Opcodes.jmpl)); case "je": return(L(Opcodes.je)); case "jne": return(L(Opcodes.jne)); case "jg": return(L(Opcodes.jg)); case "jge": return(L(Opcodes.jge)); case "jl": return(L(Opcodes.jl)); case "jle": return(L(Opcodes.jle)); case "cmp": return(RR_RL(Opcodes.cmpr, Opcodes.cmpl)); case "ret": return(NoOps(Opcodes.ret)); case "deref": return(RR_RL(Opcodes.derefr, Opcodes.derefl)); case "brk": return(NoOps(Opcodes.brk)); case "cpuid": return(NoOps(Opcodes.cpuid)); case "out": return(NoOps(Opcodes._out)); case "in": return(NoOps(Opcodes._in)); case "sa": return(RR_LR_RL_LL(Opcodes.sarr, Opcodes.salr, Opcodes.sarl, Opcodes.sall)); case "sai": return(R_L(Opcodes.sair, Opcodes.sail)); case "inca": return(NoOps(Opcodes.inca)); case "deca": return(NoOps(Opcodes.deca)); case "ls": return(RR_RL(Opcodes.lsr, Opcodes.lsl)); case "rs": return(RR_RL(Opcodes.rsr, Opcodes.rsl)); } RaiseError(string.Format("Invalid instruction \"{0}\"", operation)); return(Opcodes.nop); }
void ParseInstruction(string line) { Match match = Regex.Match(line, instructionCapture); string operation = match.Groups[1].Value; string opA = match.Groups[2].Value; string opB = match.Groups[4].Value; uint16_t operandA = Int.UInt16; uint16_t operandB = Int.UInt16; OpcodeFlags flags = OpcodeFlags.NoOperands; Func <bool> era = () => { uint16_t temp; if (ExpectRegister(opA, out temp)) { operandA = temp; return(true); } else { return(false); } }; Func <bool> erb = () => { uint16_t temp; if (ExpectRegister(opB, out temp)) { operandB = temp; return(true); } else { return(false); } }; Func <bool> ela = () => { uint16_t temp; if (ExpectLiteral(opA, out temp)) { operandA = temp; return(true); } else { return(false); } }; Func <bool> elb = () => { uint16_t temp; if (ExpectLiteral(opB, out temp)) { operandB = temp; return(true); } else { return(false); } }; Func <bool> esa = () => { uint16_t temp; if (flags.HasFlag(OpcodeFlags.Literal1)) { return(false); } if (ExpectSymbol(opA, out temp)) { operandA = temp; return(true); } else { return(false); } }; Func <bool> esb = () => { uint16_t temp; if (flags.HasFlag(OpcodeFlags.Literal2)) { return(false); } if (ExpectSymbol(opB, out temp)) { operandB = temp; return(true); } else { return(false); } }; try { if (era()) { flags = flags | OpcodeFlags.Register1; } if (erb()) { flags = flags | OpcodeFlags.Register2; } if (ela()) { flags = flags | OpcodeFlags.Literal1; } if (elb()) { flags = flags | OpcodeFlags.Literal2; } if (esa()) { flags = flags | OpcodeFlags.Literal1; } if (esb()) { flags = flags | OpcodeFlags.Literal2; } Opcodes opcode = ParseOpcode(operation, flags); Opcode oc = new Opcode(opcode, flags, operandA, operandB); buildList.Add(string.Format("${0}", opcodes.Count)); opcodes.Add(oc); address += 8; } catch { throw; } }
internal bool TestFlag(OpcodeFlags flag) { return (0 != (this.flags & flag)); }
public FlagProcessor(OpcodeFlags opcodeFlags, Flags flags) { this.opcodeFlags = opcodeFlags; this.flags = flags; }
internal bool TestFlag(OpcodeFlags flag) { return(OpcodeFlags.None != (this.flags & flag)); }
internal Opcode(OpcodeID id) { this.opcodeID = id; this.flags = OpcodeFlags.Single; }
internal Opcode(OpcodeID id) { this.opcodeID = id; this.flags = OpcodeFlags.Single; }
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)); }
internal Opcode(OpcodeID id) { this.opcodeID = id; this.flags = OpcodeFlags.Single; #if DEBUG this.uniqueID = Opcode.NextUniqueId(); #endif }