SExpr* SPrimScope::inlineAtPut(bool objVector) { assert(_selector == VMString[objVector ? _AT_PUT_ : _BYTE_AT_PUT_], "bad selector"); bool okRcvr = receiver->hasMap(); Map* rm; if (okRcvr) { rm = receiver->map(); if (objVector) { okRcvr = rm->is_objVector(); } else { okRcvr = rm->is_byteVector(); } } if (!okRcvr) { // receiver type not known statically return NULL; } if (PrintInlining) lprintf("%*s*inlining _%sAtPut:\n", (void*)(depth-1), "", objVector ? "" :"Byte"); SExpr* arg = args->nth(1); NodeGen* ng = theNodeGen; if (SICDebug) ng->comment("inlined _At:Put:/_ByteAt:Put:"); fint b = bci(); bool intArg = arg->hasMap() && arg->map() == Memory->smi_map; bool willFail = arg->hasMap() && arg->map() != Memory->smi_map; bool useUncommonTrap = !willFail && theSIC->useUncommonTraps && sender()->rscope->isUncommonAt(sender()->bci(), true); PReg* errorPR = useUncommonTrap ? NULL : new SAPReg(_sender, b, b); Node* at; if (objVector) { PReg* elementArgPR = args->nth(0)->preg(); // materialize value arg theNodeGen->materializeBlock(elementArgPR, _sender->sig, new PRegBList(1)); fint size = ((slotsMap*)rm)->empty_vector_object_size(); at = new ArrayAtPutNode(receiver->preg(), arg->preg(), intArg, elementArgPR, resultPR, errorPR, size * oopSize - Mem_Tag); } else { SExpr* value = args->nth(0); bool intVal = value->hasMap() && value->map() == Memory->smi_map; willFail |= value->hasMap() && value->map() != Memory->smi_map; at = new ByteArrayAtPutNode(receiver->preg(), arg->preg(), intArg, value->preg(), intVal, resultPR, errorPR); } ng->append(at); // success case - int index, in bounds MergeNode* ok = (MergeNode*)ng->append(new NopNode); // merge of success & failure branches MergeNode* done = (MergeNode*)ok->append(new MergeNode("inlineAtPut done")); // failure case SExpr* res = receiver->shallowCopy(resultPR, ok); ng->current = at->append1(new MergeNode("inlineAtPut current")); if (useUncommonTrap) { if (PrintInlining) { lprintf("%*s*making atPut: failure uncommon\n", (void*)(depth-1), ""); } ng->uncommonBranch(currentExprStack(0), true); ng->current = done; } else { Node* dummy; SExpr* failExpr = genPrimFailure(NULL, errorPR, dummy, done, resultPR); assert(done, "node should always exist"); res = res->mergeWith(failExpr, done); } return res; }
SExpr* SPrimScope::inlineIntComparison() { BranchOpCode cond; if (_selector == VMString[_INT_EQ_]) { cond = EQBranchOp; } else if (_selector == VMString[_INT_LT_]) { cond = LTBranchOp; } else if (_selector == VMString[_INT_LE_]) { cond = LEBranchOp; } else if (_selector == VMString[_INT_GT_]) { cond = GTBranchOp; } else if (_selector == VMString[_INT_GE_]) { cond = GEBranchOp; } else if (_selector == VMString[_INT_NE_]) { cond = NEBranchOp; } else { return NULL; } bool intRcvr = receiver->hasMap() && receiver->map() == Memory->smi_map; SExpr* arg = args->nth(0); bool intArg = arg->hasMap() && arg->map() == Memory->smi_map; if (PrintInlining) lprintf("%*s*inlining int comparison prim\n", (void*)(depth-1), ""); NodeGen* n = theNodeGen; Node* branch = n->append(new TBranchNode(cond, receiver->preg(), intRcvr, arg->preg(), intArg)); // false branch n->move(falsePR(), resultPR); SExpr* falseExpr= new ConstantSExpr(Memory->falseObj, resultPR, n->current); MergeNode* done = (MergeNode*)n->append( new MergeNode("inlineIntComparison done")); // true branch n->current = branch->append1(new AssignNode(truePR(), resultPR)); SExpr* trueExpr = new ConstantSExpr(Memory->trueObj, resultPR, n->current); n->branch(done); SExpr* res = trueExpr->copyMergeWith(falseExpr, resultPR, done); // failure branch if (!intRcvr || !intArg) { branch->hasSideEffects_now = true; if (theSIC->useUncommonTraps && sender()->rscope->isUncommonAt(sender()->bci(), true)) { n->current = branch->append(2, new NopNode); n->uncommonBranch(currentExprStack(0), true); n->current = done; if (PrintInlining) { lprintf("%*s*making int comparison failure uncommon\n", (void*)(depth-1), ""); } } else { fint b = bci(); PReg* error = new SAPReg(_sender, b, b); PReg* err = new_ConstPReg(_sender, VMString[BADTYPEERROR]); n->current = branch->append(2, new AssignNode(err, error)); Node* dummy; SExpr* failExpr = genPrimFailure(NULL, error, dummy, done, resultPR); assert(done, "merge should always exist"); res = (MergeSExpr*)res->mergeWith(failExpr, done); } } return res; }
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); } }
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::typePredict(SendInfo* info) { // try static type prediction PReg* r = info->rcvr->preg(); stringOop sel = info->sel; if (sel == VMString[IF_TRUE_] || sel == VMString[IF_FALSE_] || sel == VMString[IF_TRUE_FALSE_] || sel == VMString[IF_FALSE_TRUE_] || sel == VMString[OR] || sel == VMString[AND] || sel == VMString[NOT]) { // boolean message if (PrintInlining) { lprintf("%*s*type-predicting %s\n", (void*)depth, "", sel->copy_null_terminated()); } info->predicted = true; bool allowUnlikely = theSIC->useUncommonTraps; if (SICDeferUncommonBranches && (sel == VMString[IF_TRUE_] || sel == VMString[IF_FALSE_] || sel == VMString[IF_TRUE_FALSE_] || sel == VMString[IF_FALSE_TRUE_])) { // these bets are really safe - make uncommon even when recompiling // due to uncommon trap (if the ifTrue itself caused the trap, // rscope->isUncommonAt will be false, so this is safe) allowUnlikely = true; } if (allowUnlikely) { if (rscope->isUncommonAt(_bci, false)) { // ok, no uncommon trap here } else if (rscope->hasSubScopes(_bci)) { // has real send for ifTrue et al. -- must be NIC-compiled // make uncommon unlikely if no non-true/false receiver present RScopeBList* subs = rscope->subScopes(_bci); for (fint i = subs->length() - 1; i >= 0; i--) { RScope* s = subs->nth(i); SExpr* rcvr = s->receiverExpr(); if (rcvr->hasMap()) { Map* m = rcvr->map(); if (m != Memory-> true_map() && m != Memory->false_map()) { allowUnlikely = false; break; } } } if (WizardMode && !allowUnlikely) warning("SIC: non-bool receiver for ifTrue: et al. detected"); } if (allowUnlikely) { // make unknown case unlikely despite info->rcvr = info->rcvr->makeUnknownUnlikely(this); } } SExpr* rcvr = info->rcvr; SExpr* t = new ConstantSExpr(Memory->trueObj , r, NULL); SExpr* f = new ConstantSExpr(Memory->falseObj, r, NULL); // make sure we don't destroy splitting info; only add types if not // already present if (rcvr->findMap(Memory-> true_mapOop()) == NULL) rcvr = rcvr->mergeWith(t, NULL); if (rcvr->findMap(Memory->false_mapOop()) == NULL) rcvr = rcvr->mergeWith(f, NULL); return rcvr; } if (// binary integer arithmetic messages sel == VMString[PLUS] || sel == VMString[MINUS] || sel == VMString[PERCENT] || sel == VMString[LESS_THAN] || sel == VMString[LESS_EQUAL] || sel == VMString[GREATER_EQUAL] || sel == VMString[GREATER_THAN] | // integer looping messages sel == VMString[TO_DO_] || sel == VMString[UP_TO_DO_] || sel == VMString[DOWN_TO_DO_] || sel == VMString[TO_BY_DO_] || sel == VMString[UP_TO_BY_DO_] || sel == VMString[DOWN_TO_BY_DO_] || // unary integer arithmetic selectors sel == VMString[SUCCESSOR] || sel == VMString[SUCC] || sel == VMString[PREDECESSOR] || sel == VMString[PRED]) { if (info->rcvr->findMap(Memory->smi_map->enclosing_mapOop())) return info->rcvr; if (PrintInlining) { lprintf("%*s*type-predicting %s\n", (void*)depth, "", sel->copy_null_terminated()); } info->predicted = true; SExpr* res = info->rcvr->mergeWith(new MapSExpr(Memory->smi_map->enclosing_mapOop(), r, NULL), NULL); if (theSIC->useUncommonTraps && rscope->isUncommonAt(_bci, false)) { info->rcvr = res = res->makeUnknownUnlikely(this); } return res; } return info->rcvr; }
SExpr* SCodeScope::inlineSend(SendInfo* info) { stringOop sel = info->sel; SExpr* res = NULL; info->resReg = new SAPReg(this); MergeNode* merge = NULL; fint argc = sel->arg_count(); if (!Inline && !InlineSTMessages) { // don't do any inlining info->needRealSend = true; } else { info->rcvr = picPredict(info); UnknownSExpr* u = info->rcvr->findUnknown(); if (u && !u->isUnlikely()) { info->rcvr = typePredict(info); } if (info->rcvr->really_hasMap(this)) { // single map - try to inline this send SSelfScope* s = tryLookup(info, info->rcvr); if (s) { SExpr* r = doInline(s, info->rcvr, theNodeGen->current, NULL); if (r->isNoResultSExpr()) { res = r; } else { theNodeGen->append(new NopNode()); // to get right scope for r res = r->shallowCopy(r->preg(), theNodeGen->current); } } else { if (PrintInlining) { lprintf("%*s*marking %s send ReceiverStatic\n", (void*)depth,"", selector_string(sel)); } // receiver type is constant (but e.g. method was too big to inline) info->l |= ReceiverStaticBit; info->needRealSend = true; } } else if (info->rcvr->isMergeSExpr()) { res = inlineMerge(info, merge); } else { // unknown receiver // NB: *must* use uncommon branch if marked unlikely because // future type tests won't test for unknown if (info->rcvr->findUnknown()->isUnlikely()) { // generate an uncommon branch for the unknown case, not a send theNodeGen->current = theNodeGen->uncommonBranch(currentExprStack(0), info->restartPrim); info->needRealSend = false; if (PrintInlining) { lprintf("%*s*making %s uncommon\n", (void*)depth,"",selector_string(sel)); } } else { info->needRealSend = true; } } } if (info->needRealSend) { SExpr* r = genRealSend(info); res = res ? res->mergeWith(r, merge) : r; } if (merge && res && !res->isNoResultSExpr()) theNodeGen->branch(merge); // now pop expr stack for (fint i = 0; i < argc; i++) exprStack->pop(); if (!info->isSelfImplicit) exprStack->pop(); if (!res) res = new NoResultSExpr; return res; }