// $Archive:: /Ti/inline/inline-trans.cc $ // $Date: Fri, 18 Jun 2004 21:50:34 -0700 $ // $Revision: 1.3.1.11.1.1.1.15.1.1 $ // Description: Sturctural Inliner - performs inlining transformation on a call site // Copyright 2000, Dan Bonachea #include "AST.h" #include "compiler.h" #include "code-util.h" #include "llist.h" // cons #include "cfg.h" #include "UniqueId.h" #include "inline.h" #include "lower.h" #include "clone.h" static UniqueId tempGenerator( "inl" ); // a program-wide temp name that gets renormalized at code-gen time //------------------------------------------------------------------------------------ static ObjectNode *MakeTemporary(TypeNode* tempType, TreeNode** newdecl, TreeNode** newassn, TreeNode *initexpr, SourcePosn pos) { // if using an initializer, set newassn and initexpr to non-NULL (initexpr should be a fresh subtree) // returns an ObjectNode that should be cloned to be used const string *str = intern(tempGenerator.next()); TreeNode *name = new NameNode (TreeNode::omitted, str, (Decl *)NULL, pos); // decl NULL for now TypeNode *tempDeclType = tempType->addModifiers(Common::CompilerGenerated); TreeNode *varDecl = new VarDeclNode (false, tempDeclType, name, TreeNode::omitted, pos); Decl *d = new LocalVarDecl(str, tempDeclType, varDecl, true); name->decl(d); *newdecl = varDecl; ObjectNode *varNode = new ObjectNode(new NameNode(TreeNode::omitted, str, d, pos), pos); varNode->type(tempType); if (newassn) { assert(initexpr != NULL); *newassn = new ExpressionStmtNode(new AssignNode(varNode, initexpr, pos), pos); } return varNode; } //------------------------------------------------------------------------------------ void ensureLowered(TreeNode *method) { // make sure a method has been lowered, do it now if necessary assert(isMethodDeclNode(method) || isConstructorDeclNode(method)); // make sure the callee has been completely processed by static semantics TreeNode *cun = method; while (!isCompileUnitNode(cun)) cun = cun->parent(); ((CompileUnitNode *)cun)->lazyStaticSemantics(); // this may do some lowering, etc. } //------------------------------------------------------------------------------------ TreeNode *findLastStmt(TreeNode *t) { // return the last "real" StatementNode in this BlockNode (or NULL for none found) if (isOmittedNode(t) || isEmptyStmtNode(t) || isVarDeclNode(t)) return NULL; if (isLabeledStmtNode(t)) return findLastStmt(t->stmt()); if (isBlockNode(t)) { for (int i=t->stmts()->arity()-1; i >= 0; i--) { TreeNode *n = findLastStmt(t->stmts()->child(i)); if (n) return n; } return NULL; // empty block } return t; } //------------------------------------------------------------------------------------ typedef struct { // info used during recursive descents that perform inlining transformations TreeNode *ExitLabel; // place to jump on return TreeNode *ReturnDest; // destination for a method assign call int ReturnCount; // number of return statements replaced TreeNode *LastStmt; // the last statement in the working body - no jump needed to exit from here TreeNode *ThisPointer; // local variable that is acting as this ptr for this non-static method body TreeListNode *formalParams; // list of the declared formals for method being inlined llist *formalDecls; // list of the local decls replacing formals for method being inlined } InlineActionData; static InlineActionData _ii = { NULL, NULL, 0, NULL, NULL, NULL, NULL }; static InlineActionData* ii = &_ii; //------------------------------------------------------------------------------------ static TreeNode *fixupFormalUses(TreeNode *t) { // change refs to formals to refs to alpha-converted locals // t is a NameNode if (t->decl()->category() == Decl::Formal) { // this is a ref to a formal int i; for (i=0; i < ii->formalParams->arity(); i++) { // find correct param if (t->decl() == ii->formalParams->child(i)->simpName()->decl()) { TreeNode *name = (*(ii->formalDecls))[i]->simpName(); TreeNode *newname = name->deepClone(); delete t; // no longer needed return newname; } } // it might be a parameter from a catch clause - check that first TreeNode *declnode = t->decl()->source(); if (declnode->parent() && isCatchNode(declnode->parent())) return NULL; // param not found! cout << "ERR: Can't find param:" << endl; t->print(); cout << endl; cout << "Params: "; for (int x=0; x < ii->formalParams->arity(); x++) cout << " " << *ii->formalParams->child(x)->simpName()->decl()->name() << endl; cout << "context: " << endl; t->parent()->parent()->print(); fatal_error(""); } return NULL; } //------------------------------------------------------------------------------------ #if 0 static TreeNode *fixupConstructorReturn(TreeNode *t) { // change a return node in the callee code to an assign of this and jump assert(isReturnNode(t) && static_cast(t)->cleanups() == NULL); assert(ii->ReturnDest); // we currently only use this for call-assign sites SourcePosn returnpos = t->position(); llist *returnstmts = NULL; if (t != ii->LastStmt) { // jump required - returning from inside body somewhere returnstmts = cons((TreeNode *)new GotoNode(ii->ExitLabel, returnpos), returnstmts); } // we use a bogus thisNode below as a place-holder that gets replaced in the next pass returnstmts = cons((TreeNode *)new ExpressionStmtNode( new AssignNode(ii->ReturnDest->deepClone(), new ThisNode(TreeNode::omitted, NULL, Common::None), returnpos), returnpos), returnstmts); return new BlockNode(returnstmts, NULL, returnpos); } #endif //------------------------------------------------------------------------------------ static TreeNode *fixupReturn(TreeNode *t) { // change a return node in the callee code to an assign and jump assert(isReturnNode(t) && static_cast(t)->cleanups() == NULL); SourcePosn returnpos = t->position(); ii->ReturnCount++; if (!ii->ReturnDest) { // simple proc call if (t != ii->LastStmt) // jump required - returning from inside body somewhere return new GotoNode(ii->ExitLabel, returnpos); else return new EmptyStmtNode(returnpos); } else { // call-assign llist *returnstmts = NULL; if (t != ii->LastStmt) { // jump required - returning from inside body somewhere returnstmts = cons((TreeNode *)new GotoNode(ii->ExitLabel, returnpos), returnstmts); } returnstmts = cons((TreeNode *)new ExpressionStmtNode( new AssignNode(ii->ReturnDest->deepClone(), t->expr(), returnpos), returnpos), returnstmts); return new BlockNode(returnstmts, NULL, returnpos); } } //------------------------------------------------------------------------------------ static TreeNode *fixupThisPointer(TreeNode *t) { // change this pointer uses in the callee code to use the correct object return ii->ThisPointer->deepClone(); } //------------------------------------------------------------------------------------ TreeNode *inlineCallSite(TreeNode *callsite) { // take a method call site and return the inlined body of code that can be substituted in its place TreeNode* before = callsite; TreeNode* callnode = getSiteCallNode(callsite); assert(canCallStatically(callsite)); TreeNode* calleeDefn = getSiteCallee(callsite); // make sure the callee has been completely processed by static semantics ensureLowered(calleeDefn); bool simpleCall = isSimpleCallSite(callsite); assert(simpleCall || isCallAssignSite(callsite)); bool staticProc = !!(calleeDefn->decl()->modifiers() & Common::Static); assert(isProcedure(calleeDefn)); // ensure we don't inline static initializers (or anything else) bool constructorProc = !!(isConstructorDeclNode(calleeDefn)); bool immutableProc = calleeDefn->decl()->container()->asType()->isImmutable(); // grab important stuff TreeNode* returndest = (simpleCall ? NULL : callsite->child(0)->child(0)); TreeListNode *formalParams = calleeDefn->params(); TreeListNode *actualParams = (TreeListNode *)(callnode->child(1)); if (inlineDebug) { cout << "-------------------------" << endl; cout << "Inlining " << (simpleCall?"simple":"call-assign") << " " << (staticProc?"static":"non-static") << " " << (constructorProc?"constructor":"method") << " " << "call:"; callsite->pseudoprint(cout); cout << endl; cout << "to " << getMethodFullName(calleeDefn) << endl; #if 0 cout << "-------------------------" << endl; calleeDefn->print(); cout << "-------------------------" << endl; #endif cout << "Before: " << endl; before->print(); } else if (inlineStats) { cout << " inlining call: from " << getMethodShortName(enclosingMethod(callsite)) << " to " << getMethodShortName(calleeDefn) << " (" << callsite->position() << ")"<< endl; } llist *formalDecls = NULL; llist *formalAssigns = NULL; llist *otherDecls = NULL; llist *otherAssigns = NULL; SourcePosn callposition = callnode->position(); for (int i=formalParams->arity()-1; i >= 0; i--) { // reverse order so we cons in param order TreeNode *thisFormal = formalParams->child(i); TreeNode *thisActual = actualParams->child(i); // create a new name to represent the formal in the body // (must be renamed to prevent conflicts on initialization with actuals) TreeNode *newdecl; TreeNode *newassn; MakeTemporary(thisFormal->dtype(), &newdecl, &newassn, thisActual->deepClone(), callposition); // add decl to list - must be in same order as formalParams formalDecls = cons(newdecl, formalDecls); // add initializer to list formalAssigns = cons(newassn, formalAssigns); } // get a fresh copy of the body that we can tear apart // need to use a special cloning function to get the GotoNode's right (deepClone() is no good) TreeNode *newBody = deepCloneWithCrossTreeFixup(calleeDefn->body()); if (constructorProc) { // glue on the constructor call TreeNode *chainedCall = deepCloneWithCrossTreeFixup(calleeDefn->constructorCall()); newBody = new BlockNode(cons(chainedCall, cons(newBody)), NULL, callposition); } // fix up body: replace refs to formal with refs to local ii->formalParams = formalParams; ii->formalDecls = formalDecls; depthFirstSearch(newBody, isNameNode, fixupFormalUses); // fixup return statements // MUST come after formals fixup, because it may insert refs to caller's formals - no longer true? TreeNode *exitLabel; if (simpleCall || (constructorProc && immutableProc && isObjectNode(returndest))) { exitLabel = new LabeledStmtNode(TreeNode::omitted, new EmptyStmtNode(callposition), callposition); ii->ExitLabel = exitLabel; ii->ReturnDest = NULL; ii->LastStmt = findLastStmt(newBody); } else { // create a "fresh" variable to receive return value within inlined block // (to avoid conflicts between compiler temps in different files) TreeNode *newdecl; TreeNode *newobj = MakeTemporary(returndest->type(), &newdecl, NULL, NULL, callposition); // add decl to list otherDecls = cons(newdecl, otherDecls); // assign the return value (can't use MakeTemp assign because it's backwards) TreeNode *newassn = new ExpressionStmtNode( new AssignNode(returndest->deepClone(), newobj->deepClone(), callposition), callposition); exitLabel = new LabeledStmtNode(TreeNode::omitted, newassn, callposition); ii->ExitLabel = exitLabel; if (constructorProc && immutableProc) { assert(!isObjectNode(returndest)); // return assignment unnecessary, will be operating directly on this temp ii->ThisPointer = newobj; ii->ReturnDest = NULL; } else { ii->ReturnDest = newobj; } ii->LastStmt = findLastStmt(newBody); if (constructorProc) assert(immutableProc); // non-immutable constructors use a simple call } // turn the returns into assign-and-jump ii->ReturnCount = 0; depthFirstSearch(newBody, isReturnNode, fixupReturn); if (!ii->ReturnCount) exitLabel = exitLabel->stmt(); // label unnecessary // fixup this pointer uses // MUST come after return fixup because it may insert this refs (for constructors) // MUST come after formals fixup, because it may insert refs to caller's formals - no longer true? if (!staticProc) { TreeNode *thisObjectNode = callnode->method()->object(); MethodDecl *calleeDecl = static_cast(calleeDefn->decl()); assert(calleeDecl->category() & (Decl::Constructor | Decl::Method)); // optimization: often don't need to copy-in the this object for immutables // (only exception is when it's a field of an immutable itself) // for constructors, the caller isn't passing in any useful info anyhow if (immutableProc && constructorProc) { if (isObjectNode(returndest)) ii->ThisPointer = returndest; else { assert(isObjectNode(ii->ThisPointer)); } assert(ii->ThisPointer->type()->typeIdentNM(calleeDecl->thisType())); } // for regular methods, the callee is guaranteed to never modify the immutable else if (immutableProc && !constructorProc && isObjectNode(thisObjectNode)) { ii->ThisPointer = thisObjectNode; assert(ii->ThisPointer->type()->typeIdentNM(calleeDecl->thisType())); } else if (!immutableProc && thisObjectNode->type()->typeIdentNM(calleeDecl->thisType()) && (thisObjectNode->type()->isLocal() == calleeDecl->thisType()->isLocal())) { ii->ThisPointer = thisObjectNode; assert(isObjectNode(thisObjectNode) || isThisNode(thisObjectNode)); assert(calleeDecl->thisType()->isCastableFrom(thisObjectNode->type())); } else { // create a "fresh" variable to act as "this" pointer within inlined block // necessary to get the types to match correctly, // and prevent side-effects on the target object from affecting this method TreeNode *newdecl; TreeNode *newassn; TreeNode *thisExpr = thisObjectNode->deepClone(); TypeNode *newThisType = calleeDecl->thisType(); if (!newThisType->isCastableFrom(thisExpr->type())) { cerr << "method inliner failed while attempting to cast target object pointer with type:" << endl; thisExpr->type()->print(cerr, 0); cerr << endl; cerr << "to this calleeDecl->thisType() type:" << endl; newThisType->print(cerr,0); cerr << endl; abort(); } thisExpr = new CastNode(thisExpr, newThisType, callposition); TreeNode *newobj = MakeTemporary(newThisType, //thisObjectNode->name()->decl()->type(), &newdecl, &newassn, thisExpr, callposition); // add decl to list otherDecls = cons(newdecl, otherDecls); // add assn to list otherAssigns = cons(newassn, otherAssigns); ii->ThisPointer = newobj; } depthFirstSearch(newBody, isThisNode, fixupThisPointer); if (thisObjectNode->type()->isReference() && !constructorProc && // elide null-check on constructor calls !isThisNode(thisObjectNode)) { // and calls on this // preserve the null check TreeNode *nullCheck = new CheckNullNode(ii->ThisPointer->deepClone(), callposition); llist* bodyList = appendTreeList(newBody->stmts(), NULL); bodyList = cons(nullCheck, bodyList); newBody->stmts(new TreeListNode(bodyList, callposition)); } } // build replacement block (in reverse order) llist *stmtList = NULL; stmtList = cons(exitLabel, stmtList); stmtList = cons(newBody, stmtList); stmtList = extend(formalAssigns, stmtList); stmtList = extend(otherAssigns, stmtList); stmtList = extend(otherDecls, stmtList); stmtList = extend(formalDecls, stmtList); TreeNode *after = new BlockNode(stmtList, NULL, callposition); if (inlineDebug) { cout << "-------------------------" << endl; cout << "After: " << endl; after->pseudoprint(cout); cout << endl; cout << "-------------------------" << endl; after->print(); cout << "-------------------------" << endl; } return after; } //------------------------------------------------------------------------------------ void fixupMethodAfterInline(TreeNode *methoddecl) { // called after inlining operations have taken place on a method to fix up AST for subsequent phases methoddecl->fixParent(); methoddecl->collapseTrivialBlocks(); // take care of trivial blocks we may have inserted methoddecl->fixParent(); TreeNode *containingClassDecl = methoddecl; while (!isClassDeclNode(containingClassDecl)) containingClassDecl = containingClassDecl->parent(); containingClassDecl->resolveRequires(NULL); // make sure to suck in new headers needed by inlined code if (inlineDebug) { #if 1 checkAliasing(methoddecl); #else int check = methoddecl->fixParent(); if (check != 0) { cout << "*** Aliasing detected in AST after inline.." << check << " unfixable parents." << endl; } #endif } setupBblocksMethod(methoddecl); // fixup CFG if (methoddecl->body() != TreeNode::omitted && // we might still inline field inits into default constructors !methoddecl->getBblockRoot()) { cout << "*** Failed to verify can get getBblockRoot()" << endl; methoddecl->pseudoprint(cout); methoddecl->print(); fatal_error(""); } methoddecl->checkIF(true); } //------------------------------------------------------------------------------------