/* aux-code.cc: Code-generation-related implementations. */ #include #include #include #include #include "utils.h" #include "AST.h" #include "ClassDecl.h" #include "CodeContext.h" #include "CfHeader.h" #include "CfSource.h" #include "CtExternal.h" #include "CtObjectDescriptor.h" #include "ctBox.h" #include "FieldDecl.h" #include "MethodDecl.h" #include "aux-code.h" #include "aux-debug.h" #include "code.h" #include "code-assign.h" #include "code-util.h" #include "compiler.h" #include "code-grid.h" #include "decls.h" #include "delimit.h" #include "errors.h" #include "interface.h" #include "is-main.h" #include "lower.h" #include "nullify-local.h" #include "string-utils.h" #include "version.h" /* Set this to true if you want BlockNode::codeGen to void all */ /* its children (kludge). Put an */ /* extern bool voidResultsOfBlockStatements; */ /* in your code to get to it. */ bool voidResultsOfBlockStatements = false; extern void db_print_environ(Environ *); string FieldDecl::cFieldName() const { return MANGLE_FIELD(+, *name(), container()->cType()); } string cFieldName(Decl *decl) { assert( decl->category() == Decl::Field ); return static_cast< FieldDecl & >( *decl ).cFieldName(); } Decl *visibleMethod(Decl *meth, ClassDecl *cl) { foriter (decl, cl->environ()->allProperDecls(), EnvironIter) if ((decl->category() & Decl::Method) && (decl->name() == meth->name()) && (decl->type()->methodsConflict(meth->type()))) return &*decl; /* NOT REACHED */ assert(0); return NULL; } bool definedEarlier(Decl *meth, ClassDecl *cl) { if ((Decl *) cl == ObjectDecl) return false; ClassDecl *ancestor = cl->superClass(); foriter (decl, ancestor->environ()->allProperDecls(), EnvironIter) { if (decl->category() & Decl::Method) { if ((decl->name() == meth->name()) && (decl->type()->paramTypes() ->typeIdent(meth->type()->paramTypes()))) return true; } } return false; } void emitInstanceMethods(ClassDecl *cl, ClassDecl *outer, void (*outputFun)(MethodDecl &, ostream&), ostream& os) { if (cl) { if (cl != ObjectDecl) emitInstanceMethods(cl->superClass(), outer, outputFun, os); foriter (decl, cl->environ()->allProperDecls(), EnvironIter) if ((decl->category() == Decl::Method) && !(decl->modifiers() & (Common::Static | Common::Final)) && (decl->container() == cl) && (!definedEarlier(&*decl, cl))) { Decl *visibleDecl = visibleMethod(&*decl, outer); (*outputFun)(static_cast(*visibleDecl), os); } } } // Emit new instance fields introduced by this class, not inherited // from an ancestral superclass. void emitNovelInstanceFields(ClassDecl *cl, FieldPrinter outputFun, CfCode& os) { foriter (decl, cl->environ()->allProperDecls(), EnvironIter) if (decl->category() == Decl::Field && !(decl->modifiers() & Common::Static) && (decl->container() == cl)) (*outputFun)(static_cast(*decl), os); } // Emit all instance fields, including those inherited from an // ancestral superclass. void emitInstanceFields(ClassDecl *cl, FieldPrinter outputFun, CfCode& os) { if (cl) { if (cl != ObjectDecl) emitInstanceFields(cl->superClass(), outputFun, os); emitNovelInstanceFields(cl, outputFun, os); } } void emitStaticMethods(ClassDecl *cl, void (*outputFun)(Decl *, ostream&), ostream& os) { foriter (decl, cl->environ()->allProperDecls(), EnvironIter) { if (decl->category() & Decl::Method) { if ((decl->name() == cl->name()) || ((decl->modifiers() & Common::Static))) { (*outputFun)(&*decl, os); } } } } void emitStaticFields(ClassDecl &cl, FieldPrinter outputFun, CfCode &os) { foriter (decl, cl.environ()->allProperDecls(), EnvironIter) if ((decl->category() == Decl::Field) && (decl->modifiers() & Common::Static)) { Decl * const container = decl->container(); if (container == &cl || container->category() != Decl::Interface) (*outputFun)(static_cast< FieldDecl & >(*decl), os); } } void methodDecl(MethodDecl &decl, CfCode &os) { const TypeNode &type = *decl.type(); const TypeNode &container = *decl.container()->asType(); const TypeListNode ¶ms = *type.paramTypes(); const bool immCtor = container.isImmutable() && decl.category() & Decl::Constructor; const TypeNode * const thisType = decl.thisType(); const TypeNode &returnType = immCtor ? *thisType : *decl.type()->returnType(); vector< string > formals; if (thisType && !immCtor) { formals.push_back( thisType->cType() ); os.add( thisType->cType() ); } for (int i = 0; i < params.arity(); i++) { formals.push_back( params.child(i)->cType() ); os.add( params.child(i)->cType() ); } os.add( returnType.cType() ); os << "extern " << returnType.cType() << ' ' << decl.cMethodNameStatic() << "(" << delimit( formals ) << ");\n"; } // C Type declaration for a method string cMethodTypeString(const MethodDecl &method) { TypeListNode *params = method.type()->paramTypes(); const TypeNode * const thisType = method.thisType(); vector< string > formals; if (thisType) formals.push_back( thisType->cType() ); for (int i=0; iarity(); i++) formals.push_back( params->child(i)->cType() ); return method.type()->returnType()->cType() + "(*)(" + delimit( formals ) + ')'; } void staticFieldDecl(FieldDecl &fld, CfCode& os) { os.add( fld.cType() ); os << fld.type()->cType() << ' ' << fld.cFieldName() << ";\n"; } static void fieldComp(FieldDecl &fld, CfCode& os) { if (fld.container()->asType()->isImmutable()) { os << " && (memcmp(&s1." << fld.cFieldName() << ", &s2." << fld.cFieldName() << ", sizeof(" << fld.type()->cType() << ")) == 0)"; } else { os << " && (s1." << fld.cFieldName() << " == s2." << fld.cFieldName() << ")"; } } void externStaticFieldDecl(FieldDecl &fld, CfCode& os) { os << "extern "; staticFieldDecl(fld, os); } void tableDefn(MethodDecl &method, ostream& os) { if (method.modifiers() & Common::Abstract) os << 0; else os << method.cMethodNameStatic(); os << ",\n"; } bool implements (Decl* aClass, Decl* interface); void emitDefinitions(ClassDecl &cl, CfHeader &hFile, CfSource &cFile ) { // For some reason, Object lists Object as its superclass. :-( ClassDecl * const super = (&cl == ObjectDecl ? 0 : cl.superClass()); const string superDescName = super ? super->cDescriptorName() : string(""); /* const CtType &superDescType = super ? static_cast< const CtType & >(super->cDescriptorType()) : static_cast< const CtType & >(CtObjectDescriptor::classHeader); */ const CtType &classDescType = cl.cDescriptorType(); const string classDescName = cl.cDescriptorName(); const string classStaticFieldsStructName = cl.cStaticFieldsStructName(); const CtType &className = cl.cType(); const string nullifier = cl.cNullifyLocalName(); const bool isImmutable = cl.asType()->isImmutable(); // Other classes upon which we depend { cl.includeSupers( hFile ); cl.includeSelf( cFile ); cl.includeRequirements( cFile ); } // Instance fields className.define( hFile ); // Class descriptor { classDescType.define( hFile ); hFile << "extern " << classDescType << ' ' << classDescName << ";\n"; classDescType.define( cFile ); const char *interfaceList = cl.emitInterfaceList( cFile ); cFile << classDescType << ' ' << classDescName << " = {\n" << " { &" << JavaLangClassDecl->cDescriptorName() << " },\n" << " NULL,\n"; if (isImmutable) { cFile << " Value,\n" << " sizeof( " << className << " ),\n"; } else { cFile << " Class,\n" << " " << interfaceList << ",\n"; cFile << " (void (**)())" << cl.cIntfMTName() << ",\n"; if (super) cFile << " &" << superDescName << ",\n"; else cFile << " 0,\n"; cFile << " sizeof( " << className << " ),\n" << " " << nullifier << ",\n"; emitInstanceMethods(&cl, &cl, tableDefn, cFile); } cFile << "};\n\n"; } // Interface Method Table if (!isImmutable) { hFile << "extern void (*" << cl.cIntfMTName() + "[])();\n\n"; EmitIntfMT(cl, cFile); } // Static fields { hFile << "struct " << classDescName << "_static_fields_struct {\n" << "#ifdef HAVE_MONITORS\n" << " tic_monitor_t monitor;\n" << "#endif /* HAVE_MONITORS */\n"; emitStaticFields(cl, staticFieldDecl, hFile); // some linkers choke if we try to declare struct variables with zero size, // so make sure we have at least one byte in here: hFile << "char _pad; /* padding to prevent zero-size structs */\n"; hFile << "};\n"; hFile << "extern struct " << classDescName << "_static_fields_struct " << "STATIC_DEF(" << classStaticFieldsStructName << ");\n"; cFile << "struct " << classDescName << "_static_fields_struct " << "STATIC_DEF(" << classStaticFieldsStructName << ");\n"; } // Generate an equals method if (isImmutable) { hFile << "extern int " << MANGLE_CLASS_EQUALS(<<, className) << "(" << cl.asType()->cType() << ", " << cl.asType()->cType() << ");\n"; cFile << "int " << MANGLE_CLASS_EQUALS(<<, className) << "(" << cl.asType()->cType() << " s1, " << cl.asType()->cType() << " s2) {\n" << "return (1"; emitInstanceFields(&cl, fieldComp, cFile); cFile << ");\n" << "}\n"; } // Local field nullifier, used when cloning { hFile << "void " << nullifier << "(" << className << " *);\n"; cFile << "void " << nullifier << "(" << className << " *" << MANGLE_THIS_VAR(<<) << ") {\n"; emitInstanceFields( &cl, fieldNullifyLocal, cFile ); cFile << "}\n"; } // detect & warn about local field nullification on clone() if (!cl.asType()->isImmutable() && implements(&cl, CloneableDecl)) { bool containsLPs = false; foriter (field, cl.environ()->allDecls( Decl::Field ), EnvironIter) { const FieldDecl &fieldDecl = static_cast< FieldDecl & >( *field ); if (fieldDecl.containsEmbeddedLocals()) containsLPs = true; } if (containsLPs) cl.source()->warning() << "Class " << cl.errorName() << " contains local fields and implements Cloneable.\n" << " Local fields will be nullified by Object.clone()\n"; } } template< class Iterator > static void declareAll( CfCode &os, Iterator begin, const Iterator end ) { while (begin < end) { TreeNode *method = *begin++; if (!method->absent()) { MethodDecl &decl = static_cast< MethodDecl & >(*method->decl()); assert( decl.category() & (Decl::Constructor | Decl::Method) ); methodDecl( decl, os ); } } os << '\n'; } template< class Container > inline static void declareAll( CfCode &os, const Container &container ) { ::declareAll( os, container.begin(), container.end() ); } extern void resetPerFileTempCounters() { // this function resets counters used to generate unique temporary names // we do this when starting each source file to // prevent a change in one source file propagate insignificant naming changes into // other source files, thus reducing recompilation of unchanged source files // reset the temporary counter in ExprNode ExprNode::resetTemporary(); MonitorFetchNode::resetGenerator(); // reset the temporary count in code-alloc.cc extern int initTempNum; initTempNum = 0; // reset the temporary count in code-foreach.cc extern void reset_foreach_counters(); reset_foreach_counters(); // reset the local variable temporary count in code-decl.cc extern int VarDeclTempNum; VarDeclTempNum = 0; } void ClassDeclNode::codeGen() { // voidResultsOfBlockStatements = true; resetPerFileTempCounters(); // reset temp counters when starting each source file to // prevent spurious recompilation of unchanged source files bool hasNatives = false; const CtType &longName = decl()->cType(); const string classStaticFieldsStructName = decl()->cStaticFieldsStructName(); CfHeader hFile( longName ); CfSource cFile( longName ); hFile << "\n/* ***** " << decl()->fullName() << " generated header file ***** */\n\n"; cFile << "\n/* ***** " << decl()->fullName() << " generated source file ***** */\n\n"; emitDefinitions(*decl(), hFile, cFile); // group members according to where their code goes vector< TreeNode * > classInits, fields, methods; foriter (member, members () -> allChildren (), ChildIter) { TreeNode * const node = *member; vector< TreeNode * > *category = 0; if (isFieldDeclNode(node)) { const FieldDeclNode * const fieldDecl = static_cast< const FieldDeclNode * >( node ); if (fieldDecl->decl()->modifiers() & Static) category = &classInits; else category = &fields; } else if (isStaticInitNode(node)) category = &classInits; else if (!node->absent()) { category = &methods; if (node->decl()->modifiers() & Native) hasNatives = true; } if (category) category->push_back( node ); } // forward declare the string table, currently of unknown size cFile.declare( cFile ); // now put each bit of code in its proper place #if 0 // object instance initializers are handled by lowering now TypeNode &type = *decl()->asType(); // first, object instance initializers // this needs to be externally visible to support constructor inlining cFile << "\n/* Object instance field initializer */\n"; if (type.isImmutable()) { hFile << "\nextern void " << decl()->cInitFieldsName() << "(" << decl()->cType() << " *" << ");\n"; cFile << "void " << decl()->cInitFieldsName() << "(" << decl()->cType() << " *" << MANGLE_THIS_VAR(<<) << ")\n"; } else { const CtType &rawType = decl()->cType(); const CtType &boxedType = ctBox( rawType, true ); hFile << "\nextern void " << decl()->cInitFieldsName() << "(" << boxedType << ");\n"; cFile << "void " << decl()->cInitFieldsName() << "(" << boxedType << " " << MANGLE_THIS_VAR(<<) << ")\n"; } { CodeContext subcontext( cFile ); EncapsulateInits(fields)->emitStatement(subcontext); } cFile << '\n'; #endif hFile << "extern void " << MANGLE_CLASS_INIT(<<, longName) << "();\n"; cFile << "static void " << MANGLE_CLASS_BUILDST(<<, longName) << "();\n"; cFile << "\n/* --- " << decl()->fullName() << " Class Initializer --- */\n\n"; cFile << "void " << MANGLE_CLASS_INIT(<<, longName) << "()\n"; { CodeContext subcontext( cFile ); subcontext << "#ifdef HAVE_MONITORS\n" << "monitor_init( &STATIC_REF(" << classStaticFieldsStructName << ", monitor ));\n" << "monitor_init( &" << decl()->cDescriptorName() << ".class_object.monitor);\n" << "#endif /*HAVE_MONITORS*/\n"; subcontext << MANGLE_CLASS_BUILDST(<<, longName) << "();\n"; subcontext << decl()->cDescriptorName() << ".class_name = java_string_build_8(\"" << decl()->fullName() << "\");\n"; EncapsulateInits(classInits)->emitStatement(subcontext); } if (hasNatives) cFile << "\n\n#include \"" << decl()->cNativeName() << "\"\n"; cFile << '\n'; declareAll( hFile, methods ); codeGenAll( cFile, methods ); cFile << "\n/* --- " << decl()->fullName() << " Class String-table builder --- */\n\n"; cFile << "static void " << MANGLE_CLASS_BUILDST(<<, longName) << "()\n"; { CodeContext context( cFile ); cFile.internAll( context ); } } const string locComment(TreeNode *t) { static SourcePosn prevPosition; const SourcePosn position = t->position(); if (!position.unknown() && (position.file != prevPosition.file || position.posn != prevPosition.posn)) { prevPosition = position; return "/* " + position.asString() + " */ "; } return ""; } /* TreeNode */ void TreeNode::codeGen() { } void TreeNode::codeGen( CfSource & ) { undefined( "codeGen( CfSource & )" ); } void TreeNode::codeGen( CodeContext &context ) { foriter (p, allChildren(), ChildIter) (*p)->codeGen( context ); } void CompileUnitNode::codeGen() { if (selectedForCodeGen(false)) foriter (type, types()->allChildren(), ChildIter) if ((*type)->selectedForCodeGen(false)) (*type)->codeGen(); } bool CompileUnitNode::selectedForCodeGen(bool force_libs) const { const TreeNode &pack = *package(); if (pack.absent()) return true; else { const string name = pack.decl()->fullName(); if (name == "ti.internal") return false; else if (codeGen_libs || force_libs) return true; else if (hasPrefix(name, "java.")) return false; else if (hasPrefix(name, "ti.")) return false; else return true; } } bool TemplateDeclNode::selectedForCodeGen( bool ) const { return false; } bool ClassDeclNode::selectedForCodeGen(bool) const { if (decl()->container()->fullName() != "ti.domains") return true; const string name = *decl()->name(); return name != "tiPoint1" && name != "tiPoint2" && name != "tiPoint3" && name != "tiArray1" && name != "tiArray2" && name != "tiArray3"; } bool TitaniumArrayTypeNode::selectedForCodeGen(bool force_libs) const { if (codeGen_libs || force_libs) return true; // don't regenerate those which appear in the tlib if (elementType()->isPrimitive()) return false; if (elementType()->isPointType() && tiArity() == 1) return false; if (elementType()->isRectDomainType() && tiArity() == 1) return false; return true; } extern char *currentFilename; // used for -dumpast switch extern string tcCompilerFlags; void backend() { extern void createDomains_h(); extern void initPolys(); initPolys(); CreateIntfMethods(); // Generate the .c and .h files for each class compile_status(1,"Code generation & optimization..."); foreach (f, llist, *allFiles) { currentFilename = (char *)((*f)->ident()->c_str()); if ((*f)->selectedForCodeGen(false)) compile_status(2, string("generating/optimizing: ") + currentFilename); (*f)->codeGen(); } #if 0 foreach (instance, llist, *templateEnv.allInstances()) { const TreeNode &node = **instance; Decl &decl = *node.decl(); TypeNode &type = *decl.asType(); type.codeGen(); } #endif if (codeGen_main) { compile_status(1,"Generating ti-main..."); // Generate a main.c file that pulls all together string s; CfSource mainFile("ti-main"); mainFile << "#include \"ti-main.h\"\n"; foreach (f, llist, *allFiles) { TreeNode *const pack = (*f)->package(); if (!pack->absent() && pack->decl()->fullName() == "ti.internal") continue; // Spit out include directives foriter (type, (*f)->types()->allChildren(), TreeNode::ChildIter) if ((isClassDeclNode(*type) || isInterfaceDeclNode(*type)) && (*type)->selectedForCodeGen(false)) // filter out tiPoint[123] (*type)->decl()->includeSelf( mainFile ); } /* these variables must be nonstatic, otherwise craycc will optimize * them away and they won't be embedded in the compiled application * must be extern or non-const or AIX linker will optimize them away * DOB: use TI_IDENT() instead, which is tuned for all the compiler subtleties */ mainFile << "int titaniumCompilerMajorVersion = " << tcMajorVersion << ";\n" << "int titaniumCompilerMinorVersion = " << tcMinorVersion << ";\n" << "extern char *tcCompilerFlags;" "TI_IDENT(titaniumCompilerWhatString, \"" << tcWhatString << "\");\n" "TI_IDENT(identVersion, \"$TitaniumVersion: " << tcMajorVersion << "." << tcMinorVersion << " $\");\n" "TI_IDENT(identCompilerFlags, \"$TitaniumCompilerFlags: " << tcCompilerFlags << " $\");\n" "char *tcCompilerFlags = \"" << tcCompilerFlags << "\";\n" "\n" "struct class_header *main_class;\n" "\n" "void ti_main(int argc, char **argv) {\n"; /* Debug information */ emitStackDebugInformation(mainFile); mainFile << " init_memory_tally();\n" " gp_trace_open();\n" " value_desc_init();\n"; ostringstream libraryClassInit, userClassInit; foreach (f, llist, *allFiles) { TreeNode *const pack = (*f)->package(); if (!pack->absent() && pack->decl()->fullName() == "ti.internal") continue; // Call class initialization functions // this is still a long way from the Java class initialization semantics // (because it all happens at process startup in almost random order) // but we can at least trivially ensure that user initializers run after library initializers foriter (type, (*f)->types()->allChildren(), TreeNode::ChildIter) { if ((*type)->selectedForCodeGen(false)) { const string name = (*type)->decl()->fullName(); const ClassDecl &decl = static_cast< const ClassDecl & >(*(*type)->decl()); if (hasPrefix(name, "java.") || hasPrefix(name, "ti.")) // it's a runtime library class libraryClassInit << " " << MANGLE_CLASS_INIT(<<, decl.cType()) << "();\n"; else // it's a user class userClassInit << " " << MANGLE_CLASS_INIT(<<, decl.cType()) << "();\n"; } } } mainFile << "\n /* Run library class initializers */\n"; mainFile << libraryClassInit.str(); mainFile << "\n /* Run user class initializers */\n"; mainFile << userClassInit.str(); mainFile << "\n"; const MethodDecl &mainMethod = **mainMethods.begin(); // pause processes after initialization mainFile << " barrier();\n" << " " << mainMethod.cMethodNameStatic() << "(java_strings_build(argc - 1, argv + 1));\n" << " barrier();\n" << " gp_trace_close();\n" << " print_memory_tally();\n" << "}\n"; /* Debug information */ emitStaticDebugInformation(mainFile, mainMethod); } // Create arrays.h and arrays.c instantiateArrays(); // Create domains.h createDomains_h(); // Create type headers CtType::elaborateAll(); }