void PerformanceDebugger::report_context(InlinedScope* s) { if (!DebugPerformance) return; Reporter r(this); GrowableArray<Expr*>* temps = s->contextTemporaries(); const int len = temps->length(); int nused = 0; for (int i = 0; i < len; i++) { PReg* r = temps->at(i)->preg(); if (r->uplevelR() || r->uplevelW() || (r->isBlockPReg() && !r->isUnused())) nused++; } if (nused == 0) { str->print(" could not eliminate context of scope %s (fixable compiler restriction; should be eliminated)\n", s->key()->print_string()); } else { str->print(" could not eliminate context of scope %s; temp(s) still used: ", s->key()->print_string()); for (int j = 0; j < len; j++) { PReg* r = temps->at(j)->preg(); if (r->uplevelR() || r->uplevelW()) { str->print("%d ", j); } else if (r->isBlockPReg() && !r->isUnused()) { str->print("%d (non-inlined block)", j); } } str->print("\n"); } }
void SCodeScope::memoizeBlocks(stringOop sel) { // memoize block args so they aren't created for inlined cases fint top = exprStack->length(); fint argc = sel->arg_count(); for (fint i = 1; i <= argc; i++) { PReg* r = exprStack->nth(top - i)->preg(); if (r->isBlockPReg()) ((BlockPReg*)r)->memoize(); } }
void CompiledLoop::findRegCandidates() { // Find the PRegs with the highest number of defs & uses in this loop. // Problem: we don't have a list of all PRegs used in the loop; in fact, we don't even have // a list of all nodes. // Solution: iterate through all BBs between start and end of loop (in code generation order) // and collect the defs and uses. The BB ordering algorithm should make sure that the BBs // of the loop are consecutive. if (_bbs == NULL) _bbs = bbIterator->code_generation_order(); GrowableArray<LoopRegCandidate*> candidates(PReg::currentNo, PReg::currentNo, NULL); const int len = _bbs->length(); const BB* startBB = _startOfLoop->bb(); int i; for (i = 0; _bbs->at(i) != startBB; i++) ; // search for first BB const BB* endBB = _endOfLoop->bb(); int ncalls = 0; // iterate through all BBs in the loop for (BB* bb = _bbs->at(i); bb != endBB; i++, bb = _bbs->at(i)) { const int n = bb->duInfo.info->length(); if (bb->last->isCallNode()) ncalls++; for (int j = 0; j < n; j++) { DUInfo* info = bb->duInfo.info->at(j); PReg* r = info->reg; if (candidates.at(r->id()) == NULL) candidates.at_put(r->id(), new LoopRegCandidate(r)); LoopRegCandidate* c = candidates.at(r->id()); c->incDUs(info->uses.length(), info->defs.length()); } } loopHeader()->set_nofCallsInLoop(ncalls); // find the top 2 candidates... LoopRegCandidate* first = new LoopRegCandidate(NULL); LoopRegCandidate* second = new LoopRegCandidate(NULL); for (int j = candidates.length() - 1; j >= 0; j--) { LoopRegCandidate* c = candidates.at(j); if (c == NULL) continue; if (c->weight() > first->weight()) { second = first; first = c; } else if (c->weight() > second->weight()) { second = c; } } // ...and add them to the loop header if (first->preg() != NULL) { loopHeader()->addRegisterCandidate(first); if (second->preg() != NULL) { loopHeader()->addRegisterCandidate(second); } } else { assert(second->preg() == NULL, "bad sorting"); } }
Expr* PrimInliner::block_primitiveValue() { PReg* r = parameter(0)->preg(); if (r->isBlockPReg()) { // we know the identity of the block -- inline it if possible Inliner* inliner = _gen->inliner(); SendInfo* info = new SendInfo(_scope, parameter(0), _pdesc->selector()); Expr* res = inliner->inlineBlockInvocation(info); if (res) return res; } // could at least inline block invocation -- fix this later return NULL; }
void BB::allocateTempRegisters(BitVector** hardwired, PRegBList* tempRegs, BitVectorBList* lives) { if (!nnodes) return; // empty BB RegisterEqClassBList regClasses(nnodes + 1); regClasses.append(NULL); // first reg class has index 1 fint use_count[NumRegisters], def_count[NumRegisters]; for (fint i = 0; i < NumRegisters; i++) use_count[i] = def_count[i] = 0; allocate_to_preferred_candidates_if_possible(use_count, def_count); // allocate other temp regs (using the untouched temp regs of this BB) fint temp = 0; for (int i = 0; i < duInfo.info->length(); i++) { // collect temp regs PReg* r = duInfo.info->nth(i)->reg; if (r->loc == UnAllocated && !r->isUnused() && r->isLocalTo(this)) { assert(r->dus.first()->index == i, "should be the same"); for ( ; temp < NumTempRegs && use_count[TempRegs[temp]] + def_count[TempRegs[temp]] > 0; temp++) ; if (temp == NumTempRegs) break; // ran out of regs // ok, allocate TempRegs[temp] to the preg and equivalent pregs Location t = TempRegs[temp++]; PReg* frst = r->regClass ? regClasses.nth(r->regClass)->first : r; for (PReg* pr = frst; pr; pr = pr->regClassLink) { doAlloc(pr, t); pr->regClass = 0; } } r->regClass = 0; } if (temp == NumTempRegs) { // ran out of temp regs with the simple strategy - try using slow // allocation algorithm slowAllocateTempRegisters(hardwired, tempRegs, lives); } }
void BB::pick_candidates_for_assignment_node(Node* n, fint use_count[], fint def_count[], RegCandidateBList &cands) { PReg* src = n->src(); PReg* dest = n->dest(); bool localSrc = src ->isLocalTo(this); bool localDest = dest->isLocalTo(this); if ( isRegister(src->loc)) { if (dest->loc == UnAllocated && localDest) { // PR = PR2(reg) // allocate dest->loc to src->loc, but only if src->loc // isn't defined again cands.append(new RegCandidate(dest, src->loc, def_count[src->loc])); } } else if ( isRegister(dest->loc)) { if (src->loc == UnAllocated && localSrc) { // PR2(reg) = PR // should allocate src->loc to dest->loc, but only if dest->loc // has single definition (this one) and isn't used before // this point [simplification] if (def_count[dest->loc] != 1 || use_count[dest->loc]) { // not eligible for special treatment } else { cands.append(new RegCandidate(src, dest->loc, 1)); } } } else if (localSrc && localDest) { // both regs are local and unallocated - put them in same // equivalence class // fix this - should check for overlapping live ranges // needed to say "if nonoverlapping live ranges(src, dest)" // src->makeSameRegClass(dest, ®Classes); // if (WizardMode) warning("basicBlock: happens"); } else { // non-local registers - skip } }
void do_it(InlinedScope* s) { GrowableArray<NonTrivialNode*>* tests = s->typeTests(); int len = tests->length(); for (int i = 0; i < len; i++) { NonTrivialNode* n = tests->at(i); assert(n->doesTypeTests(), "shouldn't be in list"); if (n->deleted) continue; if (n->hasUnknownCode()) continue; // can't optimize - expects other klasses, so would get uncommon trap at run-time if (!theLoop->isInLoop(n)) continue; // not in this loop GrowableArray<PReg*> regs(4); GrowableArray<GrowableArray<klassOop>*> klasses(4); n->collectTypeTests(regs, klasses); for (int j = 0; j < regs.length(); j++) { PReg* r = regs.at(j); if (theLoop->defsInLoop(r) == 0) { // this test can be hoisted if (CompilerDebug || PrintLoopOpts) cout(PrintLoopOpts)->print("*moving type test of %s at N%d out of loop\n", r->name(), n->id()); hoistableTests->append(new HoistedTypeTest(n, r, klasses.at(j))); } } } }
void Compiler::computeBlockInfo() { FlagSetting(EliminateUnneededNodes, true); // unused context nodes must be eliminated GrowableArray<InlinedScope*>* allContexts = new GrowableArray<InlinedScope*>(25); topScope->collectContextInfo(allContexts); // for now, just allocate all contexts as in interpreter // fix this later: collect all uplevel-accessed PRegs at same loop depth, form physical // contexts for these // also, if uplevel-read and single def --> could copy into context and keep // stack/register copy // remove all unused contexts // need to iterate because removing a nested context may enable removal of a parent context // (could avoid iteration with topo sort, but there are few contexts anyway) bool changed = EliminateContexts; while (changed) { changed = false; for (int i = allContexts->length() - 1; i >= 0; i--) { InlinedScope* s = allContexts->at(i); if (s == NULL) continue; PReg* contextPR = s->context(); assert(contextPR->isSinglyAssigned(), "should have exactly one def"); GrowableArray<Expr*>* temps = s->contextTemporaries(); bool noUplevelAccesses = true; // check if all context temps can be stack-allocated for (int j = temps->length() - 1; j >= 0; j--) { PReg* r = temps->at(j)->preg(); if (r->uplevelR() || r->uplevelW() // this temp is still uplevel-accessed, so can't eliminate context || (r->isBlockPReg() && !r->isUnused()) // this block still forces a context ) { noUplevelAccesses = false; break; } } // TO DO: check if context is needed for NLRs // (noUplevelAccesses alone does not allow elimination) if (/*noUplevelAccesses || */contextPR->isSinglyUsed()) { // can eliminate context -- no uplevel-accessed vars // (single use is context initializer) if (CompilerDebug) cout(PrintEliminateContexts)->print("%*s*eliminating context %s\n", s->depth, "", contextPR->safeName()); contextPR->scope()->gen()->removeContextCreation(); allContexts->at_put(i, NULL); // make code generator break if it tries to access this context changed = true; } } } // now collect all remaining contexts int i = allContexts->length(); contextList = new GrowableArray<InlinedScope*>(i, i, NULL); while (i-- > 0) { // should merge several contexts into one physical context if possible // fix this later InlinedScope* s = allContexts->at(i); if (s == NULL) continue; PReg* contextPR = s->context(); if (CompilerDebug) { cout(PrintEliminateContexts)->print("%*s*could not eliminate context %s in scope %s\n", s->depth, "", contextPR->safeName(), s->key()->print_string()); } reporter->report_context(s); contextList->at_put(i, s); ContextCreateNode* c = s->contextInitializer()->creator(); c->set_contextNo(i); GrowableArray<Expr*>* temps = s->contextTemporaries(); // allocate the temps in this context (but only if they're used) int ntemps = temps->length(); int size = 0; for (int j = 0; j < ntemps; j++) { PReg* p = temps->at(j)->preg(); // should be: // if (p->isUsed() && (p->uplevelR() || p->uplevelW())) { // but doesn't work yet (probably must fix set_self_via_context etc.) // -Urs 6/96 if (p->isUsed()) { // allocate p to context temp assert(p->scope() == s || p->isBlockPReg(), "oops"); Location loc = Mapping::contextTemporary(i, size, s->scopeID()); if (p->isBlockPReg()) { // Blocks aren't actually assigned (at the PReg level) so that the inlining info // isn't lost. Thus we need to create a fake destination here if the context exists. SAPReg* dest = new SAPReg(s, loc, true, true, PrologueBCI, EpilogueBCI); Expr* e = new UnknownExpr(dest, NULL); //contextPR->scope()->contextInitializer()->initialize(j, init); temps->at_put(j, e); } else { p->allocateTo(loc); } size++; } } c->set_sizeOfContext(size); if (size < ntemps && c->scope()->number_of_noninlined_blocks() > 0) { // this hasn't been exercised much compiler_warning("while compiling %s: eliminated some context temps", key->print_string()); } } // Compute the number of noninlined blocks for the nmethod and allocate const int nblocks = topScope->number_of_noninlined_blocks(); if (is_method_compile() || nblocks > 0) { // allocate nblocks+1 jumpTable entries const jumpTableID id = Universe::code->jump_table()->allocate(nblocks + 1); if (is_method_compile()) { main_jumpTable_id = id; } else { promoted_jumpTable_id = id; } // first is for nmethod itself int block_index = 1; for (int i = bbIterator->exposedBlks->length() - 1; i >= 0; i--) { BlockPReg* blk = bbIterator->exposedBlks->at(i); if (blk->isUsed()) { assert(block_index <= nblocks, "nblocks too small"); blk->closure()->set_id(id.sub(block_index++)); } } assert(nblocks + 1 == block_index, "just checking"); } }
// allocate PRegs that are used & defined solely within this BB void BB::slowAllocateTempRegisters(BitVector** hardwired, PRegBList* tempRegs, BitVectorBList* lives) { // clear temporary data structures tempRegs->clear(); lives->clear(); fint i; for (i = 0; i < NumTempRegs; i++) { hardwired[i]->setLength(nnodes); hardwired[i]->clear(); } for (i = 0; i < duInfo.info->length(); i++) { // collect temp regs and hardwired temp regs PReg* r = duInfo.info->nth(i)->reg; if (r->isLocalTo(this)) { assert(r->dus.first()->index == i, "should be the same"); if (r->isUnused()) { // unused register - ignore } else { DUInfo* info = duInfo.info->nth(r->dus.first()->index); tempRegs->append(r); BitVector* bv = new BitVector(nnodes); lives->append(bv); fint firstUse = 0, lastUse = nnodes - 1; duInfo.info->nth(i)->getLiveRange(firstUse, lastUse); bv->addFromTo(firstUse, lastUse); } } else if (isTempReg(r->loc)) { fint firstUse = 0, lastUse = nnodes - 1; if (!r->incorrectDU()) { duInfo.info->nth(i)->getLiveRange(firstUse, lastUse); } else { // can't really compute live range since the temp might be non-local // so assume it's live from first node til the end } hardwired[RegToTempNo[r->loc]]->addFromTo(firstUse, lastUse); } } // now, tempRegs holds all temp regs, and lives contains each register's // live range (one bit per node, 1 = reg is live); hardwired contains // the ranges where temp regs are already taken (e.g. for NLR, calls, etc) // cycle through the temp registers to (hopefully) allow more optimizations // later (e.g. scheduling) fint lastTemp = 0; # define nextTemp(n) (n == NumTempRegs - 1) ? 0 : n + 1 for (i = 0; i < tempRegs->length(); i++) { // try to allocate tempRegs[i] to a temp register PReg* r = tempRegs->nth(i); if (r->loc != UnAllocated) { assert(r->regClass == 0, "should have been cleared"); continue; } BitVector* liveRange = lives->nth(i); for (fint tempNo = lastTemp, ntries = 0; ntries < NumTempRegs; tempNo = nextTemp(tempNo), ntries++) { if (liveRange->isDisjointFrom(hardwired[tempNo])) { Location temp = TempRegs[tempNo]; doAlloc(r, temp); hardwired[tempNo]->unionWith(liveRange); lastTemp = nextTemp(tempNo); break; } } if ( r->loc == UnAllocated && (PrintSICTempRegisterAllocation || WizardMode && TARGET_ARCH != I386_ARCH /* happens normally in I386; few regs */ )) { lprintf("*could NOT find temp assignment for local %s in BB%ld\n", r->name(), (void*)id()); } else if (r->loc == UnAllocated) { if (PrintSICTempRegisterAllocation) lprintf("out of temp regs"); } r->regClass = 0; } }
void InlinedScope::generateDebugInfo() { // Generate debug info for the common parts of methods and blocks if (PrintDebugInfoGeneration) { if (isMethodScope()) { std->print_cr("self: %s", _self->preg()->name()); } else { std->print_cr("no receiver (block method)"); } } ScopeDescRecorder* rec = theCompiler->scopeDescRecorder(); int len, i; if (!isLite()) { // temporaries if (hasTemporaries()) { len = _temporaries->length(); for (i = 0; i < len; i++) { PReg* preg = _temporaries->at(i)->preg(); rec->addTemporary(_scopeInfo, i, preg->createLogicalAddress()); if (PrintDebugInfoGeneration) std->print_cr("temp[%2d]: %s", i, preg->name()); } } // float temporaries if (hasFloatTemporaries()) { len = _floatTemporaries->length(); for (i = 0; i < len; i++) { PReg* preg = _floatTemporaries->at(i)->preg(); rec->addTemporary(_scopeInfo, i, preg->createLogicalAddress()); if (PrintDebugInfoGeneration) std->print_cr("float[%2d]: %s", i, preg->name()); } } // context temporaries if (allocatesInterpretedContext()) { len = _contextTemporaries->length(); for (i = 0; i < len; i++) { PReg* preg = _contextTemporaries->at(i)->preg(); rec->addContextTemporary(_scopeInfo, i, preg->createLogicalAddress()); if (PrintDebugInfoGeneration) std->print_cr("c_temp[%2d]: %s", i, preg->name()); } } // expr stack len = _exprStackElems->length(); for (i = 0; i < len; i++) { Expr* elem = _exprStackElems->at(i); if (elem != NULL) { PReg* r = elem->preg()->cpReg(); if (r->scope()->isSenderOrSame(this)) { // Note: Is it still needed to create this info here, since the // PReg locations may change over time and thus produce more // debug info than actually needed for the new backend. Discuss // this with Lars. rec->addExprStack(_scopeInfo, i, r->createLogicalAddress()); if (PrintDebugInfoGeneration) std->print_cr("expr[%2d]: %s", i, r->name()); } else { // r's scope is too low (i.e. it's not actually live anymore) // this can only happen if the expression is discarded; thus it's safe not to describe this entry // Urs 5/96 // fix this: should check that bci (i) is statement end (or r is NoPReg) } } } } // subscopes len = _subScopes->length(); for (i = 0; i < len; i++) { InlinedScope* s = _subScopes->at(i); if (PrintDebugInfoGeneration) std->print_cr("Subscope %d (id = %d):", i, s->scopeID()); s->generateDebugInfo(); } }