oop primitive_desc::eval(oop* a) { const bool reverseArgs = true; // change this when changing primitive calling convention oop res; if (reverseArgs) { switch (number_of_parameters()) { case 0: res = ((prim_fntype0)_fn)(); break; case 1: res = ((prim_fntype1)_fn)(a[0]); break; case 2: res = ((prim_fntype2)_fn)(a[1], a[0]); break; case 3: res = ((prim_fntype3)_fn)(a[2], a[1], a[0]); break; case 4: res = ((prim_fntype4)_fn)(a[3], a[2], a[1], a[0]); break; case 5: res = ((prim_fntype5)_fn)(a[4], a[3], a[2], a[1], a[0]); break; case 6: res = ((prim_fntype6)_fn)(a[5], a[4], a[3], a[2], a[1], a[0]); break; case 7: res = ((prim_fntype7)_fn)(a[6], a[5], a[4], a[3], a[2], a[1], a[0]); break; case 8: res = ((prim_fntype8)_fn)(a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0]); break; case 9: res = ((prim_fntype9)_fn)(a[8], a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0]); break; default: ShouldNotReachHere(); } } else { switch (number_of_parameters()) { case 0: res = ((prim_fntype0)_fn)(); break; case 1: res = ((prim_fntype1)_fn)(a[0]); break; case 2: res = ((prim_fntype2)_fn)(a[0], a[1]); break; case 3: res = ((prim_fntype3)_fn)(a[0], a[1], a[2]); break; case 4: res = ((prim_fntype4)_fn)(a[0], a[1], a[2], a[3]); break; case 5: res = ((prim_fntype5)_fn)(a[0], a[1], a[2], a[3], a[4]); break; case 6: res = ((prim_fntype6)_fn)(a[0], a[1], a[2], a[3], a[4], a[5]); break; case 7: res = ((prim_fntype7)_fn)(a[0], a[1], a[2], a[3], a[4], a[5], a[6]); break; case 8: res = ((prim_fntype8)_fn)(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break; case 9: res = ((prim_fntype9)_fn)(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); break; default: ShouldNotReachHere(); } } return res; }
void primitive_desc::print() { std->print("%48s %d %s%s%s%s%s%s%s%s%s", name(), number_of_parameters(), has_receiver() ? "R" : "_", can_fail() ? "F" : "_", can_scavenge() ? "S" : "_", can_walk_stack() ? "W" : "_", can_perform_NLR() ? "N" : "_", can_be_constant_folded() ? "C" : "_", can_invoke_delta() ? "D" : "_", is_internal() ? "I" : "_", needs_delta_fp_code() ? "P" : "_"); switch(group()) { case IntComparisonPrimitive: std->print(", smi compare"); break; case IntArithmeticPrimitive: std->print(", smi arith"); break; case FloatComparisonPrimitive: std->print(", double compare"); break; case FloatArithmeticPrimitive: std->print(", double arith"); break; case ByteArrayPrimitive: std->print(", byte array op."); break; case DoubleByteArrayPrimitive: std->print(", double-byte array op."); break; case ObjArrayPrimitive: std->print(", array op."); break; case BlockPrimitive: std->print(", block/context"); break; case NormalPrimitive: break; default: fatal("Unknown primitive group"); } std->cr(); }
PrimInliner::PrimInliner(NodeBuilder* gen, primitive_desc* pdesc, MethodInterval* failure_block) { assert(pdesc, "must have a primitive desc to inline"); _gen = gen; _pdesc = pdesc; _failure_block= failure_block; _scope = gen->_scope; _bci = gen->bci(); // bci of primitive call _exprStack = gen->_exprStack; _cannotFail = true; _params = new GrowableArray<Expr*>(number_of_parameters(), number_of_parameters(), NULL); _usingUncommonTrap = false; // get parameters int i = number_of_parameters(); int first = _exprStack->length() - i; while (i-- > 0) { _params->at_put(i, _exprStack->at(first + i)); } // %hack: assertion fails // assert(_pdesc->can_fail() == (failure_block != NULL), "primitive desc & primitive usage inconsistent"); }
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()); }
template<typename Iterator> void factor_vm::iterate_relocations(code_block *compiled, Iterator &iter) { if(compiled->relocation != F) { byte_array *relocation = untag<byte_array>(compiled->relocation); cell index = 0; cell length = array_capacity(relocation) / sizeof(relocation_entry); for(cell i = 0; i < length; i++) { relocation_entry rel = relocation->data<relocation_entry>()[i]; iter(rel,index,compiled); index += number_of_parameters(relocation_type_of(rel)); } } }
symbolOop PrimInliner::failureSymbolForArg(int i) { assert(i >= 0 && i < number_of_parameters(), "bad index"); switch (i) { case 0: return vmSymbols::receiver_has_wrong_type(); case 1: return vmSymbols::first_argument_has_wrong_type(); case 2: return vmSymbols::second_argument_has_wrong_type(); case 3: return vmSymbols::third_argument_has_wrong_type(); case 4: return vmSymbols::fourth_argument_has_wrong_type(); case 5: return vmSymbols::fifth_argument_has_wrong_type(); case 6: return vmSymbols::sixth_argument_has_wrong_type(); case 7: return vmSymbols::seventh_argument_has_wrong_type(); case 8: return vmSymbols::eighth_argument_has_wrong_type(); case 9: return vmSymbols::ninth_argument_has_wrong_type(); case 10: return vmSymbols::tenth_argument_has_wrong_type(); default: return vmSymbols::argument_has_wrong_type(); } }
Expr* PrimInliner::tryTypeCheck() { // Check if we have enough type information to prove that the primitive is going to fail; // if so, directly compile failure block (important for mixed-mode arithmetic). // Returns the failure result if the primitive call has been proven // to fail; returns NULL otherwise. // Should extend code to do general type compatibility test (including MergeExprs, e.g. for // booleans) -- fix this later. -Urs 11/95 int num = number_of_parameters(); for (int i = 0; i < num; i++) { Expr* a = parameter(i); if (a->hasKlass()) { Expr* primArgType = _pdesc->parameter_klass(i, a->preg(), NULL); if (primArgType && primArgType->hasKlass() && (a->klass() != primArgType->klass())) { // types differ -> primitive must fail return primitiveFailure(failureSymbolForArg(i)); } } } return NULL; }
char* primitive_desc::parameter_type(int index) const { assert((0 <= index) && (index < number_of_parameters()), "illegal parameter index"); return _types[1 + index]; }
Expr* PrimInliner::tryInline() { // Returns the failure result or the result of the primitive (if the // primitive can't fail) if the primitive has been inlined; returns // NULL otherwise. If the primitive has been inlined but can't fail, // the bci in the MethodDecoder is set to the first instruction after // the failure block. // NB: The comparisons below should be replaced with pointer comparisons // comparing with the appropriate vmSymbol. Should fix this at some point. char* name = _pdesc->name(); Expr* res = NULL; switch (_pdesc->group()) { case IntArithmeticPrimitive: if (number_of_parameters() == 2) { Expr* x = parameter(0); Expr* y = parameter(1); if (equal(name, "primitiveAdd:ifFail:")) { res = smi_ArithmeticOp(tAddArithOp, x, y); break; } if (equal(name, "primitiveSubtract:ifFail:")) { res = smi_ArithmeticOp(tSubArithOp, x, y); break; } if (equal(name, "primitiveMultiply:ifFail:")) { res = smi_ArithmeticOp(tMulArithOp, x, y); break; } if (equal(name, "primitiveDiv:ifFail:")) { res = smi_Div(x, y); break; } if (equal(name, "primitiveMod:ifFail:")) { res = smi_Mod(x, y); break; } if (equal(name, "primitiveBitAnd:ifFail:")) { res = smi_BitOp(tAndArithOp, x, y); break; } if (equal(name, "primitiveBitOr:ifFail:")) { res = smi_BitOp(tOrArithOp , x, y); break; } if (equal(name, "primitiveBitXor:ifFail:")) { res = smi_BitOp(tXOrArithOp, x, y); break; } if (equal(name, "primitiveRawBitShift:ifFail:")) { res = smi_Shift(x, y); break; } } break; case IntComparisonPrimitive: if (number_of_parameters() == 2) { Expr* x = parameter(0); Expr* y = parameter(1); if (equal(name, "primitiveSmallIntegerEqual:ifFail:")) { res = smi_Comparison(EQBranchOp, x, y); break; } if (equal(name, "primitiveSmallIntegerNotEqual:ifFail:")) { res = smi_Comparison(NEBranchOp, x, y); break; } if (equal(name, "primitiveLessThan:ifFail:")) { res = smi_Comparison(LTBranchOp, x, y); break; } if (equal(name, "primitiveLessThanOrEqual:ifFail:")) { res = smi_Comparison(LEBranchOp, x, y); break; } if (equal(name, "primitiveGreaterThan:ifFail:")) { res = smi_Comparison(GTBranchOp, x, y); break; } if (equal(name, "primitiveGreaterThanOrEqual:ifFail:")) { res = smi_Comparison(GEBranchOp, x, y); break; } } break; case FloatArithmeticPrimitive: break; case FloatComparisonPrimitive: break; case ObjArrayPrimitive: if (equal(name, "primitiveIndexedObjectSize")) { res = array_size(); break; } if (equal(name, "primitiveIndexedObjectAt:ifFail:")) { res = array_at_ifFail(ArrayAtNode::object_at); break; } if (equal(name, "primitiveIndexedObjectAt:put:ifFail:")) { res = array_at_put_ifFail(ArrayAtPutNode::object_at_put); break; } break; case ByteArrayPrimitive: if (equal(name, "primitiveIndexedByteSize")) { res = array_size(); break; } if (equal(name, "primitiveIndexedByteAt:ifFail:")) { res = array_at_ifFail(ArrayAtNode::byte_at); break; } if (equal(name, "primitiveIndexedByteAt:put:ifFail:")) { res = array_at_put_ifFail(ArrayAtPutNode::byte_at_put); break; } break; case DoubleByteArrayPrimitive: if (equal(name, "primitiveIndexedDoubleByteSize")) { res = array_size(); break; } if (equal(name, "primitiveIndexedDoubleByteAt:ifFail:")) { res = array_at_ifFail(ArrayAtNode::double_byte_at); break; } if (equal(name, "primitiveIndexedDoubleByteCharacterAt:ifFail:")) { res = array_at_ifFail(ArrayAtNode::character_at); break; } if (equal(name, "primitiveIndexedDoubleByteAt:put:ifFail:")) { res = array_at_put_ifFail(ArrayAtPutNode::double_byte_at_put);break; } break; case BlockPrimitive: if (strncmp(name, "primitiveValue", 14) == 0) { res = block_primitiveValue(); break; } break; case NormalPrimitive: if (strncmp(name, "primitiveNew", 12) == 0) { res = obj_new(); break; } if (equal(name, "primitiveShallowCopyIfFail:ifFail:")) { res = obj_shallowCopy(); break; } if (equal(name, "primitiveEqual:")) { res = obj_equal(); break; } if (equal(name, "primitiveClass")) { res = obj_class(true); break; } if (equal(name, "primitiveClassOf:")) { res = obj_class(false); break; } if (equal(name, "primitiveHash")) { res = obj_hash(true); break; } if (equal(name, "primitiveHashOf:")) { res = obj_hash(false); break; } if (equal(name, "primitiveProxyByteAt:ifFail:")) { res = proxy_byte_at(); break; } if (equal(name, "primitiveProxyByteAt:put:ifFail:")) { res = proxy_byte_at_put(); break; } break; default: fatal1("bad primitive group %d", _pdesc->group()); break; } if (CompilerDebug) { cout(PrintInlining && (res != NULL))->print("%*sinlining %s %s\n", _scope->depth + 2, "", _pdesc->name(), _usingUncommonTrap ? "(unc. failure)" : (_cannotFail ? "(cannot fail)" : "")); } if (!_usingUncommonTrap && !_cannotFail) theCompiler->reporter->report_prim_failure(_pdesc); return res; }