#define __SINST__ // g++ header bug #include #include #include "osstream.h" #include "AST.h" #include "ClassDecl.h" #include "CodeContext.h" #include "CtPointInstance.h" #include "CtReference.h" #include "MethodDecl.h" #include "casts.h" #include "code.h" #include "code-call.h" #include "ctBox.h" #include "decls.h" #include "delimit.h" #include "domain-decls.h" #include "code-grid.h" #include "code-util.h" #include "decimalString.h" #include "lgMacro.h" #include "code-assign.h" #include "code-operator.h" #include "inline.h" bool opt_finalize = true; extern bool tallyCalls; static void emitCallTally(CodeContext &context, const string cMethodName, bool canCallStatically, TypeNode *targetType, Common::Modifiers flags) { if (tallyCalls) { bool isSpecial = targetType->isPointType() || targetType->isDomainType() || targetType->isRectDomainType() || targetType->isTitaniumArrayType(); context << "TALLY_CALL(" << cMethodName << ", " << canCallStatically << ", " << isSpecial << ", " << !!(flags & Common::Native) << ");" << endCline; } } // returns non-zero arity N if type is Domain or tiDomainN int isDomainOrtiDomainType(const TypeNode& t) { if (t.isDomainType()) return ((DomainTypeNode*)&t)->tiArity(); // tiDomain call inside domain library string objectTypeName = t.decl()->fullName(); if (objectTypeName.find("ti.domains.tiDomain") == 0 && isdigit(objectTypeName[19])) return atoi(&objectTypeName[19]); if (objectTypeName.find("ti.domains.tiMultiRectADomain") == 0 && isdigit(objectTypeName[29])) return atoi(&objectTypeName[29]); return 0; } static int implementedByDomain(const MethodDecl *md) { if (isDomainOrtiDomainType(*md->container()->asType())) return true; const MethodSet *overriders = md->overriders(); for (MethodSet::const_iterator method = overriders->begin(); method != overriders->end(); method++) { if (isDomainOrtiDomainType(*(*method)->container()->asType())) return true; if (implementedByDomain(*method)) return true; } return false; } static void pushThis( LocalVars &vars, const string &instance, const MethodDecl &method, vector< string > ¶meters, const TypeNode &objectType, bool alwaysCast ) { bool isRef = objectType.isReference(); if (alwaysCast || !(method.modifiers() & Common::Static)) if (alwaysCast || isRef) { const bool thisLocal = ( method.modifiers() & Common::Local ) || ( method.category() == Decl::Constructor ); const CtType &rawType = /* HACK: special case dispatch for pseudo-templates */ (method.category() != Decl::Constructor && isDomainOrtiDomainType(objectType) && implementedByDomain(&method) ) ? MultiRectADomainNDecl[isDomainOrtiDomainType(objectType)-1]->cType() : method.container()->cType(); const CtType &typeName = ctBox( rawType, thisLocal ); if (alwaysCast || typeName != objectType.cType()) { vars.depend( typeName ); parameters.push_back("*(" + typeName + "*) &" + instance); return; } } /* Default: no cast is needed. */ parameters.push_back(instance); } // returns true iff the method call can be dispatched statically // methodDecl is filled in with the method target bool ObjectFieldAccessNode::canCallStatically(MethodDecl *&methodDecl) const { assert( decl()->category() == Decl::Method || decl()->category() == Decl::Constructor); const TypeDecl &receiver = *accessedObjectType()->decl(); bool result = false; if (receiver.category() == Decl::Class) { // fundamentally cannot be overridden if (decl()->modifiers() & (Final | Private | Static)) result = true; // constructor calls can always be statically bound if (decl()->category() == Decl::Constructor) result = true; // dispatched to a final class if (receiver.modifiers() & Final) result = true; // not observed to be overridden anywhere if (codeGen_main && opt_finalize && decl()->overriders()->empty() && !(decl()->modifiers() & Abstract)) result = true; if (result) { methodDecl = static_cast< MethodDecl * >( decl() ); return true; } } if (codeGen_main && opt_finalize) { // check if it's an interface method or abstract method which is only // implemented in one place (so only one possible target) llist* possTargets = getPossibleMethodTargets(decl(), (ClassDecl*)&receiver); if (possTargets->size() == 1 && possTargets->front()->decl()->container()->container()->fullName() != "ti.internal") { // PR606: don't statically bind calls to methods implemented only in ti.internal.ImplementsWorld methodDecl = dynamic_cast(possTargets->front()->decl()); result = true; } free_all(possTargets); } return result; } static const string dispatch( CodeContext &context, const ObjectFieldAccessNode &access, string &instance, vector< string > ¶meters, bool &staticDispatch ) { const TypeNode &objectType = *access.accessedObjectType(); ClassDecl &classDecl = static_cast< ClassDecl & >(*objectType.decl()); MethodDecl *methodDecl = static_cast< MethodDecl * >( access.decl() ); assert( classDecl.category() & (Decl::Class | Decl::Interface) ); assert( methodDecl->category() == Decl::Method || methodDecl->category() == Decl::Constructor ); const bool isLocal = objectType.isLocal(); const bool needsDispatch = !access.canCallStatically(methodDecl) && !isDomainOrtiDomainType(objectType); // special case for Domains staticDispatch = !needsDispatch; const bool isImmutable = objectType.isImmutable(); const bool isConstructor = (methodDecl->category() == Decl::Constructor); if (!(isImmutable && isConstructor)) { // Immutable constructors currently don't take a this argument instance = access.object()->emitExpression( context ); pushThis( context, instance, *methodDecl, parameters, objectType, false ); } if (objectType.isReference() && !isThisNode(access.object()) && // can always elide nullcheck on "this" !isConstructor) { // and on constructor calls context << lgMacro( "CHECK_NULL", isLocal ) << "_IFBC(" << instance << ", \"" << access.position().asString() << "\");" << endCline; } if (needsDispatch) { assert(!isImmutable && !isConstructor); if (methodDecl->container()->modifiers() & Common::Interface) { const string descTemp = getClassInfo( context, classDecl, instance, isLocal ); return "IFACE_DISPATCH_" + methodDecl->cMethodNameStatic() + '(' + descTemp + ')'; } else { const string descTemp = getClassInfo( context, classDecl, instance, isLocal ); return descTemp + "->" + methodDecl->cMethodNameDynamic(); } } else return methodDecl->cMethodNameStatic(); } const string MethodCallNode::emitExpression( CodeContext &context ) { Decl *d = method()->simpName()->decl(); string methodname = *(d->name()); // Here are the cases that we have to worry about: // The method call can either be on a java-defined class or titanium-defined class. // The call can be either a static method call or instance method call. // The object (for an instance method call) can be either "this" or an // explicitly defined object. TypeNode *t; assert(!isThisFAN(method()) && !isSFAN(method())); if (d->modifiers() & Common::Static) { if (isTypeFAN(method())) t = method()->ftype()->decl()->asType(); else t = method()->object()->type(); } else { t = method()->object()->type(); } // Inform the GC of escaped pointers during an exchange operations if (t->isTitaniumArrayType() && methodname == "exchange") { TreeNode *arg = args()->child(0); checkPointerEscape(arg, arg->simpleVar(context), "", true, "exchange", context); } if (((MethodDecl*)d)->isDead()) { static treeSet DMEErr; if (DMEErr.find(d->source()) == DMEErr.end()) { DMEErr.insert(d->source()); error() << "internal error: DME removed a live method: " << getMethodShortName(d->source()) << endl; } } return method()->emitMethodCall( context, *args() ); } static const string pointDispatch(MethodDecl * const methodDecl, int arity) { TypeListNode *params = methodDecl->type()->paramTypes(); string args; for (int i=0; iarity(); i++) { TypeNode ¶mType = *params->child(i); assert(!paramType.isJavaArrayType()); assert(!paramType.isTitaniumArrayType()); if (paramType.hasFormalSignature()) { args += paramType.formalSignature(); } else { args += paramType.cType(); } } assert(*methodDecl->name() != "arity"); // handled in lowering string conv_name = methodDecl->cMethodNameBare(); return MANGLE_TI_DOMAINS_POINT_RAWDISPATCH(+, int2string(arity), conv_name, args); } static const string domainDispatch(MethodDecl * const methodDecl, int arity, bool isDomain) { TypeListNode *params = methodDecl->type()->paramTypes(); string args; for (int i=0; iarity(); i++) { TypeNode ¶mType = *params->child(i); if (paramType.hasFormalSignature()) { args += paramType.formalSignature(); } else { args += paramType.cType(); } } assert(*methodDecl->name() != "arity"); // handled in lowering string conv_name = methodDecl->cMethodNameBare(); if (isDomain) { if (methodDecl->modifiers() & Common::Static) /* static Domain methods always dispatched to tiDomainN */ return conv_name + args + string("m") + MANGLE_DOMAIN_TYPE_UNBOXED(+, int2string(arity)); else { assert(implementedByDomain((MethodDecl*)methodDecl)); return MANGLE_TI_DOMAINS_DOMAIN_RAWDISPATCH(+, int2string(arity), conv_name, args); } } else return MANGLE_TI_DOMAINS_RECTDOMAIN_RAWDISPATCH(+, int2string(arity), conv_name, args); } static const string TypeFieldAccessNode_emitMethodCall(TreeNode *n, TypeNode *t, CodeContext &context, TreeListNode ¶ms) { MethodDecl *d = static_cast< MethodDecl * >(n->simpName()->decl()); string method; if (t->isPointType()) method = pointDispatch(d, t->tiArity()); else if (t->isRectDomainType()) method = domainDispatch(d, t->tiArity(), false); else if (t->isDomainType()) method = domainDispatch(d, t->tiArity(), true); else if (t->isTitaniumArrayType()) { const TitaniumArrayTypeNode &node = *reinterpret_cast(t); method = string("_ti_arrayclassmethod_") + *(d->name()) + '(' + node.elementType()->cType() + ", " + decimalString(node.expr()->typeName()) + ')'; } else method = d->cMethodNameStatic(); /* manual inlining for key static native methods */ if (d->fullName() == "ti.lang.Ti.thisProc") { string retval = "MYPROC"; return retval; } else if (d->fullName() == "ti.lang.Ti.numProcs") { string retval = "PROCS"; return retval; } else if (d->fullName() == "ti.lang.Ti.barrier") { string retval = "barrier()"; return retval; } else if (d->fullName().find("java.lang.Math.") == 0) { const string name = *(d->name()); #define MATH_FN(fn) if (name == #fn) method = "(jdouble)"#fn MATH_FN( acos ); MATH_FN( asin ); MATH_FN( atan ); MATH_FN( ceil ); MATH_FN( cos ); MATH_FN( exp ); MATH_FN( floor ); MATH_FN( log ); MATH_FN( rint ); MATH_FN( sin ); MATH_FN( sqrt ); MATH_FN( tan ); MATH_FN( IEEEremainder ); MATH_FN( atan2 ); MATH_FN( pow ); } else if (t->isPointType() && *d->name() == "all") { /* special cases for Point.all(0) and Point.all(1) */ if (params.arity() == 1 && isPrimitiveLitNode(params.child(0))) { int val = ((PrimitiveLitNode*)params.child(0))->literal().intValue(); if (val == 0) return MANGLE_TI_DOMAINS_POINT_EMPTY(+, int2string(t->tiArity())) + "()"; else if (val == 1) return MANGLE_TI_DOMAINS_POINT_UNIT(+, int2string(t->tiArity())) + "()"; else if (val == -1) return MANGLE_TI_DOMAINS_POINT_NEGUNIT(+, int2string(t->tiArity())) + "()"; } } vector< string > parameters; params.emitExpressionList( context, parameters ); emitCallTally(context, method, true, t, d->modifiers()); // ensure communication inside array calls gets traced to the right source line number context << "ti_srcpos();" << endCline; return locComment(n) + method + '(' + delimit( parameters ) + ')'; } const string TypeFieldAccessNode::emitMethodCall( CodeContext &context, TreeListNode ¶ms ) { return TypeFieldAccessNode_emitMethodCall(this, ftype(), context, params); } static const string SuperFieldAccessNode_emitMethodCall(ObjectFieldAccessNode *n, CodeContext &context, TreeListNode ¶ms) { MethodDecl *d = static_cast< MethodDecl * >( n->simpName()->decl() ); vector< string > parameters; // object might not be 'this' after inlining const string instance = n->object()->emitExpression( context ); pushThis( context, instance, *d, parameters, *d->container()->asType(), true ); params.emitExpressionList( context, parameters ); const string method = d->cMethodNameStatic(); emitCallTally(context, method, true, d->container()->asType(), d->modifiers()); return locComment(n) + method + '(' + delimit( parameters ) + ')'; } const string ObjectFieldAccessNode::emitMethodCall( CodeContext &context, TreeListNode ¶ms ) { MethodDecl *d = static_cast< MethodDecl * >( simpName()->decl() ); TypeNode &t = *object()->type(); bool staticDispatch_ref; // used for callTally only bool staticDispatch = true; if (d->modifiers() & Common::Static) return TypeFieldAccessNode_emitMethodCall(this, &t, context, params); if (isRewrittenSFAN()) return SuperFieldAccessNode_emitMethodCall(this, context, params); /* manual inlining for key native methods */ if (t.isRectDomainType() && (*d->name() == "isEmpty" || *d->name() == "isNull" || *d->name() == "isNotEmpty" || *d->name() == "isNotNull")) { // This assumes a representation of empty RectDomain where loopStride[1] < 0 const string sense = ((*d->name() == "isEmpty" || *d->name() == "isNull") ? "<" : ">="); const string instance = object()->emitExpression( context ); const string type = "tiRectDomain"+int2string(t.tiArity()); string retval = "(POINT"+int2string(t.tiArity())+"_GET((" + instance + ").f10loopStrideT"+int2string(type.length())+type+"7domains2ti,0) "+sense+" 0)"; return retval; } #if 0 cout << "emitMethodCall" << endl; parent()->pseudoprint(cout, 0); if (isObjectNode(object())) { cout << "calling object's decl: "; object()->name()->decl()->print(cout); cout << endl << "calling object's decl's source: "; object()->name()->decl()->source()->print(cout, 0); } cout << endl; #endif vector< string > parameters; string instance; string method; method = dispatch( context, *this, instance, parameters, staticDispatch_ref ); params.emitExpressionList( context, parameters ); if (t.isPointType()) method = pointDispatch(d, t.tiArity()); else if (t.isRectDomainType()) { method = domainDispatch(d, t.tiArity(), false); } else if (d->category() != Decl::Constructor && isDomainOrtiDomainType(t) && (implementedByDomain(d) || d->modifiers() & Common::Static)) { method = domainDispatch(d, isDomainOrtiDomainType(t), true); #if 0 // DOB: no longer use dynamic dispatch for Domains ClassDecl &classDecl = static_cast< ClassDecl & >(*t.decl()); assert( classDecl.category() == Decl::Class ); const string descTemp = getClassInfo( context, classDecl, instance, t.isLocal() ); method = descTemp + "->" + domainDispatch(d, t.tiArity(), true); #endif } else if (t.isTitaniumArrayType()) { const TitaniumArrayTypeNode &node = reinterpret_cast(t); string methodName; /* DOB: manual overloading for TiArray methods */ if (*d->name() == "writeTo" || *d->name() == "readFrom") { // I/O char* methodNameSuffix; const string argtypename = params.child(0)->type()->decl()->fullName(); if (argtypename == "java.io.RandomAccessFile") methodNameSuffix = "RAF"; else if (argtypename == "java.io.DataOutputStream") methodNameSuffix = "DOS"; else if (argtypename == "java.io.DataInputStream") methodNameSuffix = "DIS"; else unimplemented( "Ti array I/O with file objects other than RAF/DIS/DOS" ); methodName = *(d->name()) + methodNameSuffix; } else if (*d->name() == "copy" && params.arity() == 2) { // sparse copy TypeNode *argtype = params.child(1)->type(); if (argtype->isRectDomainType() && argtype->tiArity() == t.tiArity()) methodName = "copy_withrectdomain"; else if (argtype->isDomainType() && argtype->tiArity() == t.tiArity()) methodName = "copy_withdomain"; else if (argtype->isTitaniumArrayType() && argtype->tiArity() == 1 && argtype->elementType()->isPointType() && argtype->elementType()->tiArity() == t.tiArity()) methodName = "copy_withptarray"; else unimplemented( "Ti array copy with unrecognized 2rd argument type"); } else { methodName = *(d->name()); } method = arrayMethodPrefix(&t) + methodName + '(' + node.elementType()->cType() + ", " + decimalString(node.expr()->typeName()) + ')'; } else if (t.isJavaArrayType() && (*d->name() == "clone" || *d->name() == "localClone")) { // dispatch array cloning methods to the superclass methods in Object MethodDecl *superdecl = dynamic_cast(ObjectDecl->environ()->lookup(d->name())); assert(superdecl != NULL); method = superdecl->cMethodNameStatic(); // ensure communication inside array calls gets traced to the right source line number context << "ti_srcpos();" << endCline; return locComment(this) + method + '(' + "*(" + ObjectDecl->asType()-> withModifiers((Modifiers)(superdecl->modifiers() & Local))->cType() + " *)&" + delimit( parameters ) + ')'; } else { // plain-old Object or immutable staticDispatch = staticDispatch_ref; } emitCallTally(context, method, staticDispatch, &t, d->modifiers()); // ensure communication inside array calls gets traced to the right source line number context << "ti_srcpos();" << endCline; return locComment(this) + method + '(' + delimit( parameters ) + ')'; } /* Call the named method with the given argument list. If methodName starts with '_' assume it is an "internal" method (e.g., PTR). args should begin with an open paren and end with a close paren, or be the empty string. */ const string callGridMethod(TypeNode &t, const string methodName, const string args) { const TitaniumArrayTypeNode &node = reinterpret_cast(t); return (methodName.at(0) == '_' ? arrayInternal(methodName.substr(1), !t.isLocal()) : (arrayMethodPrefix(&t) + methodName)) + '(' + node.elementType()->cType() + ", " + decimalString(node.expr()->typeName()) + ')' + args; }