Expr* PrimInliner::generate_cond(BranchOpCode cond, NodeBuilder* gen, PReg* resPReg) { // generate code loading true or false depending on current condition code // Note: condition is negated in order to provoke a certain code order // when compiling whileTrue loops - gri 6/26/96 // n2: conditional branch BranchNode* n2 = NodeFactory::new_BranchNode(not(cond)); gen->append(n2); // tAsgn: true branch ConstPReg* tPReg = new_ConstPReg(gen->scope(), trueObj); AssignNode* tAsgn = NodeFactory::new_AssignNode(tPReg, resPReg); ConstantExpr* tExpr = new ConstantExpr(trueObj, resPReg, tAsgn); n2->append(0, tAsgn); // fAsgn: false branch ConstPReg* fPReg = new_ConstPReg(gen->scope(), falseObj); AssignNode* fAsgn = NodeFactory::new_AssignNode(fPReg, resPReg); ConstantExpr* fExpr = new ConstantExpr(falseObj, resPReg, fAsgn); n2->append(1, fAsgn); // ftm: false & true branch merger MergeNode* ftm = NodeFactory::new_MergeNode(fAsgn, tAsgn); Expr* result = new MergeExpr(fExpr, tExpr, resPReg, ftm); gen->setCurrent(ftm); return result; }
SExpr* SPrimScope::tryTypeCheck() { // for inlined prims, try to see if primitive will always fail if (!InlinePrimitives) return NULL; bool fail = false; switch (pd->type()) { case NotReallyAPrimitive: case InternalPrimitive: fatal("cannot call an internal primitive from Self code"); return NULL; case IntComparisonPrimitive: case IntArithmeticPrimitive: // must have two smis fail = CHECK_INT(receiver) || CHECK_INT(args->last()); break; case FloatArithmeticPrimitive: case FloatComparisonPrimitive: // must have two floats fail = CHECK_FLOAT(receiver) || CHECK_FLOAT(args->last()); break; case AtPrimitive: case AtPutPrimitive: // must have array rcvr and smi arg fail = TYPECHECK(receiver, is_objVector()) || CHECK_INT(args->last()); break; case SizePrimitive: // must have array rcvr fail = TYPECHECK(receiver, is_objVector()); break; case ByteAtPutPrimitive: // stored value must be 0..255; for now, test only for integer fail = CHECK_INT(args->nth(0)); // fall through case ByteAtPrimitive: // must have array rcvr and smi arg fail |= TYPECHECK(receiver, is_byteVector()) || CHECK_INT(args->last()); break; case ByteSizePrimitive: // must have array rcvr fail = TYPECHECK(receiver, is_byteVector()); break; default: return NULL; } if (fail) { // primitive will always fail ConstPReg* error = new_ConstPReg(_sender, VMString[BADTYPEERROR]); Node* dummy; MergeNode* mdummy = NULL; return genPrimFailure(NULL, error, dummy, mdummy, resultPR, false); } else { return NULL; } }
SExpr* SPrimScope::tryConstantFold() { const fint MaxPrimArgs = 5; // fix switch stmt below when increasing this if (!pd->canBeConstantFolded() || nargs > MaxPrimArgs) return NULL; oop a[MaxPrimArgs]; // argument oops if (!receiver->isConstantSExpr()) return NULL; oop r = receiver->constant(); // get arguments (NB: args is in reverse order) fint i; for (i = 0; i < nargs; i++) { if (!args->nth(i)->isConstantSExpr()) return NULL; a[nargs - i - 1] = args->nth(i)->constant(); }; // ok, all args are consts: call the primitive oop res; fntype f = pd->fn(); switch(nargs) { case 0: res = f(r); break; case 1: res = f(r, a[0]); break; case 2: res = f(r, a[0], a[1]); break; case 3: res = f(r, a[0], a[1], a[2]); break; case 4: res = f(r, a[0], a[1], a[2], a[3]); break; case 5: res = f(r, a[0], a[1], a[2], a[3], a[4]); break; default: ShouldNotReachHere(); } if (PrintInlining) { char buf[1024]; char* b = buf; mysprintf(b, "%*s*constant-folding %s %s ", (void*)depth, "", r->debug_print(), pd->name()); for (i = 0; i < nargs; i++) { mysprintf(b, "%s ", a[i]->debug_print()); } mysprintf(b, "--> %s\n", res->debug_print()); lprintf(buf); } // should scan backwards here and discard all nodes computing the args if (res->is_mark()) { // primitive will always fail ConstPReg* error = new_ConstPReg(_sender, res->memify()); Node* dummy; MergeNode* mdummy = NULL; SExpr* failExpr = genPrimFailure(NULL, error, dummy, mdummy, resultPR, false); return failExpr; } else { theNodeGen->loadOop(res, resultPR); return new ConstantSExpr(res, resultPR, theNodeGen->current); } }
Expr* PrimInliner::smi_Mod(Expr* x, Expr* y) { if (y->preg()->isConstPReg()) { assert(y->is_smi(), "type check should have failed"); int d = smiOop(((ConstPReg*)y->preg())->constant)->value(); if (is_power_of_2(d)) { // replace it with mask ConstPReg* preg = new_ConstPReg(_scope, as_smiOop(d-1)); return smi_BitOp(tAndArithOp, x, new ConstantExpr(preg->constant, preg, _gen->_current)); } } // otherwise leave it alone return NULL; }
Expr* PrimInliner::tryConstantFold() { // Returns the result if the primitive call has been constant folded // successfully; returns NULL otherwise. // Note: The result may be a marked oop - which has to be unmarked // before using it - and which indicates that the primitive will fail // always. if (!_pdesc->can_be_constant_folded()) { // check for Symbol>>at: before declaring failure if ((equal(_pdesc->name(), "primitiveIndexedByteAt:ifFail:") || equal(_pdesc->name(), "primitiveIndexedByteCharacterAt:ifFail:")) && parameter(0)->hasKlass() && parameter(0)->klass() == Universe::symbolKlassObj()) { // the at: primitive can be constant-folded for symbols // what if the receiver is a constant string? unfortunately, Smalltalk has // "assignable constants" like Fortran... } else { return NULL; } } // get parameters int i = number_of_parameters(); oop* args = NEW_RESOURCE_ARRAY(oop, i); while (i > 0) { i--; Expr* arg = parameter(i); if (!arg->isConstantExpr()) return NULL; args[i] = arg->constant(); } // all parameters are constants, so call primitive oop res = _pdesc->eval(args); if (res->is_mark()) { // primitive failed return primitiveFailure(unmarkSymbol(res)); } else if (res->is_mem() && !res->is_old()) { // must tenure result because nmethods aren't scavenged if (res->is_double()) { res = oopFactory::clone_double_to_oldspace(doubleOop(res)); } else { // don't know how to tenure non-doubles warning("const folding: primitive %s is returning non-tenured object", _pdesc->name()); return NULL; } } ConstPReg* constResult = new_ConstPReg(_scope, res); SAPReg* result = new SAPReg(_scope); _gen->append(NodeFactory::new_AssignNode(constResult, result)); if (CompilerDebug) cout(PrintInlining)->print("%*sconstant-folding %s --> %#x\n", _scope->depth + 2, "", _pdesc->name(), res); assert(!constResult->constant->is_mark(), "result must not be marked"); return new ConstantExpr(res, constResult, _gen->current()); }
Expr* PrimInliner::smi_Comparison(BranchOpCode cond, Expr* arg1, Expr* arg2) { assert_failure_block(); assert_receiver(); // parameters & result registers bool intArg1 = arg1->is_smi(); bool intArg2 = arg2->is_smi(); bool intBoth = intArg1 && intArg2; // if true, tag error cannot occur SAPReg* resPReg = new SAPReg(_scope); // holds the result if primitive didn't fail SAPReg* errPReg = new SAPReg(_scope); // holds the error message if primitive failed // n1: comparison & treatment of tag error Node* n1; ConstPReg* n1PReg; AssignNode* n1Err; ConstantExpr* n1Expr; if (intBoth) { // tag error cannot occur n1 = NodeFactory::new_ArithRRNode(new NoPReg(_scope), arg1->preg(), tCmpArithOp, arg2->preg()); } else { // tag error can occur n1 = NodeFactory::new_TArithRRNode(new NoPReg(_scope), arg1->preg(), tCmpArithOp, arg2->preg(), intArg1, intArg2); n1PReg = new_ConstPReg(_scope, vmSymbols::first_argument_has_wrong_type()); n1Err = NodeFactory::new_AssignNode(n1PReg, errPReg); n1Expr = new ConstantExpr(n1PReg->constant, errPReg, n1Err); n1->append(1, n1Err); } _gen->append(n1); Expr* result = generate_cond(cond, _gen, resPReg); // continuation if (!intBoth) { // failure can occur if (shouldUseUncommonTrap()) { // simply do an uncommon trap n1Err->append(NodeFactory::new_UncommonNode(_gen->copyCurrentExprStack(), _bci /*_failure_block->begin_bci()*/)); } else { // treat failure case result = merge_failure_block(result->node(), result, n1Err, n1Expr, false); } } return result; }
Expr* PrimInliner::obj_class(bool has_receiver) { assert_no_failure_block(); if (has_receiver) assert_receiver(); Expr* obj = parameter(0); if (obj->hasKlass()) { // constant-fold it klassOop k = obj->klass(); return new ConstantExpr(k, new_ConstPReg(_scope, k), NULL); } else { SAPReg* resPReg = new SAPReg(_scope); InlinedPrimitiveNode* n = NodeFactory::new_InlinedPrimitiveNode( InlinedPrimitiveNode::obj_klass, resPReg, NULL, obj->preg() ); _gen->append(n); // don't know exactly what it is - just use PIC info return new UnknownExpr(resPReg, n); } }
Expr* PrimInliner::primitiveFailure(symbolOop failureCode) { // compile a failing primitive if (CompilerDebug) cout(PrintInlining)->print("%*sprimitive %s will fail: %s\n", _scope->depth + 2, "", _pdesc->name(), failureCode->as_string()); if (_failure_block == NULL) { error("\nPotential compiler bug: compiler believes primitive %s will fail\nwith error %s, but primitive has no failure block.\n", _pdesc->name(), failureCode->as_string()); return NULL; } // The byte code compiler stores the primitive result in a temporary (= the parameter of // the inlined failure block). Thus, we store res in the corresponding temp Expr so that // its value isn't forgotten (the default Expr for temps is UnknownExpr) and restore the // temp's original value afterwards. This is safe because within the failure block that // temporary is treated as a parameter and therefore is read-only. AssignmentFinder closure(_failure_block); assert(closure.interval != NULL, "no assignment found"); Expr* old_temp = _scope->temporary(closure.tempNo); // save temp Expr* res = new ConstantExpr(failureCode, new_ConstPReg(_scope, failureCode), _gen->current()); _scope->set_temporary(closure.tempNo, res); closure.set_prim_failure(false); // allow inlining sends in failure branch _gen->generate_subinterval(closure.interval, true); _scope->set_temporary(closure.tempNo, old_temp); return _gen->exprStack()->pop(); // restore temp }
void InlinedScope::createTemporaries(int nofTemps) { // add nofTemps temporaries (may be called multiple times) int firstNew; if (!hasTemporaries()) { // first time we're called _temporaries = new GrowableArray<Expr*>(nofTemps, nofTemps, NULL); // The canonical model has the context in the first temporary. // To preserve this model the first temporary is aliased to _context. // Lars, 3/8/96 if (_context) { _temporaries->at_put(0, new ContextExpr(_context)); firstNew = 1; } else { firstNew = 0; } } else { // grow existing temp array const GrowableArray<Expr*>* oldTemps = _temporaries; const int n = nofTemps + oldTemps->length(); _temporaries = new GrowableArray<Expr*>(n, n, NULL); firstNew = oldTemps->length(); nofTemps += oldTemps->length(); for (int i = 0; i < firstNew; i++) _temporaries->at_put(i, oldTemps->at(i)); } // initialize new temps ConstPReg* nil = new_ConstPReg(this, nilObj); for (int i = firstNew; i < nofTemps; i++) { PReg* r = new PReg(this); _temporaries->at_put(i, new UnknownExpr(r, NULL)); if (isTop()) { // temps are initialized by PrologueNode } else { gen()->append(NodeFactory::new_AssignNode(nil, r)); } } }
SExpr* SPrimScope::genPrimFailure(PrimNode* call, PReg* errorReg, Node*& test, MergeNode*& merge, PReg* resultReg, bool failure) { // generate primitive failure code // two modes: // if call == NULL, omit the test for failure because it's already // been generated (inlined prim.); in this case, errorReg // must be set // if call != NULL, generate test code (setting test & merge node args) // returns the result of the failure branch // pop prim args (they're not on the expr stack anymore in the fail branch) while (npop-- > 0) exprStack()->pop(); SCodeScope* s = sender(); NodeGen* ng = theNodeGen; if (call) { fint b = bci(); SAPReg* t = new SAPReg(s, b, b); // extract tag field and test for mark tag ng->append(new ArithRCNode(AndArithOp, call->dest(), Tag_Mask, t)); ng->append(new ArithRCNode(SubCCArithOp, t, Tag_Mask, ng->noPR)); test = ng->append(new BranchNode(NEBranchOp)); // failure branch; load error string if (!errorReg) errorReg = new SAPReg(s, b, b); ng->current = test->append(new ArithRCNode(SubArithOp, call->dest(), Mark_Tag-Mem_Tag, errorReg)); } SExpr* failReceiver = hasFailBlock ? failBlock : receiver; SendInfo* info = new SendInfo(failReceiver, NormalLookupType, false, false, (stringOop)failSelector, NULL); info->computeNSends(rscope, bci()); info->primFailure = failure; info->restartPrim = call == NULL; // restart inlined prims (unc. traps) s->exprStack->push(failReceiver); if (errorReg->isConstPReg()) { s->exprStack->push(new ConstantSExpr(((ConstPReg*)errorReg)->constant, errorReg, ng->current)); } else { s->exprStack->push(new MapSExpr(Memory->stringObj->map()->enclosing_mapOop(), errorReg, ng->current)); } ConstPReg* failSelReg = new_ConstPReg(s, selector()); s->exprStack->push(new ConstantSExpr(selector(), failSelReg, NULL)); SExpr* res = s->inlineSend(info); if (res->isNoResultSExpr()) { // never returns ng->current = merge; // set to NULL if no merge } else { if (needZap) { assert(failBlock->preg()->isBlockPReg(), "should be a block"); ng->zapBlock((BlockPReg*)failBlock->preg()); } ng->move(res->preg(), resultReg); res = res->shallowCopy(resultReg, ng->current); // moved creation down from before if res->isNoResult... // to avoid creating unreachable merge -- dmu if (merge == NULL) merge = new MergeNode("genPrimFailure merge"); ng->append(merge); } 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); } }
ConstPReg* SPrimScope::falsePR() { return new_ConstPReg(_sender, Memory->falseObj); }
ConstPReg* SPrimScope::truePR() { return new_ConstPReg(_sender, Memory->trueObj); }
void NodeGen::loadOop(oop p, PReg* dest) { APPEND(new AssignNode(new_ConstPReg(currentScope(), p), dest)); }
Expr* PrimInliner::smi_ArithmeticOp(ArithOpCode op, Expr* arg1, Expr* arg2) { assert_failure_block(); assert_receiver(); // parameters & result registers bool intArg1 = arg1->is_smi(); bool intArg2 = arg2->is_smi(); bool intBoth = intArg1 && intArg2; // if true, tag error cannot occur SAPReg* resPReg = new SAPReg(_scope); // holds the result if primitive didn't fail SAPReg* errPReg = new SAPReg(_scope); // holds the error message if primitive failed MergeNode* failureStart = NodeFactory::new_MergeNode(_failure_block->begin_bci()); // n1: operation & treatment of tag error Node* n1; AssignNode* n1Err; ConstantExpr* n1Expr = NULL; if (intBoth) { // tag error cannot occur n1 = NodeFactory::new_ArithRRNode(resPReg, arg1->preg(), op, arg2->preg()); } else { // tag error can occur n1 = NodeFactory::new_TArithRRNode(resPReg, arg1->preg(), op, arg2->preg(), intArg1, intArg2); if (shouldUseUncommonTrap()) { // simply jump to uncommon branch code n1->append(1, failureStart); } else { ConstPReg* n1PReg = new_ConstPReg(_scope, vmSymbols::first_argument_has_wrong_type()); n1Err = NodeFactory::new_AssignNode(n1PReg, errPReg); n1Expr = new ConstantExpr(n1PReg->constant, errPReg, n1Err); n1->append(1, n1Err); n1Err->append(failureStart); } } _gen->append(n1); Expr* result = new KlassExpr(smiKlassObj, resPReg, n1); // n2: overflow check & treatment of overflow const bool taken_is_uncommon = true; BranchNode* n2 = NodeFactory::new_BranchNode(VSBranchOp, taken_is_uncommon); AssignNode* n2Err; ConstantExpr* n2Expr = NULL; if (shouldUseUncommonTrap()) { // simply jump to uncommon branch code n2->append(1, failureStart); } else { ConstPReg* n2PReg = new_ConstPReg(_scope, vmSymbols::smi_overflow()); n2Err = NodeFactory::new_AssignNode(n2PReg, errPReg); n2Expr = new ConstantExpr(n2PReg->constant, errPReg, n2Err); n2->append(1, n2Err); n2Err->append(failureStart); } _gen->append(n2); // continuation if (shouldUseUncommonTrap()) { // uncommon branch instead of failure code failureStart->append(NodeFactory::new_UncommonNode(_gen->copyCurrentExprStack(), _bci /*_failure_block->begin_bci()*/)); } else { assert(n2Expr != NULL, "no error message defined"); Expr* error; if (n1Expr != NULL) { error = new MergeExpr(n1Expr, n2Expr, errPReg, failureStart); } else { error = n2Expr; } result = merge_failure_block(n2, result, failureStart, error, false); } return result; }