lookupTarget* sicScopeLookupTarget::get_target_for_slot(slotDesc* s, simpleLookup* L) { Unused(L); if (s->name == VMString[LEXICAL_PARENT]) { return new sicScopeLookupTarget(scope->parent()); } else if (s->name == VMString[SELF]) { SExpr* t = scope->receiverExpr(); if (t->isConstantSExpr()) { return (new objectLookupTarget(t->constant())) -> be_receiver(); } else if (t->hasMap()) { return ( new mapLookupTarget(t->map())) -> be_receiver(); } else { // don't know the receiver type return NULL; } } else if (! s->is_map_slot()) { return NULL; } else { return new objectLookupTarget(s->data); } }
SExpr* SPrimScope::inlineIntArithmetic() { ArithOpCode op = opcode_for_selector(_selector); bool intRcvr = receiver->hasMap() && receiver->map() == Memory->smi_map; SExpr* arg = args->nth(0); bool intArg = arg->hasMap() && arg->map() == Memory->smi_map; if ( intArg && arg->isConstantSExpr() && intRcvr && arg->constant() == as_smiOop(0) && can_fold_rcvr_op_zero_to_zero(op)) { if (PrintInlining) lprintf("%*s*constant-folding %s: 0\n", (void*)(depth-1), "", ArithOpName[op]); return receiver; } if (PrintInlining) lprintf("%*s*inlining %s:\n", (void*)(depth-1), "", ArithOpName[op]); if (!TArithRRNode::isOpInlinable(op)) return NULL; NodeGen* n = theNodeGen; Node* arith = n->append(new TArithRRNode(op, receiver->preg(), arg->preg(), resultPR, intRcvr, intArg)); // success case - no overflow, int tags MergeNode* ok = (MergeNode*)n->append(new MergeNode("inlineIntArithmetic ok")); SExpr* succExpr = new MapSExpr(Memory->smi_map->enclosing_mapOop(), resultPR, ok); // merge of success & failure branches MergeNode* done = (MergeNode*)ok->append(new MergeNode("inlineIntArithmetic done")); // failure case n->current = arith->append1(new NopNode); if (theSIC->useUncommonTraps && sender()->rscope->isUncommonAt(sender()->bci(), true)) { n->uncommonBranch(currentExprStack(0), true); n->current = done; if (PrintInlining) { lprintf("%*s*making arithmetic failure uncommon\n", (void*)(depth-1), ""); } return succExpr; } else { fint b = bci(); PReg* error = new SAPReg(_sender, b, b); if (intRcvr && intArg) { // must be overflow n->loadOop(VMString[OVERFLOWERROR], error); } else { arith->hasSideEffects_now = true; // may fail, so can't eliminate if (intRcvr || TARGET_ARCH == I386_ARCH) { // arg & TagMask already done by TArithRRNode // I386 does 'em all } else { PReg* t = new TempPReg(this, Temp1, false, true); n->append(new ArithRCNode(AndCCArithOp, t, Tag_Mask, t)); n->current->hasSideEffects_now = true; } // Note: this code assumes that condcode EQ means overflow Node* branch = n->append(new BranchNode(EQBranchOp)); // no overflow, must be type error n->loadOop(VMString[BADTYPEERROR], error); MergeNode* cont = (MergeNode*)n->append( new MergeNode("inlineIntArithmetic cont")); // overflow error PReg* err = new_ConstPReg(_sender, VMString[OVERFLOWERROR]); n->current = branch->append1(new AssignNode(err, error)); n->branch(cont); } Node* dummy; SExpr* failExpr = genPrimFailure(NULL, error, dummy, done, resultPR); assert(done, "merge should always exist"); return succExpr->mergeWith(failExpr, done); } }
const char* NodeGen::splitCondBranch( MergeNode* targetNode, bool isBackwards, PReg* targetPR, SExpr* testExpr, BranchBCTargetStack* targetStack, SExprStack* exprStack, PRegBList* exprStackPRs, SplitSig* s ) { // try to split a conditional branch bc to avoid materializing // the boolean // local splitting only for now assert(targetPR->isConstPReg(), "cond branch must be testing for constant"); oop targetOop = ((ConstPReg*)targetPR)->constant; if (!testExpr->isMergeSExpr()) return "testExpr not MergeSExpr"; if (!((MergeSExpr*)testExpr)->isSplittable()) return "textExpr not splittable"; SExprBList* exprs = ((MergeSExpr*)testExpr)->exprs; assert(testExpr->node(), "splittable implies node()"); Node* preceedingMerge = testExpr->node(); if (current != preceedingMerge) return "not local"; // local only for now if ( preceedingMerge->nPredecessors() != exprs->length() ) return "would have to iterate over predecessors"; fint i; for ( i = 0; i < exprs->length(); ++i) { SExpr* e = exprs->nth(i); Node* n = e->node(); if ( !preceedingMerge->isPredecessor(n) ) return "merge not immediately after expr node"; if ( !e->isConstantSExpr() ) return "merge contains non-constant expression"; } MergeNode* mergeForBranching = new MergeNode("for branching"); MergeNode* mergeForFallingThrough = new MergeNode("for falling through"); mergeForBranching ->setScope(currentScope()); mergeForFallingThrough->setScope(currentScope()); for ( i = 0; i < exprs->length(); ++i) { SExpr* e = exprs->nth(i); Node* n = e->node(); MergeNode* mn = e->constant() == targetOop ? mergeForBranching : mergeForFallingThrough; mn->setPrev(n); n->moveNext(preceedingMerge, mn); } while (preceedingMerge->nPredecessors()) preceedingMerge->removePrev(preceedingMerge->prev(0)); current = mergeForBranching; branchCode( targetNode, isBackwards, NULL, NULL, targetStack, exprStack, exprStackPRs, s); append(mergeForFallingThrough); return NULL; }
SExpr* SCodeScope::inlineMerge(SendInfo* info, MergeNode*& merge) { // inline the send by type-casing; return uninlined cases in others list // If merge has no predecessors, return NULL for merge ref. SExpr* res = NULL; assert(info->rcvr->isMergeSExpr(), "must be a merge"); MergeSExpr* r = (MergeSExpr*)info->rcvr; stringOop sel = info->sel; merge = NULL; if (r->isSplittable() && shouldSplit(info)) { return splitMerge(info, merge); } fint ncases = r->exprs->length(); if (ncases > SICTypeCaseLimit) { info->needRealSend = true; if (PrintInlining) { lprintf("%*s*not type-casing %s (%ld > SICTypeCaseLimit)\n", (void*)depth, "", selector_string(sel), (void*)ncases); } return res; } assert( merge == NULL, "I assume merge is out param only"); merge = new MergeNode("inlineMerge merge"); if (SICDebug) { char* s = NEW_RESOURCE_ARRAY(char, 200); sprintf(s, "begin type-case of %s (ends at node N%ld)", sel->copy_null_terminated(), long(merge->id())); theNodeGen->comment(s); } if (PrintInlining) { lprintf("%*s*type-casing %s\n", (void*)depth, "", selector_string(sel)); } // build list of cases to inline // (add only immediate maps at first, collect others in ...2 lists SSelfScopeBList* slist = new SSelfScopeBList(ncases); SSelfScopeBList* slist2 = new SSelfScopeBList(ncases); SExprBList* elist = new SExprBList(ncases); SExprBList* elist2 = new SExprBList(ncases); SExprBList* others = new SExprBList(ncases); OopBList* mlist = new OopBList(ncases); OopBList* mlist2 = new OopBList(ncases); bool needMapLoad = false; fint i; for (i = 0; i < ncases; i++) { SExpr* nth = r->exprs->nth(i); assert(!nth->isConstantSExpr() || nth->next == NULL || nth->constant() == nth->next->constant(), "shouldn't happen: merged consts - convert to map"); SSelfScope* s; if (!nth->hasMap() || (s = tryLookup(info, nth)) == NULL) { // cannot inline others->append(nth); info->needRealSend = true; continue; } // can inline this case // Notice that for immediates, instead of putting the constants in the mlist, // we put the maps. No point in optimizing just for 17. -- dmu 6/05 Map* map = nth->map(); if (map == Memory->smi_map || map == Memory->float_map) { slist ->append(s); // immediate maps go first // Bug fix: instead of nth->shallowCopy, must generalize to any // with same map, not just the same constant, because other ints (for example) // will pass the type test, too. -- dmu 6/05 elist ->append(new MapSExpr(map->enclosing_mapOop(), r->preg(), NULL)); mlist ->append(map->enclosing_mapOop()); continue; } // can inline but not immediate map slist2->append(s); // append later elist2->append(nth->shallowCopy(r->preg(), NULL)); // use preg of merge if (nth->isConstantSExpr()) { mlist2->append(nth->constant()); } else { needMapLoad = true; // will need to load map of testee mlist2->append(map->enclosing_mapOop()); } } mlist->appendList(mlist2); elist->appendList(elist2); slist->appendList(slist2); // now do the type test and inline the individual cases if (slist->length() > 0) { memoizeBlocks(sel); Node* typeCase = theNodeGen->append(new TypeTestNode(r->preg(), mlist, needMapLoad, info->needRealSend)); Node* fallThrough = typeCase->append(new NopNode); for (i = 0; i < slist->length(); i++) { theNodeGen->current = typeCase->append(i + 1, new NopNode); SExpr* e = doInline(slist->nth(i), elist->nth(i), theNodeGen->current, merge); if (!e->isNoResultSExpr()) { theNodeGen->append(new NopNode); e = e->shallowCopy(info->resReg, theNodeGen->current); res = res ? res->mergeWith(e, merge) : e; } theNodeGen->branch(merge); } theNodeGen->current = fallThrough; } if (res && res->isMergeSExpr()) res->setNode(merge, info->resReg); assert( info->needRealSend && others->length() || !info->needRealSend && !others->length(), "inconsistent"); // NB: *must* use uncommon branch if marked unlikely because // future type tests won't test for unknown if (others->isEmpty()) { // typecase cannot fail theNodeGen->deadEnd(); } else if ( others->length() == 1 && others->first()->isUnknownSExpr() && ((UnknownSExpr*)others->first())->isUnlikely()) { // generate an uncommon branch for the unknown case, not a send theNodeGen->uncommonBranch(currentExprStack(0), info->restartPrim); info->needRealSend = false; if (PrintInlining) lprintf("%*s*making %s uncommon (2)\n", (void*)depth,"",selector_string(sel)); } return res; }
SExpr* SCodeScope::picPredict(SendInfo* info) { // check PICs for information if (!UsePICRecompilation) return info->rcvr; bool canBeUnlikely = theSIC->useUncommonTraps; if (rscope->hasSubScopes(_bci)) { RScopeBList* l = rscope->subScopes(_bci); if (l->first()->isUntakenScope() && l->length() == 1) { return picPredictUnlikely(info, (RUntakenScope*)l->first()); } else if (info->rcvr->containsUnknown()) { if (PrintInlining) { lprintf("%*s*PIC-type-predicting %s (%ld maps)\n", (void*)depth, "", info->sel->copy_null_terminated(), (void*)l->length()); } for (fint i = 0; i < l->length(); i++) { RScope* r = l->nth(i); SExpr* expr = r->receiverExpr(); if (expr->isUnknownSExpr()) { // untaken real send (from PIC) } else if (expr->map()->is_block()) { // for now, doesn't make sense to predict block maps because of // map cloning if (PrintInlining) { lprintf("%*s*not predicting block map\n", (void*)depth, ""); } canBeUnlikely = false; } else { SExpr* alreadyThere = info->rcvr->findMap(expr->myMapOop()); if (alreadyThere) { // generalize to map if only have constant if (alreadyThere->isConstantSExpr()) info->rcvr = info->rcvr->mergeWith(expr, NULL); } else { // add map only if type isn't already present (for splitting) info->predicted = true; info->rcvr = info->rcvr->mergeWith(expr, NULL); if (expr->hasConstant() && l->length() == 1) { // check to see if single predicted receiver is true or false; // if so, add other boolean to prediction. Reduces the number // of uncommon branches; not doing so appears to be overly // aggressive (as observed experimentally) oop c = expr->constant(); if (c == Memory->trueObj && !info->rcvr->findMap(Memory->false_mapOop())) { SExpr* f = new ConstantSExpr(Memory->falseObj, NULL, NULL); info->rcvr = info->rcvr->mergeWith(f, NULL); } else if (c == Memory->falseObj && !info->rcvr->findMap(Memory->true_mapOop())) { SExpr* t = new ConstantSExpr(Memory->trueObj, NULL, NULL); info->rcvr = info->rcvr->mergeWith(t, NULL); } } } } } } else { // know receiver type precisely return info->rcvr; } // mark unknown branch as unlikely UnknownSExpr* u = info->rcvr->findUnknown(); if (u && canBeUnlikely && theSIC->useUncommonTraps && rscope->isUncommonAt(_bci, false)) { info->rcvr = info->rcvr->makeUnknownUnlikely(this); } } else if (theSIC->useUncommonTraps && info->primFailure && rscope->isUncommonAt(_bci, true)) { // this is the failure send of a primitive, and the failure was made // uncommon in the recompilee, and it was never taken, so keep it // uncommon if (PrintInlining) { lprintf("%*s*PIC-type-predicting %s as never executed (2)\n", (void*)depth, "", info->sel->copy_null_terminated()); } info->rcvr = new UnknownSExpr(info->rcvr->preg(), NULL, true); } assert(info->rcvr->preg(), "should have a preg"); return info->rcvr; }