#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 "config.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 "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; } } #if 0 static const char *retTypeSig(string sig) { int i; for (i=0; sig[i] != ')'; i++); switch (sig[i+1]) { // case 'T': case 'V': return ""; case 'Q': case 'L': return ".l"; case 'Z': return ".z"; case 'B': return ".b"; case 'C': return ".c"; case 'S': return ".s"; case 'I': return ".i"; case 'J': return ".j"; case 'F': return ".f"; case 'D': return ".d"; default: return 0; // should never happen } } #endif 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 = 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); } bool ObjectFieldAccessNode::canCallStatically() const { assert( decl()->category() == Decl::Method || decl()->category() == Decl::Constructor); const TypeDecl &receiver = *accessedObjectType()->decl(); if (receiver.category() == Decl::Class) { // fundamentally cannot be overridden if (decl()->modifiers() & (Final | Private | Static)) return true; // constructor calls can always be statically bound if (decl()->category() == Decl::Constructor) return true; // dispatched to a final class if (receiver.modifiers() & Final) return true; // not observed to be overridden anywhere if (codeGen_main && opt_finalize && decl()->overriders()->empty()) return true; } return false; } static const string dispatch( CodeContext &context, const ObjectFieldAccessNode &access, const string &instance, vector< string > ¶meters ) { const TypeNode &objectType = *access.accessedObjectType(); ClassDecl &classDecl = static_cast< ClassDecl & >(*objectType.decl()); const 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(); const bool isImmutable = objectType.isImmutable(); const bool isConstructor = (methodDecl.category() == Decl::Constructor); if (objectType.isReference() && !isThisNode(access.object()) && // can always elide nullcheck on "this" !isConstructor) { // and on constructor calls context << "#if BOUNDS_CHECKING" << endCline << " " << lgMacro("CHECK_NULL", isLocal ) << '(' << instance << ')' << ';' << endCline << "#endif" << endCline; } if (!(isImmutable && isConstructor)) // Immutable constructors currently don't take a this argument pushThis( context, instance, methodDecl, parameters, objectType, false ); if (needsDispatch) { 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), "", 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(Decl * const methodDecl, int arity) { TypeListNode *params = methodDecl->type()->paramTypes(); string conv_name; const string name = *methodDecl->name(); string ending; for (int i=0; iarity(); i++) { TypeNode ¶mType = *params->child(i); assert(!paramType.isJavaArrayType()); assert(!paramType.isTitaniumArrayType()); if (paramType.hasFormalSignature()) { ending += paramType.formalSignature(); } else { ending += paramType.cType(); } } static stringmap m; static bool inited = false; if (!inited) { static char *s[] = { "+", "add", "-", "sub", "*", "mul", "/", "div", "==", "isEqual", "!=", "isNotEqual", "<", "isLessThan", "<=", "isLessThanEqual", ">", "isGreaterThan", ">=", "isGreaterThanEqual", "set", "set", "get", "get", "getPrevious", "getPrevious", "setThis", "setThis", "setAllThis", "setAllThis", "permute", "permute", "getLowerBound", "getLowerBound", "getUpperBound", "getUpperBound", /* "dot", "dot", */ "getLcm", "getLcm", "all", "all", "direction", "setDirection", /* "arity", "arity", DOB: handed in lowering */ "toString", "toString" }; for (int i = 0; i < (int)(sizeof(s) / sizeof(s[0])); i += 2) { m[string(s[i])] = string(s[i + 1]); m[string(s[i + 1])] = string(s[i + 1]); } inited = true; } if ((conv_name = m[name]) == "") { printf("Internal Error: Unknown point call to \"%s\"\n", name.c_str()); return string("???"); } return string("m") + int2string(conv_name.length()) + conv_name + ending + "m" + PointNDecl[arity - 1]->cType(); } string rectDomainMethodSlice(Decl * const methodDecl, string instance, int arity, string dim_param, CodeContext &os, const string temp_p0, const string temp_p1, const string temp_stride) { // const CtType &pointFull = *new CtPointInstance( arity ); const CtType &pointSliced = *new CtPointInstance( arity - 1 ); string arity_str; string arity_m1_str; arity_str = int2string(arity); arity_m1_str = int2string(arity-1); os.declare( temp_p0, pointSliced ); os.declare( temp_p1, pointSliced ); os.declare( temp_stride, pointSliced ); os << "{" << endCline; os << " P" << arity << " slicevar_old_p0, slicevar_old_p1, slicevar_old_stride;" << endCline; os << " int slicevar_index, slicevar_count;" << endCline; os << " int slicevar_kill_dim = (" << dim_param << ") - 1;" << endCline; os << " slicevar_old_p0 = " << MANGLE_TI_DOMAINS_RECTDOMAIN_DISPATCH(<<, arity_str, "getLowerBound", "") << instance << ");" << endCline; os << " slicevar_old_p1 = " << MANGLE_TI_DOMAINS_RECTDOMAIN_DISPATCH(<<, arity_str, "getUpperBound", "") << instance << ");" << endCline; os << " slicevar_old_stride = " << MANGLE_TI_DOMAINS_RECTDOMAIN_DISPATCH(<<, arity_str, "getStride", "") << instance << ");" << endCline; os << " for (slicevar_index = 0, slicevar_count = 0; slicevar_index < " << arity << "; slicevar_index++) {" << endCline; os << " if (slicevar_index != slicevar_kill_dim) {" << endCline; os << " " << temp_p0 << " = " << SET_POINT_INDEX_START(<<, arity_m1_str) << temp_p0 << ", " << SET_POINT_INDEX_END(<<, "slicevar_count", MANGLE_TI_DOMAINS_POINT_GET(<<, arity_str) << "slicevar_old_p0, slicevar_index)") << ";" << endCline; os << " " << temp_p1 << " = " << SET_POINT_INDEX_START(<<, arity_m1_str) << temp_p1 << ", " << SET_POINT_INDEX_END(<<, "slicevar_count", MANGLE_TI_DOMAINS_POINT_GET(<<, arity_str) << "slicevar_old_p1, slicevar_index)") << ";" << endCline; os << " " << temp_stride << " = " << SET_POINT_INDEX_START(<<, arity_m1_str) << temp_stride << ", " << SET_POINT_INDEX_END(<<, "slicevar_count", MANGLE_TI_DOMAINS_POINT_GET(<<, arity_str) << "slicevar_old_stride, slicevar_index)") << ";" << endCline; os << " slicevar_count++;" << endCline; os << " }" << endCline; os << " }" << endCline; os << "}" << endCline; return (string) NEW_RECTDOMAIN_3PT_UPB_START(+, arity_m1_str) + temp_p0 + "," + temp_p1 + "," + temp_stride + NEW_RECTDOMAIN_3PT_UPB_END(); } static const string domainDispatch(Decl * const methodDecl, int arity, bool isDomain, bool genStatic) { TypeListNode *params = methodDecl->type()->paramTypes(); string conv_name; string ending; const string name = *methodDecl->name(); string result; for (int i=0; iarity(); i++) { TypeNode ¶mType = *params->child(i); if (paramType.hasFormalSignature()) { ending += paramType.formalSignature(); } else { ending += paramType.cType(); } } if (name == "isRectangular") { conv_name = "isRectangular"; } else if (name == "+") { conv_name = "add"; } else if (name == "-") { conv_name = "difference"; } else if (name == "*") { conv_name = "multiply"; } else if (name == "/") { conv_name = "divide"; } else if (name == "==") { conv_name = "isEqual"; } else if (name == "!=") { conv_name = "isNotEqual"; } else if (name == "<") { conv_name = "isStrictSubset"; } else if (name == "<=") { conv_name = "isSubset"; } else if (name == ">") { conv_name = "isStrictSuperset"; } else if (name == ">=") { conv_name = "isSuperset"; } else if (name == "min") { conv_name = "getLowerBound"; } else if (name == "max") { conv_name = "getUpperMaxPoint"; } else if (name == "lwb") { conv_name = "getLowerBound"; } else if (name == "upb") { conv_name = "getUpperBound"; } else if (name == "stride") { conv_name = "getLoopingStride"; } else if (name == "accrete") { conv_name = "accrete"; } else if (name == "shrink") { conv_name = "shrink"; } else if (name == "border") { conv_name = "border"; } else if (name == "contains") { conv_name = "doesContain"; } else if (name == "boundingBox") { conv_name = "getBoundingBox"; /* } else if (name == "arity") { // handled in lowering conv_name = "getArity"; */ } else if (name == "size") { conv_name = "getNumPoints"; } else if (name == "isNull") { conv_name = "isNull"; } else if (name == "isNotNull") { conv_name = "isNotNull"; } else if (name == "isADR") { conv_name = "isADR"; } else if (name == "toString") { conv_name = "toString"; } else if (name == "permute") { conv_name = "permute"; } else if (isDomain && (name == "RectDomainList")) { conv_name = "getRectDomains"; } else if (isDomain && (name == "PointList")) { conv_name = "getPoints"; } else if (isDomain && (name == "toDomain")) { conv_name = "factory"; } else if (isDomain && (name == "setRegion")) { conv_name = "setRegion"; } else { cerr << "Internal Error: Unknown domain call to \"" << name << '\"' << endl; conv_name = "???"; } result = string("m") + int2string(conv_name.length()) + conv_name + ending; if ((!isDomain) || genStatic) { if (isDomain) { result += string("mT") + int2string(int2string(arity).length() + 8) + "tiDomain" + int2string(arity) + "7domains2ti"; } else { result += string("mT") + int2string(int2string(arity).length() + 12) + "tiRectDomain" + int2string(arity) + "7domains2ti"; } } return result; } static const string TypeFieldAccessNode_emitMethodCall(TreeNode *n, TypeNode *t, CodeContext &context, TreeListNode ¶ms) { Decl *d = n->simpName()->decl(); string method; vector< string > parameters; params.emitExpressionList( context, parameters ); if (t->isPointType()) method = pointDispatch(d, t->tiArity()); else if (t->isRectDomainType()) method = domainDispatch(d, t->tiArity(), false, true); else if (t->isDomainType()) method = domainDispatch(d, t->tiArity(), true, 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().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 ); } emitCallTally(context, method, true, t, d->modifiers()); 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(); 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 (d->fullName() == "java.lang.Object.object_initialize") { // inline object initialization for performance const string instance = object()->emitExpression( context ); string retval; retval += "/* java.lang.Object.object_initialize() */\n"; retval += "#ifdef HAVE_MONITORS\n"; retval += " monitor_init(&((*(LT6Object4lang4java *)&" + instance + ")->monitor));\n"; retval += "#endif /* HAVE_MONITORS */\n\n"; 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; const string instance = object()->emitExpression( context ); string method = dispatch( context, *this, instance, parameters ); params.emitExpressionList( context, parameters ); if (t.isPointType()) method = pointDispatch(d, t.tiArity()); else if (t.isRectDomainType()) { if (*d->name() == "slice") { return rectDomainMethodSlice(d, instance, t.tiArity(), parameters[1], context, getTemporary(), getTemporary(), getTemporary()); } else { method = domainDispatch(d, t.tiArity(), false, false); } } else if (t.isDomainType()) { 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, false); } 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(); return locComment(this) + method + '(' + "*(" + ObjectDecl->asType()->cType() + " *)&" + delimit( parameters ) + ')'; } emitCallTally(context, method, (method == d->cMethodNameStatic()), &t, d->modifiers()); 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; }