diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2010-05-04 20:50:39 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2010-05-04 20:50:39 +0000 |
commit | 377552607e51dc1d3e6ff33833f9620bcfe815ac (patch) | |
tree | 3c3ffb5df9fa6dfb2c48b807faf73dd2943db75d /lib | |
parent | 750ce4d809c7e2a298a389a512a17652ff5be3f2 (diff) | |
download | FreeBSD-src-377552607e51dc1d3e6ff33833f9620bcfe815ac.zip FreeBSD-src-377552607e51dc1d3e6ff33833f9620bcfe815ac.tar.gz |
Update LLVM to r103052.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/CodeGen/LiveIntervalAnalysis.cpp | 24 | ||||
-rw-r--r-- | lib/CodeGen/PHIElimination.cpp | 81 | ||||
-rw-r--r-- | lib/CodeGen/PHIElimination.h | 2 | ||||
-rw-r--r-- | lib/Target/ARM/ARMISelDAGToDAG.cpp | 13 | ||||
-rw-r--r-- | lib/Target/ARM/NEONPreAllocPass.cpp | 28 | ||||
-rw-r--r-- | lib/Target/CellSPU/SPUISelDAGToDAG.cpp | 12 | ||||
-rw-r--r-- | lib/Target/X86/AsmParser/X86AsmParser.cpp | 60 | ||||
-rw-r--r-- | lib/Target/X86/X86.td | 5 | ||||
-rw-r--r-- | lib/Transforms/Scalar/GVN.cpp | 9 | ||||
-rw-r--r-- | lib/VMCore/Metadata.cpp | 42 |
10 files changed, 236 insertions, 40 deletions
diff --git a/lib/CodeGen/LiveIntervalAnalysis.cpp b/lib/CodeGen/LiveIntervalAnalysis.cpp index 26a7190..ca9921c 100644 --- a/lib/CodeGen/LiveIntervalAnalysis.cpp +++ b/lib/CodeGen/LiveIntervalAnalysis.cpp @@ -262,6 +262,23 @@ static void printRegName(unsigned reg, const TargetRegisterInfo* tri_) { } #endif +static +bool MultipleDefsByMI(const MachineInstr &MI, unsigned MOIdx) { + unsigned Reg = MI.getOperand(MOIdx).getReg(); + for (unsigned i = MOIdx+1, e = MI.getNumOperands(); i < e; ++i) { + const MachineOperand &MO = MI.getOperand(i); + if (!MO.isReg()) + continue; + if (MO.getReg() == Reg && MO.isDef()) { + assert(MI.getOperand(MOIdx).getSubReg() != MO.getSubReg() && + MI.getOperand(MOIdx).getSubReg() && + MO.getSubReg()); + return true; + } + } + return false; +} + void LiveIntervals::handleVirtualRegisterDef(MachineBasicBlock *mbb, MachineBasicBlock::iterator mi, SlotIndex MIIdx, @@ -372,6 +389,13 @@ void LiveIntervals::handleVirtualRegisterDef(MachineBasicBlock *mbb, } } else { + if (MultipleDefsByMI(*mi, MOIdx)) + // Mutple defs of the same virtual register by the same instruction. e.g. + // %reg1031:5<def>, %reg1031:6<def> = VLD1q16 %reg1024<kill>, ... + // This is likely due to elimination of REG_SEQUENCE instructions. Return + // here since there is nothing to do. + return; + // If this is the second time we see a virtual register definition, it // must be due to phi elimination or two addr elimination. If this is // the result of two address elimination, then the vreg is one of the diff --git a/lib/CodeGen/PHIElimination.cpp b/lib/CodeGen/PHIElimination.cpp index f0057ce..1651719 100644 --- a/lib/CodeGen/PHIElimination.cpp +++ b/lib/CodeGen/PHIElimination.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include <algorithm> #include <map> using namespace llvm; @@ -52,22 +53,22 @@ void llvm::PHIElimination::getAnalysisUsage(AnalysisUsage &AU) const { MachineFunctionPass::getAnalysisUsage(AU); } -bool llvm::PHIElimination::runOnMachineFunction(MachineFunction &Fn) { - MRI = &Fn.getRegInfo(); +bool llvm::PHIElimination::runOnMachineFunction(MachineFunction &MF) { + MRI = &MF.getRegInfo(); bool Changed = false; // Split critical edges to help the coalescer if (LiveVariables *LV = getAnalysisIfAvailable<LiveVariables>()) - for (MachineFunction::iterator I = Fn.begin(), E = Fn.end(); I != E; ++I) - Changed |= SplitPHIEdges(Fn, *I, *LV); + for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) + Changed |= SplitPHIEdges(MF, *I, *LV); // Populate VRegPHIUseCount - analyzePHINodes(Fn); + analyzePHINodes(MF); // Eliminate PHI instructions by inserting copies into predecessor blocks. - for (MachineFunction::iterator I = Fn.begin(), E = Fn.end(); I != E; ++I) - Changed |= EliminatePHINodes(Fn, *I); + for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) + Changed |= EliminatePHINodes(MF, *I); // Remove dead IMPLICIT_DEF instructions. for (SmallPtrSet<MachineInstr*, 4>::iterator I = ImpDefs.begin(), @@ -81,11 +82,16 @@ bool llvm::PHIElimination::runOnMachineFunction(MachineFunction &Fn) { // Clean up the lowered PHI instructions. for (LoweredPHIMap::iterator I = LoweredPHIs.begin(), E = LoweredPHIs.end(); I != E; ++I) - Fn.DeleteMachineInstr(I->first); + MF.DeleteMachineInstr(I->first); LoweredPHIs.clear(); ImpDefs.clear(); VRegPHIUseCount.clear(); + + // Eliminate REG_SEQUENCE instructions. Their whole purpose was to preseve + // SSA form. + Changed |= EliminateRegSequences(MF); + return Changed; } @@ -364,8 +370,8 @@ void llvm::PHIElimination::LowerAtomicPHINode( /// used in a PHI node. We map that to the BB the vreg is coming from. This is /// used later to determine when the vreg is killed in the BB. /// -void llvm::PHIElimination::analyzePHINodes(const MachineFunction& Fn) { - for (MachineFunction::const_iterator I = Fn.begin(), E = Fn.end(); +void llvm::PHIElimination::analyzePHINodes(const MachineFunction& MF) { + for (MachineFunction::const_iterator I = MF.begin(), E = MF.end(); I != E; ++I) for (MachineBasicBlock::const_iterator BBI = I->begin(), BBE = I->end(); BBI != BBE && BBI->isPHI(); ++BBI) @@ -443,3 +449,58 @@ MachineBasicBlock *PHIElimination::SplitCriticalEdge(MachineBasicBlock *A, return NMBB; } + +static void UpdateRegSequenceSrcs(unsigned SrcReg, + unsigned DstReg, unsigned SrcIdx, + MachineRegisterInfo *MRI) { + for (MachineRegisterInfo::reg_iterator RI = MRI->reg_begin(SrcReg), + UE = MRI->reg_end(); RI != UE; ) { + MachineOperand &MO = RI.getOperand(); + ++RI; + MO.setReg(DstReg); + MO.setSubReg(SrcIdx); + } +} + +/// EliminateRegSequences - Eliminate REG_SEQUENCE instructions as second part +/// of de-ssa process. This replaces sources of REG_SEQUENCE as sub-register +/// references of the register defined by REG_SEQUENCE. e.g. +/// +/// %reg1029<def>, %reg1030<def> = VLD1q16 %reg1024<kill>, ... +/// %reg1031<def> = REG_SEQUENCE %reg1029<kill>, 5, %reg1030<kill>, 6 +/// => +/// %reg1031:5<def>, %reg1031:6<def> = VLD1q16 %reg1024<kill>, ... +bool PHIElimination::EliminateRegSequences(MachineFunction &MF) { + bool Changed = false; + + for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) + for (MachineBasicBlock::iterator BBI = I->begin(), BBE = I->end(); + BBI != BBE; ) { + MachineInstr &MI = *BBI; + ++BBI; + if (MI.getOpcode() != TargetOpcode::REG_SEQUENCE) + continue; + unsigned DstReg = MI.getOperand(0).getReg(); + if (MI.getOperand(0).getSubReg() || + TargetRegisterInfo::isPhysicalRegister(DstReg) || + !(MI.getNumOperands() & 1)) { + DEBUG(dbgs() << "Illegal REG_SEQUENCE instruction:" << MI); + llvm_unreachable(0); + } + for (unsigned i = 1, e = MI.getNumOperands(); i < e; i += 2) { + unsigned SrcReg = MI.getOperand(i).getReg(); + if (MI.getOperand(i).getSubReg() || + TargetRegisterInfo::isPhysicalRegister(SrcReg)) { + DEBUG(dbgs() << "Illegal REG_SEQUENCE instruction:" << MI); + llvm_unreachable(0); + } + unsigned SrcIdx = MI.getOperand(i+1).getImm(); + UpdateRegSequenceSrcs(SrcReg, DstReg, SrcIdx, MRI); + } + + MI.eraseFromParent(); + Changed = true; + } + + return Changed; +} diff --git a/lib/CodeGen/PHIElimination.h b/lib/CodeGen/PHIElimination.h index 7dedf03..3292aa2 100644 --- a/lib/CodeGen/PHIElimination.h +++ b/lib/CodeGen/PHIElimination.h @@ -94,6 +94,8 @@ namespace llvm { return I; } + bool EliminateRegSequences(MachineFunction &MF); + typedef std::pair<unsigned, unsigned> BBVRegPair; typedef DenseMap<BBVRegPair, unsigned> VRegPHIUse; diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index 36a1827..616942c 100644 --- a/lib/Target/ARM/ARMISelDAGToDAG.cpp +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -27,6 +27,7 @@ #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/Target/TargetLowering.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -34,6 +35,10 @@ using namespace llvm; +static cl::opt<bool> +UseRegSeq("neon-reg-sequence", cl::Hidden, + cl::desc("Use reg_sequence to model ld / st of multiple neon regs")); + //===--------------------------------------------------------------------===// /// ARMDAGToDAGISel - ARM specific code to select ARM machine /// instructions for SelectionDAG operations. @@ -939,10 +944,14 @@ SDNode *ARMDAGToDAGISel::SelectT2IndexedLoad(SDNode *N) { /// form a quad register. SDNode *ARMDAGToDAGISel::PairDRegs(EVT VT, SDValue V0, SDValue V1) { DebugLoc dl = V0.getNode()->getDebugLoc(); - SDValue Undef = - SDValue(CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, dl, VT), 0); SDValue SubReg0 = CurDAG->getTargetConstant(ARM::DSUBREG_0, MVT::i32); SDValue SubReg1 = CurDAG->getTargetConstant(ARM::DSUBREG_1, MVT::i32); + if (UseRegSeq) { + const SDValue Ops[] = { V0, SubReg0, V1, SubReg1 }; + return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops, 4); + } + SDValue Undef = + SDValue(CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, dl, VT), 0); SDNode *Pair = CurDAG->getMachineNode(TargetOpcode::INSERT_SUBREG, dl, VT, Undef, V0, SubReg0); return CurDAG->getMachineNode(TargetOpcode::INSERT_SUBREG, dl, diff --git a/lib/Target/ARM/NEONPreAllocPass.cpp b/lib/Target/ARM/NEONPreAllocPass.cpp index 7334259..ef6bf3a 100644 --- a/lib/Target/ARM/NEONPreAllocPass.cpp +++ b/lib/Target/ARM/NEONPreAllocPass.cpp @@ -12,12 +12,14 @@ #include "ARMInstrInfo.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/MachineFunctionPass.h" using namespace llvm; namespace { class NEONPreAllocPass : public MachineFunctionPass { const TargetInstrInfo *TII; + MachineRegisterInfo *MRI; public: static char ID; @@ -30,6 +32,8 @@ namespace { } private: + bool FormsRegSequence(MachineInstr *MI, + unsigned FirstOpnd, unsigned NumRegs); bool PreAllocNEONRegisters(MachineBasicBlock &MBB); }; @@ -334,6 +338,27 @@ static bool isNEONMultiRegOp(int Opcode, unsigned &FirstOpnd, unsigned &NumRegs, return false; } +bool NEONPreAllocPass::FormsRegSequence(MachineInstr *MI, + unsigned FirstOpnd, unsigned NumRegs) { + MachineInstr *RegSeq = 0; + for (unsigned R = 0; R < NumRegs; ++R) { + MachineOperand &MO = MI->getOperand(FirstOpnd + R); + assert(MO.isReg() && MO.getSubReg() == 0 && "unexpected operand"); + unsigned VirtReg = MO.getReg(); + assert(TargetRegisterInfo::isVirtualRegister(VirtReg) && + "expected a virtual register"); + if (!MRI->hasOneNonDBGUse(VirtReg)) + return false; + MachineInstr *UseMI = &*MRI->use_nodbg_begin(VirtReg); + if (UseMI->getOpcode() != TargetOpcode::REG_SEQUENCE) + return false; + if (RegSeq && RegSeq != UseMI) + return false; + RegSeq = UseMI; + } + return true; +} + bool NEONPreAllocPass::PreAllocNEONRegisters(MachineBasicBlock &MBB) { bool Modified = false; @@ -343,6 +368,8 @@ bool NEONPreAllocPass::PreAllocNEONRegisters(MachineBasicBlock &MBB) { unsigned FirstOpnd, NumRegs, Offset, Stride; if (!isNEONMultiRegOp(MI->getOpcode(), FirstOpnd, NumRegs, Offset, Stride)) continue; + if (FormsRegSequence(MI, FirstOpnd, NumRegs)) + continue; MachineBasicBlock::iterator NextI = llvm::next(MBBI); for (unsigned R = 0; R < NumRegs; ++R) { @@ -382,6 +409,7 @@ bool NEONPreAllocPass::PreAllocNEONRegisters(MachineBasicBlock &MBB) { bool NEONPreAllocPass::runOnMachineFunction(MachineFunction &MF) { TII = MF.getTarget().getInstrInfo(); + MRI = &MF.getRegInfo(); bool Modified = false; for (MachineFunction::iterator MFI = MF.begin(), E = MF.end(); MFI != E; diff --git a/lib/Target/CellSPU/SPUISelDAGToDAG.cpp b/lib/Target/CellSPU/SPUISelDAGToDAG.cpp index c3c2b39..9afdb2b 100644 --- a/lib/Target/CellSPU/SPUISelDAGToDAG.cpp +++ b/lib/Target/CellSPU/SPUISelDAGToDAG.cpp @@ -941,13 +941,21 @@ SPUDAGToDAGISel::Select(SDNode *N) { && ((RN = dyn_cast<RegisterSDNode>(Op0.getNode())) != 0 && RN->getReg() != SPU::R1))) { NewOpc = SPU::Ar32; + Ops[1] = Op1; if (Op1.getOpcode() == ISD::Constant) { ConstantSDNode *CN = cast<ConstantSDNode>(Op1); Op1 = CurDAG->getTargetConstant(CN->getSExtValue(), VT); - NewOpc = (isI32IntS10Immediate(CN) ? SPU::AIr32 : SPU::Ar32); + if (isInt<10>(CN->getSExtValue())) { + NewOpc = SPU::AIr32; + Ops[1] = Op1; + } else { + Ops[1] = SDValue(CurDAG->getMachineNode(SPU::ILr32, dl, + N->getValueType(0), + Op1), + 0); + } } Ops[0] = Op0; - Ops[1] = Op1; n_ops = 2; } } diff --git a/lib/Target/X86/AsmParser/X86AsmParser.cpp b/lib/Target/X86/AsmParser/X86AsmParser.cpp index da01350..6b403c1 100644 --- a/lib/Target/X86/AsmParser/X86AsmParser.cpp +++ b/lib/Target/X86/AsmParser/X86AsmParser.cpp @@ -51,11 +51,14 @@ private: void InstructionCleanup(MCInst &Inst); /// @name Auto-generated Match Functions - /// { + /// { bool MatchInstruction(const SmallVectorImpl<MCParsedAsmOperand*> &Operands, MCInst &Inst); + bool MatchInstructionImpl( + const SmallVectorImpl<MCParsedAsmOperand*> &Operands, MCInst &Inst); + /// } public: @@ -132,7 +135,7 @@ struct X86Operand : public MCParsedAsmOperand { X86Operand(KindTy K, SMLoc Start, SMLoc End) : Kind(K), StartLoc(Start), EndLoc(End) {} - + /// getStartLoc - Get the location of the first token of this operand. SMLoc getStartLoc() const { return StartLoc; } /// getEndLoc - Get the location of the last token of this operand. @@ -142,6 +145,11 @@ struct X86Operand : public MCParsedAsmOperand { assert(Kind == Token && "Invalid access!"); return StringRef(Tok.Data, Tok.Length); } + void setTokenValue(StringRef Value) { + assert(Kind == Token && "Invalid access!"); + Tok.Data = Value.data(); + Tok.Length = Value.size(); + } unsigned getReg() const { assert(Kind == Register && "Invalid access!"); @@ -632,6 +640,54 @@ void X86ATTAsmParser::InstructionCleanup(MCInst &Inst) { } } +bool +X86ATTAsmParser::MatchInstruction(const SmallVectorImpl<MCParsedAsmOperand*> + &Operands, + MCInst &Inst) { + // First, try a direct match. + if (!MatchInstructionImpl(Operands, Inst)) + return false; + + // Ignore anything which is obviously not a suffix match. + if (Operands.size() == 0) + return true; + X86Operand *Op = static_cast<X86Operand*>(Operands[0]); + if (!Op->isToken() || Op->getToken().size() > 15) + return true; + + // FIXME: Ideally, we would only attempt suffix matches for things which are + // valid prefixes, and we could just infer the right unambiguous + // type. However, that requires substantially more matcher support than the + // following hack. + + // Change the operand to point to a temporary token. + char Tmp[16]; + StringRef Base = Op->getToken(); + memcpy(Tmp, Base.data(), Base.size()); + Op->setTokenValue(StringRef(Tmp, Base.size() + 1)); + + // Check for the various suffix matches. + Tmp[Base.size()] = 'b'; + bool MatchB = MatchInstructionImpl(Operands, Inst); + Tmp[Base.size()] = 'w'; + bool MatchW = MatchInstructionImpl(Operands, Inst); + Tmp[Base.size()] = 'l'; + bool MatchL = MatchInstructionImpl(Operands, Inst); + + // Restore the old token. + Op->setTokenValue(Base); + + // If exactly one matched, then we treat that as a successful match (and the + // instruction will already have been filled in correctly, since the failing + // matches won't have modified it). + if (MatchB + MatchW + MatchL == 2) + return false; + + // Otherwise, the match failed. + return true; +} + + extern "C" void LLVMInitializeX86AsmLexer(); // Force static initialization. diff --git a/lib/Target/X86/X86.td b/lib/Target/X86/X86.td index ec86fc2..a53f973 100644 --- a/lib/Target/X86/X86.td +++ b/lib/Target/X86/X86.td @@ -179,8 +179,9 @@ include "X86CallingConv.td" // Currently the X86 assembly parser only supports ATT syntax. def ATTAsmParser : AsmParser { - string AsmParserClassName = "ATTAsmParser"; - string AsmParserInstCleanup = "InstructionCleanup"; + string AsmParserClassName = "ATTAsmParser"; + string AsmParserInstCleanup = "InstructionCleanup"; + string MatchInstructionName = "MatchInstructionImpl"; int Variant = 0; // Discard comments in assembly strings. diff --git a/lib/Transforms/Scalar/GVN.cpp b/lib/Transforms/Scalar/GVN.cpp index 321def7..65b34b1 100644 --- a/lib/Transforms/Scalar/GVN.cpp +++ b/lib/Transforms/Scalar/GVN.cpp @@ -1584,7 +1584,7 @@ bool GVN::processNonLocalLoad(LoadInst *LI, for (unsigned i = 0, e = UnavailableBlocks.size(); i != e; ++i) FullyAvailableBlocks[UnavailableBlocks[i]] = false; - bool NeedToSplitEdges = false; + SmallVector<std::pair<TerminatorInst*, unsigned>, 4> NeedToSplit; for (pred_iterator PI = pred_begin(LoadBB), E = pred_end(LoadBB); PI != E; ++PI) { BasicBlock *Pred = *PI; @@ -1600,12 +1600,13 @@ bool GVN::processNonLocalLoad(LoadInst *LI, return false; } unsigned SuccNum = GetSuccessorNumber(Pred, LoadBB); - toSplit.push_back(std::make_pair(Pred->getTerminator(), SuccNum)); - NeedToSplitEdges = true; + NeedToSplit.push_back(std::make_pair(Pred->getTerminator(), SuccNum)); } } - if (NeedToSplitEdges) + if (!NeedToSplit.empty()) { + toSplit.append(NeedToSplit.size(), NeedToSplit.front()); return false; + } // Decide whether PRE is profitable for this load. unsigned NumUnavailablePreds = PredLoads.size(); diff --git a/lib/VMCore/Metadata.cpp b/lib/VMCore/Metadata.cpp index 092fe00..b894ea3 100644 --- a/lib/VMCore/Metadata.cpp +++ b/lib/VMCore/Metadata.cpp @@ -115,14 +115,17 @@ MDNode::~MDNode() { } static const Function *getFunctionForValue(Value *V) { - assert(!isa<MDNode>(V) && "does not iterate over metadata operands"); if (!V) return NULL; - if (Instruction *I = dyn_cast<Instruction>(V)) - return I->getParent()->getParent(); - if (BasicBlock *BB = dyn_cast<BasicBlock>(V)) - return BB->getParent(); + if (Instruction *I = dyn_cast<Instruction>(V)) { + BasicBlock *BB = I->getParent(); + return BB ? BB->getParent() : 0; + } if (Argument *A = dyn_cast<Argument>(V)) return A->getParent(); + if (BasicBlock *BB = dyn_cast<BasicBlock>(V)) + return BB->getParent(); + if (MDNode *MD = dyn_cast<MDNode>(V)) + return MD->getFunction(); return NULL; } @@ -156,17 +159,9 @@ const Function *MDNode::getFunction() const { return assertLocalFunction(this); #endif if (!isFunctionLocal()) return NULL; - - for (unsigned i = 0, e = getNumOperands(); i != e; ++i) { - if (Value *V = getOperand(i)) { - if (MDNode *MD = dyn_cast<MDNode>(V)) { - if (const Function *F = MD->getFunction()) - return F; - } else { - return getFunctionForValue(V); - } - } - } + for (unsigned i = 0, e = getNumOperands(); i != e; ++i) + if (const Function *F = getFunctionForValue(getOperand(i))) + return F; return NULL; } @@ -272,8 +267,19 @@ void MDNode::replaceOperand(MDNodeOperand *Op, Value *To) { // with an instruction or some other function-local object. If this is a // non-function-local MDNode, it can't point to a function-local object. // Handle this case by implicitly dropping the MDNode reference to null. - if (!isFunctionLocal() && To && isFunctionLocalValue(To)) - To = 0; + // Likewise if the MDNode is function-local but for a different function. + if (To && isFunctionLocalValue(To)) { + if (!isFunctionLocal()) + To = 0; + else { + const Function *F = getFunction(); + const Function *FV = getFunctionForValue(To); + // Metadata can be function-local without having an associated function. + // So only consider functions to have changed if non-null. + if (F && FV && F != FV) + To = 0; + } + } if (From == To) return; |