internal void DoJump(CompilerContext cc) { Contract.Requires(cc != null); // The state have to be already known. The _doJump() method is called by // translate() or by Compiler in case that it's forward jump. if (_jumpTarget.State == null) throw new CompilerException("Cannot jump to a target without knowing its state."); if ((Code == InstructionCode.Jmp) || (IsTaken && _jumpTarget.Offset < Offset)) { // Instruction type is JMP or conditional jump that should be taken (likely). // We can set state here instead of jumping out, setting state and jumping // to _jumpTarget. // // NOTE: We can't use this technique if instruction is forward conditional // jump. The reason is that when generating code we can't change state here, // because the next instruction depends on it. cc.RestoreState(_jumpTarget.State, _jumpTarget.Offset); } else { // Instruction type is JMP or conditional jump that should be not normally // taken. If we need add code that will switch between different states we // add it after the end of function body (after epilog, using 'ExtraBlock'). Compiler compiler = cc.Compiler; CompilerItem ext = cc.ExtraBlock; CompilerItem old = compiler.CurrentItem; compiler.CurrentItem = ext; cc.RestoreState(_jumpTarget.State, _jumpTarget.Offset); if (compiler.CurrentItem != ext) { // Add the jump to the target. compiler.Jmp(_jumpTarget.Label); ext = compiler.CurrentItem; // The cc._restoreState() method emitted some instructions so we need to // patch the jump. Label L = compiler.DefineLabel(); compiler.CurrentItem = cc.ExtraBlock; compiler.MarkLabel(L); // Finally, patch the jump target. if (Operands.Length == 0) throw new CompilerException(); Operands[0] = L; // Operand part (Label). _jumpTarget = compiler.GetTarget(L.Id); // Item part (ETarget). } cc.ExtraBlock = ext; compiler.CurrentItem = old; // Assign state back. cc.AssignState(_state); } }
protected override CompilerItem TranslateImpl(CompilerContext cc) { // If this Target was already translated, it's needed to change the current // state and return null to tell CompilerContext to process next untranslated // item. if (IsTranslated) { cc.RestoreState(_state); return null; } if (cc.Unreachable) { // If the context has "isUnreachable" flag set and there is no state then // it means that this code will be never called. This is a problem, because // we are unable to assign a state to current location so we can't allocate // registers for variables used inside. So instead of doing anything wrong // we remove the unreachable code. if (_state == null) return RemoveUnreachableItems(); // Assign state to the compiler context. cc.Unreachable = false; cc.AssignState(_state); } else { _state = cc.SaveState(); } return Next; }