void LoopUnroller::unroll(otawa::CFG *cfg, BasicBlock *header, VirtualCFG *vcfg) { VectorQueue<BasicBlock*> workList; VectorQueue<BasicBlock*> loopList; VectorQueue<BasicBlock*> virtualCallList; genstruct::Vector<BasicBlock*> doneList; typedef genstruct::Vector<Pair<VirtualBasicBlock*, Edge::kind_t> > BackEdgePairVector; BackEdgePairVector backEdges; bool dont_unroll = false; BasicBlock *unrolled_from; int start; /* Avoid unrolling loops with LOOP_COUNT of 0, since it would create a LOOP_COUNT of -1 for the non-unrolled part of the loop*/ /* if (header && (ipet::LOOP_COUNT(header) == 0)) { dont_unroll = true; } */ //if (header) dont_unroll = true; start = dont_unroll ? 1 : 0; for (int i = start; ((i < 2) && header) || (i < 1); i++) { doneList.clear(); ASSERT(workList.isEmpty()); ASSERT(loopList.isEmpty()); ASSERT(doneList.isEmpty()); workList.put(header ? header : cfg->entry()); doneList.add(header ? header : cfg->entry()); genstruct::Vector<BasicBlock*> bbs; while (!workList.isEmpty()) { BasicBlock *current = workList.get(); if (LOOP_HEADER(current) && (current != header)) { /* we enter another loop */ loopList.put(current); /* add exit edges destinations to the worklist */ for (genstruct::Vector<Edge*>::Iterator exitedge(**EXIT_LIST(current)); exitedge; exitedge++) { if (!doneList.contains(exitedge->target())) { workList.put(exitedge->target()); doneList.add(exitedge->target()); } } } else { VirtualBasicBlock *new_bb = 0; if ((!current->isEntry()) && (!current->isExit())) { /* Duplicate the current basic block */ new_bb = new VirtualBasicBlock(current); new_bb->removeAllProp(&ENCLOSING_LOOP_HEADER); new_bb->removeAllProp(&EXIT_LIST); new_bb->removeAllProp(&REVERSE_DOM); new_bb->removeAllProp(&LOOP_EXIT_EDGE); new_bb->removeAllProp(&LOOP_HEADER); new_bb->removeAllProp(&ENTRY); /* Remember the call block so we can correct its destination when we have processed it */ if (VIRTUAL_RETURN_BLOCK(new_bb)) virtualCallList.put(new_bb); if ((current == header) && (!dont_unroll)) { if (i == 0) { unrolled_from = new_bb; } else { UNROLLED_FROM(new_bb) = unrolled_from; } } /* if (ipet::LOOP_COUNT(new_bb) != -1) { if (i == 0) { new_bb->removeAllProp(&ipet::LOOP_COUNT); } else { int old_count = ipet::LOOP_COUNT(new_bb); new_bb->removeAllProp(&ipet::LOOP_COUNT); ipet::LOOP_COUNT(new_bb) = old_count - (1 - start); ASSERT(ipet::LOOP_COUNT(new_bb) >= 0); } } */ INDEX(new_bb) = idx; idx++; vcfg->addBB(new_bb); bbs.add(current); map.put(current, new_bb); } /* add successors which are in loop (including possible sub-loop headers) */ for (BasicBlock::OutIterator outedge(current); outedge; outedge++) { if (outedge->target() == cfg->exit()) continue; if (outedge->kind() == Edge::CALL) continue; if (ENCLOSING_LOOP_HEADER(outedge->target()) == header) { // cout << "Test for add: " << outedge->target()->number() << "\n"; if (!doneList.contains(outedge->target())) { workList.put(outedge->target()); doneList.add(outedge->target()); } } if (LOOP_EXIT_EDGE(outedge)) { ASSERT(new_bb); /* Connect exit edge */ VirtualBasicBlock *vdst = map.get(outedge->target()); new Edge(new_bb, vdst, outedge->kind()); } } } } while (!virtualCallList.isEmpty()) { BasicBlock *vcall = virtualCallList.get(); BasicBlock *vreturn = map.get(VIRTUAL_RETURN_BLOCK(vcall), 0); ASSERT(vreturn != 0); VIRTUAL_RETURN_BLOCK(vcall) = vreturn; } while (!loopList.isEmpty()) { BasicBlock *loop = loopList.get(); unroll(cfg, loop, vcfg); } /* Connect the internal edges for the current loop */ for (genstruct::Vector<BasicBlock*>::Iterator bb(bbs); bb; bb++) { for (BasicBlock::OutIterator outedge(bb); outedge; outedge++) { if (LOOP_EXIT_EDGE(outedge)) continue; if (LOOP_HEADER(outedge->target()) && (outedge->target() != header)) continue; if (outedge->target() == cfg->exit()) continue; VirtualBasicBlock *vsrc = map.get(*bb, 0); VirtualBasicBlock *vdst = map.get(outedge->target(), 0); if (outedge->kind() == Edge::CALL) { CFG *called_cfg = outedge->calledCFG(); int called_idx = INDEX(called_cfg); CFG *called_vcfg = coll->get(called_idx); Edge *vedge = new Edge(vsrc, called_vcfg->entry(), Edge::CALL); CALLED_BY(called_vcfg).add(vedge); ENTRY(called_vcfg->entry()) = called_vcfg; CALLED_CFG(outedge) = called_vcfg; /* XXX: ??!? */ } else if ((outedge->target() != header) || ((i == 1) /* XXX && !dont_unroll XXX*/ )) { new Edge(vsrc, vdst, outedge->kind()); } else { backEdges.add(pair(vsrc, outedge->kind())); } } } if (i == start) { /* Connect virtual entry edges */ if (header) { for (BasicBlock::InIterator inedge(header); inedge; inedge++) { if (Dominance::dominates(header, inedge->source())) continue; /* skip back edges */ if (inedge->source() == cfg->entry()) continue; VirtualBasicBlock *vsrc = map.get(inedge->source()); VirtualBasicBlock *vdst = map.get(header); new Edge(vsrc, vdst, inedge->kind()); } } } else { /* Connect virtual backedges from the first to the other iterations */ for (BackEdgePairVector::Iterator iter(backEdges); iter; iter++) { VirtualBasicBlock *vdst = map.get(header); new Edge((*iter).fst, vdst, (*iter).snd); } } } if (!header) { /* add main entry edges */ for (BasicBlock::OutIterator outedge(cfg->entry()); outedge; outedge++) { VirtualBasicBlock *vdst = map.get(outedge->target()); new Edge(vcfg->entry(), vdst, Edge::VIRTUAL_CALL); } /* add main exit edges */ for (BasicBlock::InIterator inedge(cfg->exit()); inedge; inedge++) { VirtualBasicBlock *vsrc = map.get(inedge->source()); new Edge(vsrc, vcfg->exit(), Edge::VIRTUAL_RETURN); } } }
/** * Scan the CFG for finding exit and builds virtual edges with entry and exit. * For memory-place and time purposes, this method is only called when the CFG * is used (call to an accessors method). */ void CFG::scan(void) { // Prepare data typedef HashTable<BasicBlock *, BasicBlock *> map_t; map_t map; VectorQueue<BasicBlock *> todo; todo.put(ent); // Find all BB _bbs.add(&_entry); while(todo) { BasicBlock *bb = todo.get(); ASSERT(bb); // second case : calling jump to a function if(map.exists(bb) || (bb != ent && ENTRY(bb))) continue; // build the virtual BB BasicBlock *vbb = new VirtualBasicBlock(bb); _bbs.add(vbb); map.put(bb, vbb); ASSERTP(map.exists(bb), "not for " << bb->address()); // resolve targets for(BasicBlock::OutIterator edge(bb); edge; edge++) { ASSERT(edge->target()); if(edge->kind() != Edge::CALL) todo.put(edge->target()); } } // Relink the BB BasicBlock *vent = map.get(ent, 0); ASSERT(vent); new Edge(&_entry, vent, Edge::VIRTUAL); for(bbs_t::Iterator vbb(_bbs); vbb; vbb++) { if(vbb->isEnd()) continue; BasicBlock *bb = ((VirtualBasicBlock *)*vbb)->bb(); if(bb->isReturn()) new Edge(vbb, &_exit, Edge::VIRTUAL); for(BasicBlock::OutIterator edge(bb); edge; edge++) { // A call if(edge->kind() == Edge::CALL) { Edge *vedge = new Edge(vbb, edge->target(), Edge::CALL); vedge->toCall(); } // Pending edge else if(!edge->target()) { new Edge(vbb, 0, edge->kind()); } // Possibly a not explicit call else { ASSERT(edge->target()); BasicBlock *vtarget = map.get(edge->target(), 0); if(vtarget) new Edge(vbb, vtarget, edge->kind()); else { // calling jump to a function Edge *nedge = new Edge(vbb, edge->target(), Edge::CALL); vbb->flags |= BasicBlock::FLAG_Call; new Edge(vbb, &_exit, Edge::VIRTUAL); } } } } _bbs.add(&_exit); // Number the BB for(int i = 0; i < _bbs.length(); i++) { INDEX(_bbs[i]) = i; _bbs[i]->_cfg = this; } flags |= FLAG_Scanned; }