void HeterotbbTransform::copy_function (Function* NF, Function* F) { DenseMap<const Value*, Value *> ValueMap; // Get the names of the parameters for old function for(Function::arg_iterator FI = F->arg_begin(), FE=F->arg_end(), DI=NF->arg_begin(); FE!=FI; ++FI,++DI) { DI->setName(FI->getName()); ValueMap[FI]=DI; } for (Function::const_iterator BI=F->begin(),BE = F->end(); BI != BE; ++BI) { const BasicBlock &FBB = *BI; BasicBlock *NFBB = BasicBlock::Create(FBB.getContext(), "", NF); ValueMap[&FBB] = NFBB; if (FBB.hasName()) { NFBB->setName(FBB.getName()); //DEBUG(dbgs()<<NFBB->getName()<<"\n"); } for (BasicBlock::const_iterator II = FBB.begin(), IE = FBB.end(); II != IE; ++II) { Instruction *NFInst = II->clone(/*F->getContext()*/); if (II->hasName()) NFInst->setName(II->getName()); const Instruction *FInst = &(*II); rewrite_instruction((Instruction *)FInst, NFInst, ValueMap); NFBB->getInstList().push_back(NFInst); ValueMap[II] = NFInst; } } // Remap the instructions again to take care of forward jumps for (Function::iterator BB = NF->begin(), BE=NF->end(); BB != BE; ++ BB) { for (BasicBlock::iterator II = BB->begin(); II != BB->end(); ++II) { int opIdx = 0; //DEBUG(dbgs()<<*II<<"\n"); for (User::op_iterator i = II->op_begin(), e = II->op_end(); i != e; ++i, opIdx++) { Value *V = *i; if (ValueMap[V] != NULL) { II->setOperand(opIdx, ValueMap[V]); } } } } //NF->dump(); }
/// eliminateUnconditionalBranch - Clone the instructions from the destination /// block into the source block, eliminating the specified unconditional branch. /// If the destination block defines values used by successors of the dest /// block, we may need to insert PHI nodes. /// void TailDup::eliminateUnconditionalBranch(BranchInst *Branch) { BasicBlock *SourceBlock = Branch->getParent(); BasicBlock *DestBlock = Branch->getSuccessor(0); assert(SourceBlock != DestBlock && "Our predicate is broken!"); DEBUG(errs() << "TailDuplication[" << SourceBlock->getParent()->getName() << "]: Eliminating branch: " << *Branch); // See if we can avoid duplicating code by moving it up to a dominator of both // blocks. if (BasicBlock *DomBlock = FindObviousSharedDomOf(SourceBlock, DestBlock)) { DEBUG(errs() << "Found shared dominator: " << DomBlock->getName() << "\n"); // If there are non-phi instructions in DestBlock that have no operands // defined in DestBlock, and if the instruction has no side effects, we can // move the instruction to DomBlock instead of duplicating it. BasicBlock::iterator BBI = DestBlock->getFirstNonPHI(); while (!isa<TerminatorInst>(BBI)) { Instruction *I = BBI++; bool CanHoist = I->isSafeToSpeculativelyExecute() && !I->mayReadFromMemory(); if (CanHoist) { for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op) if (Instruction *OpI = dyn_cast<Instruction>(I->getOperand(op))) if (OpI->getParent() == DestBlock || (isa<InvokeInst>(OpI) && OpI->getParent() == DomBlock)) { CanHoist = false; break; } if (CanHoist) { // Remove from DestBlock, move right before the term in DomBlock. DestBlock->getInstList().remove(I); DomBlock->getInstList().insert(DomBlock->getTerminator(), I); DEBUG(errs() << "Hoisted: " << *I); } } } } // Tail duplication can not update SSA properties correctly if the values // defined in the duplicated tail are used outside of the tail itself. For // this reason, we spill all values that are used outside of the tail to the // stack. for (BasicBlock::iterator I = DestBlock->begin(); I != DestBlock->end(); ++I) if (I->isUsedOutsideOfBlock(DestBlock)) { // We found a use outside of the tail. Create a new stack slot to // break this inter-block usage pattern. DemoteRegToStack(*I); } // We are going to have to map operands from the original block B to the new // copy of the block B'. If there are PHI nodes in the DestBlock, these PHI // nodes also define part of this mapping. Loop over these PHI nodes, adding // them to our mapping. // std::map<Value*, Value*> ValueMapping; BasicBlock::iterator BI = DestBlock->begin(); bool HadPHINodes = isa<PHINode>(BI); for (; PHINode *PN = dyn_cast<PHINode>(BI); ++BI) ValueMapping[PN] = PN->getIncomingValueForBlock(SourceBlock); // Clone the non-phi instructions of the dest block into the source block, // keeping track of the mapping... // for (; BI != DestBlock->end(); ++BI) { Instruction *New = BI->clone(); New->setName(BI->getName()); SourceBlock->getInstList().push_back(New); ValueMapping[BI] = New; } // Now that we have built the mapping information and cloned all of the // instructions (giving us a new terminator, among other things), walk the new // instructions, rewriting references of old instructions to use new // instructions. // BI = Branch; ++BI; // Get an iterator to the first new instruction for (; BI != SourceBlock->end(); ++BI) for (unsigned i = 0, e = BI->getNumOperands(); i != e; ++i) { std::map<Value*, Value*>::const_iterator I = ValueMapping.find(BI->getOperand(i)); if (I != ValueMapping.end()) BI->setOperand(i, I->second); } // Next we check to see if any of the successors of DestBlock had PHI nodes. // If so, we need to add entries to the PHI nodes for SourceBlock now. for (succ_iterator SI = succ_begin(DestBlock), SE = succ_end(DestBlock); SI != SE; ++SI) { BasicBlock *Succ = *SI; for (BasicBlock::iterator PNI = Succ->begin(); isa<PHINode>(PNI); ++PNI) { PHINode *PN = cast<PHINode>(PNI); // Ok, we have a PHI node. Figure out what the incoming value was for the // DestBlock. Value *IV = PN->getIncomingValueForBlock(DestBlock); // Remap the value if necessary... std::map<Value*, Value*>::const_iterator I = ValueMapping.find(IV); if (I != ValueMapping.end()) IV = I->second; PN->addIncoming(IV, SourceBlock); } } // Next, remove the old branch instruction, and any PHI node entries that we // had. BI = Branch; ++BI; // Get an iterator to the first new instruction DestBlock->removePredecessor(SourceBlock); // Remove entries in PHI nodes... SourceBlock->getInstList().erase(Branch); // Destroy the uncond branch... // Final step: now that we have finished everything up, walk the cloned // instructions one last time, constant propagating and DCE'ing them, because // they may not be needed anymore. // if (HadPHINodes) { while (BI != SourceBlock->end()) { Instruction *Inst = BI++; if (isInstructionTriviallyDead(Inst)) Inst->eraseFromParent(); else if (Constant *C = ConstantFoldInstruction(Inst)) { Inst->replaceAllUsesWith(C); Inst->eraseFromParent(); } } } ++NumEliminated; // We just killed a branch! }
void HeterotbbTransform::gen_opt_code_per_f (Function* NF, Function* F) { // Get the names of the parameters for old function Function::arg_iterator FI = F->arg_begin(); Argument *classname = &*FI; FI++; Argument *numiters = &*FI; // Set the names of the parameters for new function Function::arg_iterator DestI = NF->arg_begin(); DestI->setName(classname->getName()); Argument *class_name = &(*DestI); //second argument DestI++; DestI->setName(numiters->getName()); Argument *num_iters = &(*DestI); #ifdef EXPLICIT_REWRITE DenseMap<const Value*, Value *> ValueMap; #else ValueToValueMapTy ValueMap; #endif #if EXPLICIT_REWRITE //get the old basic block and create a new one Function::const_iterator BI = F->begin(); const BasicBlock &FB = *BI; BasicBlock *NFBB = BasicBlock::Create(FB.getContext(), "", NF); if (FB.hasName()) { NFBB->setName(FB.getName()); //DEBUG(dbgs()<<FB.getName()<<"\n"); } ValueMap[&FB] = NFBB; ValueMap[numiters] = num_iters; //must create a new instruction which casts i32* back to the class name CastInst *StrucRevCast = CastInst::Create(Instruction::BitCast, class_name, classname->getType(), classname->getName(), NFBB); ValueMap[classname] = StrucRevCast; for (BasicBlock::const_iterator II = FB.begin(), IE = FB.end(); II != IE; ++II) { Instruction *NFInst = II->clone(/*F->getContext()*/); // DEBUG(dbgs()<<*II<<"\n"); if (II->hasName()) NFInst->setName(II->getName()); const Instruction *FInst = &(*II); rewrite_instruction((Instruction *)FInst, NFInst, ValueMap); NFBB->getInstList().push_back(NFInst); ValueMap[II] = NFInst; } BI++; for (Function::const_iterator /*BI=F->begin(),*/BE = F->end(); BI != BE; ++BI) { const BasicBlock &FBB = *BI; BasicBlock *NFBB = BasicBlock::Create(FBB.getContext(), "", NF); ValueMap[&FBB] = NFBB; if (FBB.hasName()) { NFBB->setName(FBB.getName()); //DEBUG(dbgs()<<NFBB->getName()<<"\n"); } for (BasicBlock::const_iterator II = FBB.begin(), IE = FBB.end(); II != IE; ++II) { Instruction *NFInst = II->clone(/*F->getContext()*/); if (II->hasName()) NFInst->setName(II->getName()); const Instruction *FInst = &(*II); rewrite_instruction((Instruction *)FInst, NFInst, ValueMap); NFBB->getInstList().push_back(NFInst); ValueMap[II] = NFInst; } } // Remap the instructions again to take care of forward jumps for (Function::iterator BB = NF->begin(), BE=NF->end(); BB != BE; ++ BB) { for (BasicBlock::iterator II = BB->begin(); II != BB->end(); ++II) { int opIdx = 0; //DEBUG(dbgs()<<*II<<"\n"); for (User::op_iterator i = II->op_begin(), e = II->op_end(); i != e; ++i, opIdx++) { Value *V = *i; if (ValueMap[V] != NULL) { II->setOperand(opIdx, ValueMap[V]); } } } } #else Function::const_iterator BI = F->begin(); const BasicBlock &FB = *BI; BasicBlock *NFBB = BasicBlock::Create(FB.getContext(), "", NF); if (FB.hasName()) { NFBB->setName(FB.getName()); } ValueMap[&FB] = NFBB; CastInst *StrucRevCast = CastInst::Create(Instruction::BitCast, class_name, classname->getType(), classname->getName(), NFBB); ValueMap[classname] = StrucRevCast; ValueMap[numiters] = num_iters; CloneFunctionWithExistingBBInto(NF, NFBB, F, ValueMap, ""); #endif }
bool GenericToNVVM::runOnModule(Module &M) { // Create a clone of each global variable that has the default address space. // The clone is created with the global address space specifier, and the pair // of original global variable and its clone is placed in the GVMap for later // use. for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E;) { GlobalVariable *GV = &*I++; if (GV->getType()->getAddressSpace() == llvm::ADDRESS_SPACE_GENERIC && !llvm::isTexture(*GV) && !llvm::isSurface(*GV) && !llvm::isSampler(*GV) && !GV->getName().startswith("llvm.")) { GlobalVariable *NewGV = new GlobalVariable( M, GV->getValueType(), GV->isConstant(), GV->getLinkage(), GV->hasInitializer() ? GV->getInitializer() : nullptr, "", GV, GV->getThreadLocalMode(), llvm::ADDRESS_SPACE_GLOBAL); NewGV->copyAttributesFrom(GV); GVMap[GV] = NewGV; } } // Return immediately, if every global variable has a specific address space // specifier. if (GVMap.empty()) { return false; } // Walk through the instructions in function defitinions, and replace any use // of original global variables in GVMap with a use of the corresponding // copies in GVMap. If necessary, promote constants to instructions. for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { if (I->isDeclaration()) { continue; } IRBuilder<> Builder(I->getEntryBlock().getFirstNonPHIOrDbg()); for (Function::iterator BBI = I->begin(), BBE = I->end(); BBI != BBE; ++BBI) { for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; ++II) { for (unsigned i = 0, e = II->getNumOperands(); i < e; ++i) { Value *Operand = II->getOperand(i); if (isa<Constant>(Operand)) { II->setOperand( i, remapConstant(&M, &*I, cast<Constant>(Operand), Builder)); } } } } ConstantToValueMap.clear(); } // Copy GVMap over to a standard value map. ValueToValueMapTy VM; for (auto I = GVMap.begin(), E = GVMap.end(); I != E; ++I) VM[I->first] = I->second; // Walk through the metadata section and update the debug information // associated with the global variables in the default address space. for (NamedMDNode &I : M.named_metadata()) { remapNamedMDNode(VM, &I); } // Walk through the global variable initializers, and replace any use of // original global variables in GVMap with a use of the corresponding copies // in GVMap. The copies need to be bitcast to the original global variable // types, as we cannot use cvta in global variable initializers. for (GVMapTy::iterator I = GVMap.begin(), E = GVMap.end(); I != E;) { GlobalVariable *GV = I->first; GlobalVariable *NewGV = I->second; // Remove GV from the map so that it can be RAUWed. Note that // DenseMap::erase() won't invalidate any iterators but this one. auto Next = std::next(I); GVMap.erase(I); I = Next; Constant *BitCastNewGV = ConstantExpr::getPointerCast(NewGV, GV->getType()); // At this point, the remaining uses of GV should be found only in global // variable initializers, as other uses have been already been removed // while walking through the instructions in function definitions. GV->replaceAllUsesWith(BitCastNewGV); std::string Name = GV->getName(); GV->eraseFromParent(); NewGV->setName(Name); } assert(GVMap.empty() && "Expected it to be empty by now"); return true; }
void SuperBlock::fixSideEntrances() { // due to merging of BBs, some superblocks may have 1 BB remaining list<map<BasicBlock*, list<BasicBlock*> >::iterator > delSuperBlocks; for (map<BasicBlock*, list<BasicBlock*> >::iterator sp = superBlocks.begin(), sp_e = superBlocks.end(); sp != sp_e; ++sp) { // we need to keep track of the predecessor of the current basic block // being checked BasicBlock* prev = sp->first; // don't clone basic blocks if the code size threshold is achieved if (currCodeSize/originalCodeSize > CODE_EXPANSION_THRESHOLD) { break; } // the first basic block for a superblock need not be duplicated for (list<BasicBlock*>::iterator bb = sp->second.begin(), bb_e = sp->second.end(); bb != bb_e; ++bb) { // first, collect all predecessors for this BB // (note: we could not just iterate through as the predecessor set may // change list<BasicBlock*> predBBs; for (pred_iterator pred = pred_begin(*bb), pred_e = pred_end(*bb); pred != pred_e; ++pred) { predBBs.push_back(*pred); } // now, walk through all predecessors of this current basic block BasicBlock* clonedBB = NULL; for (list<BasicBlock*>::iterator pred = predBBs.begin(), pred_e = predBBs.end(); pred != pred_e; ++pred) { // if it is not the predecessor of this current basic block present in // the superblock, duplicate! if (*pred != prev) { // there is no need to clone this BB multiple times if (clonedBB == NULL) { ValueToValueMapTy vmap; // clone this basic block, and place the corresponding code after // the last BB of this superblock clonedBB = CloneBasicBlock(*bb, vmap, ".cloned", (*bb)->getParent()); vmap[*bb] = clonedBB; /* errs() << "@@ BEFORE: " << *clonedBB << "\n"; // fix phi nodes in the cloned BB for (BasicBlock::iterator I = clonedBB->begin(); isa<PHINode>(I); ++I) { PHINode* PN = dyn_cast<PHINode>(I); int bbIdx = PN->getBasicBlockIndex(prev); if (bbIdx != -1) { PN->removeIncomingValue(bbIdx, false); } } */ // add size of duplicated BBs to total code size count currCodeSize += clonedBB->size(); // modify operands in this basic block for (BasicBlock::iterator instr = clonedBB->begin(), instr_e = clonedBB->end(); instr != instr_e; ++instr) { for (unsigned idx = 0, num_ops = instr->getNumOperands(); idx < num_ops; ++idx) { Value* op = instr->getOperand(idx); ValueToValueMapTy::iterator op_it = vmap.find(op); if (op_it != vmap.end()) { instr->setOperand(idx, op_it->second); } } } } // remove phi nodes into this BB in the trace /* for (BasicBlock::iterator I = (*bb)->begin(); isa<PHINode>(I); ++I) { PHINode* PN = dyn_cast<PHINode>(I); int bbIdx = PN->getBasicBlockIndex(*pred); if (bbIdx != -1) { PN->removeIncomingValue(bbIdx, false); } } */ // modify the branch instruction of the predecessor not in the superblock to // branch to the cloned basic block Instruction* br_instr = (*pred)->getTerminator(); br_instr->replaceUsesOfWith((Value*)*bb, (Value*)clonedBB); } } // determine if we can merge the BB (definitely can be merged), and its clone // with theirpredecessors if (clonedBB != NULL) { if (MergeBlockIntoPredecessor(*bb, this)) { // since we have merged this BB, delete from our superblock mappings partOfSuperBlock.erase(*bb); bb = sp->second.erase(bb); --bb; if (sp->second.empty()) { delSuperBlocks.push_back(sp); } } MergeBlockIntoPredecessor(clonedBB, this); } prev = *bb; } } // erase some superblocks (which only have 1 BB remaining) for (list<map<BasicBlock*, list<BasicBlock*> >::iterator >::iterator del = delSuperBlocks.begin(), del_e = delSuperBlocks.end(); del != del_e; ++del) { superBlocks.erase(*del); } }
/** * Generate code for */ void HeteroOMPTransform::gen_code_per_f (Function* NF, Function* F, Instruction *max_threads){ Function::arg_iterator FI = F->arg_begin(); Argument *ctxname = &*FI; Function::arg_iterator DestI = NF->arg_begin(); DestI->setName(ctxname->getName()); Argument *ctx_name = &(*DestI); DestI++; DestI->setName("tid"); Argument *num_iters = &(*DestI); #ifdef EXPLICIT_REWRITE DenseMap<const Value*, Value *> ValueMap; #else ValueToValueMapTy ValueMap; #endif //get the old basic block and create a new one Function::const_iterator BI = F->begin(); const BasicBlock &FB = *BI; BasicBlock *NFBB = BasicBlock::Create(FB.getContext(), "", NF); if (FB.hasName()){ NFBB->setName(FB.getName()); } ValueMap[&FB] = NFBB; //ValueMap[numiters] = num_iters; ValueMap[ctxname] = ctx_name; #if EXPLICIT_REWRITE for (BasicBlock::const_iterator II = FB.begin(), IE = FB.end(); II != IE; ++II) { Instruction *NFInst = II->clone(/*F->getContext()*/); // DEBUG(dbgs()<<*II<<"\n"); if (II->hasName()) NFInst->setName(II->getName()); const Instruction *FInst = &(*II); rewrite_instruction((Instruction *)FInst, NFInst, ValueMap); NFBB->getInstList().push_back(NFInst); ValueMap[II] = NFInst; } BI++; for (Function::const_iterator /*BI=F->begin(),*/BE = F->end();BI != BE; ++BI) { const BasicBlock &FBB = *BI; BasicBlock *NFBB = BasicBlock::Create(FBB.getContext(), "", NF); ValueMap[&FBB] = NFBB; if (FBB.hasName()){ NFBB->setName(FBB.getName()); //DEBUG(dbgs()<<NFBB->getName()<<"\n"); } for (BasicBlock::const_iterator II = FBB.begin(), IE = FBB.end(); II != IE; ++II) { Instruction *NFInst = II->clone(/*F->getContext()*/); if (II->hasName()) NFInst->setName(II->getName()); const Instruction *FInst = &(*II); rewrite_instruction((Instruction *)FInst, NFInst, ValueMap); NFBB->getInstList().push_back(NFInst); ValueMap[II] = NFInst; } } // Remap the instructions again to take care of forward jumps for (Function::iterator BB = NF->begin(), BE=NF->end(); BB != BE; ++ BB) { for (BasicBlock::iterator II = BB->begin(); II != BB->end(); ++II){ int opIdx = 0; //DEBUG(dbgs()<<*II<<"\n"); for (User::op_iterator i = II->op_begin(), e = II->op_end(); i != e; ++i, opIdx++) { Value *V = *i; if (ValueMap[V] != NULL) { II->setOperand(opIdx, ValueMap[V]); } } } } #else SmallVector<ReturnInst*, 8> Returns; // Ignore returns cloned. CloneFunctionInto(NF, F, ValueMap, false, Returns, ""); #endif //max_threads->dump(); /* Remap openmp omp_num_threads() and omp_thread_num() */ /* * define internal void @_Z20initialize_variablesiPfS_.omp_fn.4(i8* nocapture %.omp_data_i) nounwind ssp { * entry: * %0 = bitcast i8* %.omp_data_i to i32* ; <i32*> [#uses=1] * %1 = load i32* %0, align 8 ; <i32> [#uses=2] * %2 = tail call i32 @omp_get_num_threads() nounwind readnone ; <i32> [#uses=2] * %3 = tail call i32 @omp_get_thread_num() nounwind readnone ; <i32> [#uses=2] %4 = sdiv i32 %1, %2 %5 = mul nsw i32 %4, %2 %6 = icmp ne i32 %5, %1 %7 = zext i1 %6 to i32 */ vector<Instruction *> toDelete; for (Function::iterator BB = NF->begin(), BE=NF->end(); BB != BE; ++ BB) { for (BasicBlock::iterator II = BB->begin(); II != BB->end(); ++II){ if (isa<CallInst>(II)) { CallSite CI(cast<Instruction>(II)); if (CI.getCalledFunction() != NULL){ string called_func_name = CI.getCalledFunction()->getName(); if (called_func_name == OMP_GET_NUM_THREADS_NAME && CI.arg_size() == 0) { II->replaceAllUsesWith(ValueMap[max_threads]); toDelete.push_back(II); } else if (called_func_name == OMP_GET_THREAD_NUM_NAME && CI.arg_size() == 0) { II->replaceAllUsesWith(num_iters); toDelete.push_back(II); } } } } } /* Delete the last branch instruction of the first basic block -- Assuming it is safe */ Function::iterator nfBB = NF->begin(); TerminatorInst *lastI = nfBB->getTerminator(); BranchInst *bI; BasicBlock *returnBlock; if ((bI = dyn_cast<BranchInst>(lastI)) && bI->isConditional() && (returnBlock = bI->getSuccessor(1)) && (returnBlock->getName() == "return")) { /* modify to a unconditional branch to next basic block and not return */ Instruction *bbI = BranchInst::Create(bI->getSuccessor(0),lastI); bbI->dump(); toDelete.push_back(lastI); } //NF->dump(); while(!toDelete.empty()) { Instruction *g = toDelete.back(); //g->replaceAllUsesWith(UndefValue::get(g->getType())); toDelete.pop_back(); g->eraseFromParent(); } //NF->dump(); }
bool ReplaceNopCastsAndByteSwaps::processBasicBlock(BasicBlock& BB) { bool Changed = false; /** * First pass: replace nopCasts with bitcasts and bswap intrinsics with logic operations */ for ( BasicBlock::iterator it = BB.begin(); it != BB.end(); ) { Instruction * Inst = it++; if (isNopCast(Inst) ) { assert( isa<CallInst>(Inst) ); CallInst * call = cast<CallInst>(Inst); if ( TypeSupport::isClientType( call->getType()) ) { llvm::errs() << "Cast of client type: " << *call << "\n"; continue; } if ( TypeSupport::isClientType( call->getArgOperand(0)->getType()) ) { llvm::errs() << "Cast of client type: " << *call->getArgOperand(0) << "\n"; continue; } ReplaceInstWithInst( call, BitCastInst::Create( Instruction::CastOps::BitCast, call->getArgOperand(0), call->getType() ) ); Changed = true; } else if( IntrinsicInst* II = dyn_cast<IntrinsicInst>(Inst) ) { if(II->getIntrinsicID() == Intrinsic::bswap) { IL->LowerIntrinsicCall(II); Changed = true; } else if(II->getIntrinsicID() == Intrinsic::cheerp_deallocate) { II->eraseFromParent(); Changed = true; } } } /** * Second pass: collapse bitcasts of bitcasts. * * Note: this might leave some dead instruction around, but we don't care since bitcasts are inlined anyway */ for ( BasicBlock::iterator it = BB.begin(); it != BB.end(); ++it ) { if ( isa<BitCastInst>(it) ) { while ( BitCastInst * src = dyn_cast<BitCastInst>(it->getOperand(0) ) ) { it->setOperand(0, src->getOperand(0) ); Changed = true; } } } return Changed; }