#include "AST.h" #include "CodeContext.h" #include "CfSource.h" #include "CtType.h" #include "MethodDecl.h" #include "FieldDecl.h" #include "code.h" #include "code-assign.h" #include "code-MIVE.h" #include "decls.h" #include "optimize.h" #include "lower.h" #include "Poly.h" #include "osstream.h" #include "inline.h" #include "string-utils.h" #define DEBUG_FREE DEBUG_PHASE_ENABLED("free", currentFilename) extern bool freeMethodBodies; /* tc command-line flag */ static void removeAliasing(TreeNode *n) { int childCount = n->arity(); for (int sweep = 0; sweep < childCount; sweep++) { TreeNode *child = n->child(sweep); if (child != TreeNode::omitted && !::strstr(child->oper_name(), "Type")) { if (child->parent() != n) { n->child(sweep, TreeNode::omitted); // someone else has an alias to my child, so release it } else removeAliasing(child); } } } static void freeASTNodes(TreeNode *t) { #if 0 // fails because lazy opts modify the AST in ways it doesn't like if (!t->checkIF()) { cout << "*** failed checkIF() while trying to free" << endl; fatal_error(""); } #endif #if 1 // rather than reporting errors about aliasing, just deal with them removeAliasing(t); #else t->fixParent(); if (checkAliasing(t)) { fatal_error("*** failed checkAliasing() while trying to free"); } #endif t->free(); } // free method bodies after code-gen void freeMethodBody(TreeNode *method) { if (freeMethodBodies) { assert(isMethodDeclNode(method) || isConstructorDeclNode(method)); if (DEBUG_FREE) cout << "Freeing method " << method->decl()->fullName() << endl; freeASTNodes(method->body()); method->body(TreeNode::omitted); if (isConstructorDeclNode(method)) { freeASTNodes(method->constructorCall()); method->constructorCall(TreeNode::omitted); } NodeStorage *ns = method->getNodeStorage(); method->setBblockRoot(NULL); method->setNodeStorage(NULL); delete ns; if (DEBUG_FREE) cout << "Finished freeing method " << method->decl()->fullName() << endl; } } void FieldDeclNode::codeGen( CodeContext &context ) { fatal_error(""); // DOB: we never directly call codeGen on a FieldDeclNode anymore // ClassDeclNode::codeGen pulls out the relevant declarations // and EncapsulateInits uses the info in this node to generate initializer expressions } int VarDeclTempNum = -1; void VarDeclNode::codeGen( CodeContext &context ) { assert(VarDeclTempNum >= 0); // make sure resetPerFileTempCounters() was called // we should never codeGen the same DeclNode twice if (mangledIdent() != NULL) { fatal_error("!! *mangledIdent() for " + pseudocode(this) + " is " + *mangledIdent()); } ostringstream assemble; if (dtype()->modifiers() & Common::CompilerGenerated) { string id = *simpName()->simpName()->ident(); if (hasPrefix(id, "inl")) id = "inl"; // renormalize temps created by inlining to reduce recompilation assemble << MANGLE_TEMP(+, id); } else assemble << MANGLE_STACK_VAR(+, *simpName()->simpName()->ident()); assemble << "_u" << (VarDeclTempNum++); mangledIdent() = new string(assemble.str()); context.declare( *mangledIdent(), dtype()->cType() ); /* cout << "set *mangledIdent() for " << pseudocode(this) << " to " << *mangledIdent() << endl; */ } void MethodDeclNode::codeGen( CfSource &os ) { compile_status(3, string("codeGen: ") + nameOfEnclosingMethod(this)); MethodDecl &mdecl = static_cast< MethodDecl & >( *decl() ); if (!(mdecl.modifiers() & (Common::Native | Common::Abstract))) { bool first = true; extern bool line_directives; if ((debugger_level > 0) && (line_directives)) { const SourcePosn pos = position(); if (!pos.unknown()) { os << "#line " << pos.posnToLineNumber() << " \"" << *pos.file << "\"\n"; } } os << "\n/* --- " << getMethodFullName(this) << " --- */\n\n"; // DOB: used by runtime/native-utils.h to output location on exceptions os << "#undef __TI_CURRENT_FUNCTION__\n"; os << "#define __TI_CURRENT_FUNCTION__ \"" << getMethodShortName(this) << "\"\n"; os << returnType()->cType() << ' ' << mdecl.cMethodNameStatic() << '('; const TypeNode *thisType = mdecl.thisType(); if (thisType) { os << thisType->cType() << " " << MANGLE_THIS_VAR(<<); first = false; } foriter (param, params()->allChildren(), TreeNode::ChildIter) { if (first) first = false; else os << ","; os << (*param)->dtype()->cType() << " " << MANGLE_STACK_VAR(<<, *(*param)->simpName()->ident()); } os << ") { \n"; os << "TI_BEGIN_FUNCTION\n"; if (mdecl.isDead()) { os << "fprintf(stderr,\"ERROR: Called a method removed by DME: " << getMethodShortName(this) << " \\n\"); fflush(stderr);" << endl << " abort();" << endl; } else { CodeContext context(os); body()->codeGen(context); if ((flags() | Common::Synchronized) && returnType()->kind() != VoidKind) { /* DOB: hack-fix for a very hairy interaction between synchronized and our exception handling mechanism that creates C code which appears to allow control to reach the end of a non-void C function (although it never does) */ context << "abort(); /* never reach here */" << endCline; } freePolys(); reset_MIVE(); } os << "\n}\n"; // free method bodies after codegen freeMethodBody(this); } } void MethodSignatureNode::codeGen ( CodeContext & ) { } void ConstructorDeclNode::codeGen( CfSource &os ) { const MethodDecl &mdecl = static_cast< MethodDecl & >( *decl() ); const TypeNode &thisType = *mdecl.thisType(); const CtType &thisTypeName = thisType.cType(); const bool isImmutable = thisType.isImmutable(); const TypeNode &returnType = isImmutable ? thisType : *theVoidType; const CtType &returnTypeName = returnType.cType(); extern bool line_directives; if ((debugger_level > 0) && (line_directives)) { const SourcePosn pos = position(); if (!pos.unknown()) { os << "#line " << pos.posnToLineNumber() << " \"" << *pos.file << "\"\n"; } } //os << "\n/* --- Constructor " << decl()->fullName() << "() --- */\n\n"; os << "\n/* --- " << getMethodFullName(this) << " --- */\n\n"; // DOB: used by runtime/native-utils.h to output location on exceptions os << "#undef __TI_CURRENT_FUNCTION__\n"; os << "#define __TI_CURRENT_FUNCTION__ \"" << getMethodShortName(this) << "\"\n"; os << returnTypeName << ' ' << decl()->cMethodNameStatic() << '('; bool first = true; if (!isImmutable) { os << thisTypeName << ' ' << MANGLE_THIS_VAR(<<); first = false; } foriter (param, params()->allChildren(), TreeNode::ChildIter) { if (!first) os << ","; os << (*param)->dtype()->cType() << " " << MANGLE_STACK_VAR(<<, *(*param)->simpName()->ident()); first = false; } os << ") { \n"; os << "TI_BEGIN_FUNCTION\n"; if (mdecl.isDead()) { os << " fprintf(stderr,\"ERROR: Called a method removed by DME: " << getMethodShortName(this) << "\\n\"); fflush(stderr);" << endl << " abort();" << endl; } else { CodeContext context( os ); if (isImmutable) { // declare me context.declare( MANGLE_THIS_VAR(+), thisType.cType() ); // (clearing and initialization is handled by field initializer in bottom level constructor) } if (constructorCall() != TreeNode::omitted) { context << "\n/* Call chained constructors and perform instance field init */\n\n"; constructorCall()->codeGen(context); context << endCline; } context << "\n/* Method body */\n\n"; body()->codeGen(context); #if 0 // now handled in rewriting if (isImmutable) context << "return " << MANGLE_THIS_VAR(<<) << ";" << endCline; #endif reset_MIVE(); } os << "\n}\n"; // free method bodies after codegen freeMethodBody(this); }