void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal) { Expr* returnPrim = fn->body->body.tail; SET_LINENO(returnPrim); CallExpr* returnCall = toCallExpr(returnPrim); Expr* returnValue = returnCall->get(1)->remove(); CallExpr* moveExpr = new CallExpr(PRIM_ASSIGN, formal, returnValue); Expr* expr = returnPrim; // Walk backwards while the previous element is an autoDestroy call while (expr->prev != NULL) { bool stop = true; if (CallExpr* call = toCallExpr(expr->prev)) if (FnSymbol* calledFn = call->isResolved()) if (calledFn->hasFlag(FLAG_AUTO_DESTROY_FN)) stop = false; if (stop) break; expr = expr->prev; } Expr* returnOrFirstAutoDestroy = expr; // Add the move to return before the first autoDestroy // At this point we could also invoke some other function // if that turns out to be necessary. It might well be // necessary in order to return array slices by value. returnOrFirstAutoDestroy->insertBefore(moveExpr); }
/* * Attempts to replace iteration over simple anonymous ranges with calls to * direct iterators that take low, high and stride as arguments. This is to * avoid the cost of constructing ranges, and if the stride is known at compile * time, provide a more optimized iterator that uses "<, <=, >, or >=" as the * relational operator. * * This is only meant to replace anonymous range iteration for "simple" bounded * ranges. Simple means it's a range of the form "low..high" or "low..high by * stride". Anything more complex is ignored with the thinking that this should * optimize the most common range iterators, but it could be expanded to handle * more cases. * * An alternative is to update scalar replacement of aggregates to work on * ranges, which should be able to achieve similar results as this optimization * while handling all ranges, including non-anonymous ranges. * * Will optimize things like: * - "for i in 1..10" * - "for i in 1..10+1" * - "var lo=1, hi=10;for i in lo..hi" * - "for i in 1..10 by 2" * - "for (i, j) in zip(1..10 by 2, 1..10 by -2)" * - "for (i, j) in zip(A, 1..10 by 2)" // will optimize range iter still * - "coforall i in 1..10 by 2" // works for coforalls as well * * Will not optimize ranges like: * - "for in in (1..)" // doesn't handle unbounded ranges * - "for i in 1..10 by 2 by 2" // doesn't handle more than one by operator * - "for i in 1..10 align 2" // doesn't handle align operator * - "for i in 1..#10" // doesn't handle # operator * - "var r = 1..10"; for i in r" // not an anonymous range * - "forall i in 1..10" // does not get applied to foralls * * Note that this function is pretty fragile because it relies on names of * functions/iterators as well as the arguments and order of those * functions/iterators but there's not really a way around it this early in * compilation. If the iterator can't be replaced, it is left unchanged. */ static void tryToReplaceWithDirectRangeIterator(Expr* iteratorExpr) { CallExpr* range = NULL; Expr* stride = NULL; if (CallExpr* call = toCallExpr(iteratorExpr)) { // grab the stride if we have a strided range if (call->isNamed("chpl_by")) { range = toCallExpr(call->get(1)->copy()); stride = toExpr(call->get(2)->copy()); } // assume the call is the range (checked below) and set default stride else { range = call; stride = new SymExpr(new_IntSymbol(1)); } // see if we're looking at a builder for a bounded range. the builder is // iteratable since range has these() iterators if (range && range->isNamed("chpl_build_bounded_range")) { // replace the range construction with a direct range iterator Expr* low = range->get(1)->copy(); Expr* high = range->get(2)->copy(); iteratorExpr->replace(new CallExpr("chpl_direct_range_iter", low, high, stride)); } } }
forv_Vec(DefExpr, def, gDefExprs) { if (toVarSymbol(def->sym)) { // The test for FLAG_TEMP allows compiler-generated (temporary) variables // to be declared without an explicit type or initializer expression. if ((!def->init || def->init->isNoInitExpr()) && !def->exprType && !def->sym->hasFlag(FLAG_TEMP)) if (isBlockStmt(def->parentExpr) && !isArgSymbol(def->parentSymbol)) if (def->parentExpr != rootModule->block && def->parentExpr != stringLiteralModule->block) if (!def->sym->hasFlag(FLAG_INDEX_VAR)) USR_FATAL_CONT(def->sym, "Variable '%s' is not initialized or has no type", def->sym->name); } // // This test checks to see if query domains (e.g., '[?D]') are // used in places other than formal argument type specifiers. // if (!isFnSymbol(def->parentSymbol)) { if (CallExpr* type = toCallExpr(def->exprType)) { if (type->isNamed("chpl__buildArrayRuntimeType")) { if (CallExpr* domainExpr = toCallExpr(type->get(1))) { DefExpr* queryDomain = toDefExpr(domainExpr->get(1)); if (queryDomain) { USR_FATAL_CONT(queryDomain, "Domain query expressions may currently only be used in formal argument types"); } } } } } checkPrivateDecls(def); }
// Determines if the expression is in the header of a Loop expression // // After normalization, the conditional test for a WhileStmt is expressed as // a SymExpr inside a primitive CallExpr. // // The init, test, incr clauses of a C-For loop are expressed as BlockStmts // that contain the relevant expressions. This function only returns true // for the expressions inside the BlockStmt and not the BlockStmt itself // // TODO should this be updated to only look for exprs in the test segment that // are conditional primitives? // static bool isInLoopHeader(Expr* expr) { bool retval = false; if (expr->parentExpr == NULL) { retval = false; } else if (CallExpr* call = toCallExpr(expr->parentExpr)) { if (call->isPrimitive(PRIM_BLOCK_WHILEDO_LOOP) || call->isPrimitive(PRIM_BLOCK_DOWHILE_LOOP)) { retval = true; } } else if (expr->parentExpr->parentExpr == NULL) { retval = false; } else if (CallExpr* call = toCallExpr(expr->parentExpr->parentExpr)) { if (call->isPrimitive(PRIM_BLOCK_C_FOR_LOOP)) retval = true; } else { retval = false; } return retval; }
void BlockStmt::replaceChild(Expr* old_ast, Expr* new_ast) { if (old_ast == blockInfo) blockInfo = toCallExpr(new_ast); else if (old_ast == modUses) modUses = toCallExpr(new_ast); else INT_FATAL(this, "Unexpected case in BlockStmt::replaceChild"); }
// Insert pairs into available copies map // Also, record references when they are created. static void extractReferences(Expr* expr, RefMap& refs) { // We're only interested in call expressions. if (CallExpr* call = toCallExpr(expr)) { // Only the move primitive creates an available pair. if (call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN)) { SymExpr* lhe = toSymExpr(call->get(1)); // Left-Hand Expression Symbol* lhs = lhe->var; // Left-Hand Symbol if (SymExpr* rhe = toSymExpr(call->get(2))) // Right-Hand Expression { Symbol* rhs = rhe->var; // Right-Hand Symbol if (lhs->type->symbol->hasFlag(FLAG_REF) && rhs->type->symbol->hasFlag(FLAG_REF)) { // This is a ref <- ref assignment. // Which means that the lhs is now also a reference to whatever the // rhs refers to. RefMap::iterator refDef = refs.find(rhs); // Refs can come from outside the function (e.g. through arguments), // so are not necessarily defined within the function. if (refDef != refs.end()) { Symbol* val = refDef->second; #if DEBUG_CP if (debug > 0) printf("Creating ref (%s[%d], %s[%d])\n", lhs->name, lhs->id, val->name, val->id); #endif refs.insert(RefMapElem(lhs, val)); } } } if (CallExpr* rhc = toCallExpr(call->get(2))) { if (rhc->isPrimitive(PRIM_ADDR_OF)) { SymExpr* rhe = toSymExpr(rhc->get(1)); // Create the pair lhs <- &rhs. Symbol* lhs = lhe->var; Symbol* rhs = rhe->var; #if DEBUG_CP if (debug > 0) printf("Creating ref (%s[%d], %s[%d])\n", lhs->name, lhs->id, rhs->name, rhs->id); #endif refs.insert(RefMapElem(lhs, rhs)); } } } } }
void collectMyCallExprs(BaseAST* ast, Vec<CallExpr*>& callExprs, FnSymbol* parent_fn) { AST_CHILDREN_CALL(ast, collectMyCallExprs, callExprs, parent_fn); if (CallExpr* callExpr = toCallExpr(ast)) if (callExpr->parentSymbol == parent_fn) callExprs.add(callExpr); }
static void view_ast(BaseAST* ast, bool number = false, int mark = -1, int indent = 0) { if (!ast) return; if (Expr* expr = toExpr(ast)) { printf("\n"); for (int i = 0; i < indent; i++) printf(" "); printf("("); if (ast->id == mark) printf("***"); if (number) printf("%d ", ast->id); printf("%s", expr->astTagAsString()); if (isBlockStmt(expr)) if (FnSymbol* fn = toFnSymbol(expr->parentSymbol)) if (expr == fn->where) printf(" where"); if (GotoStmt *gs= toGotoStmt(ast)) { printf( " "); view_ast(gs->label, number, mark, indent+1); } if (CallExpr* call = toCallExpr(expr)) if (call->primitive) printf(" %s", call->primitive->name); if (NamedExpr* named = toNamedExpr(expr)) printf(" \"%s\"", named->name); if (toDefExpr(expr)) printf(" "); int64_t i; const char *str; if (get_int(expr, &i)) { printf(" %" PRId64, i); } else if (get_string(expr, &str)) { printf(" \"%s\"", str); } if (SymExpr* sym = toSymExpr(expr)) { printf(" "); view_sym(sym->var, number, mark); } else if (UnresolvedSymExpr* sym = toUnresolvedSymExpr(expr)) { printf(" '%s'", sym->unresolved); } } if (Symbol* sym = toSymbol(ast)) { view_sym(sym, number, mark); } AST_CHILDREN_CALL(ast, view_ast, number, mark, indent+2); if (toExpr(ast)) printf(")"); }
// // Returns true if 'ref' is only used in the following cases: // // 1) Used in 'defCall', usually a PRIM_ADDR_OF or PRIM_SET_REFERENCE // // 2) Passed to an initializer in the 'this' slot. // // 3) Initialized from a function call, as represented by return-by-ref. // // An initializer can thwart detection of a 'const' thing because it takes the // "this" argument by ref. Normally such a pattern would cause us to assume // the variable was not const, but in this case we know it is a single def. // // defCall: The CallExpr where 'ref' is set from a PRIM_ADDR_OF or // PRIM_SET_REFERENCE. This call will be ignored while considering uses of // the 'ref' Symbol. // static bool onlyUsedForInitOrRetarg(Symbol* ref, CallExpr* defCall) { bool seenInitOrRetarg = false; INT_ASSERT(ref->isRef()); INT_ASSERT(defCall != NULL); for_SymbolSymExprs(use, ref) { if (use->parentExpr == defCall) continue; CallExpr* call = toCallExpr(use->parentExpr); if (FnSymbol* fn = call->resolvedFunction()) { ArgSymbol* form = actual_to_formal(use); if (passedToInitOrRetarg(use, form, fn)) { INT_ASSERT(!seenInitOrRetarg); // init/retarg happens just once seenInitOrRetarg = true; } else { return false; // a use other than what we are looking for } } } return true; }
static void removeVoidReturn(BlockStmt* cloneBody) { CallExpr* retexpr = toCallExpr(cloneBody->body.tail); INT_ASSERT(retexpr && retexpr->isPrimitive(PRIM_RETURN)); INT_ASSERT(toSymExpr(retexpr->get(1))->symbol() == gVoid); retexpr->remove(); }
void collectMyCallExprsSTL(BaseAST* ast, std::vector<CallExpr*>& callExprs, FnSymbol* parent_fn) { AST_CHILDREN_CALL(ast, collectMyCallExprsSTL, callExprs, parent_fn); if (CallExpr* callExpr = toCallExpr(ast)) if (callExpr->parentSymbol == parent_fn) callExprs.push_back(callExpr); }
// // Assumes 'parent' is a PRIM_MOVE or PRIM_ASSIGN // // Returns false if the LHS of the move/assign indicates that the rhs cannot // be a const-ref. For example, if we have a case like this: // // (move A, (set-reference B)) // // where 'A' and 'B' are references, B cannot be a const-ref if A is not a // const-ref. // // In the case of a dereference, (move A, (deref B)), this function will return // true because we're simply reading B. // static bool canRHSBeConstRef(CallExpr* parent, SymExpr* use) { INT_ASSERT(isMoveOrAssign(parent)); SymExpr* LHS = toSymExpr(parent->get(1)); CallExpr* rhs = toCallExpr(parent->get(2)); INT_ASSERT(rhs); switch (rhs->primitive->tag) { case PRIM_GET_MEMBER: case PRIM_GET_MEMBER_VALUE: case PRIM_GET_SVEC_MEMBER: case PRIM_GET_SVEC_MEMBER_VALUE: case PRIM_GET_REAL: case PRIM_GET_IMAG: case PRIM_ADDR_OF: case PRIM_SET_REFERENCE: { // If LHS is a reference and is not a const-ref, the reference in 'rhs' // should not be considered a const-ref either. // // For the get-member primitives, I intend this to be a safe approach // until we know what const-ref means for fields. Basically, if any field // might be modified I do not consider the base object to be const-ref. // // Note that the get-*-value primitives may return a reference if the // field is a reference. if (LHS->isRef()) { if (!inferConstRef(LHS->symbol())) { return false; } } } default: break; } return isSafeRefPrimitive(use); }
// // inlines the function called by 'call' at that call site // static void inlineCall(FnSymbol* fn, CallExpr* call, Vec<FnSymbol*>& canRemoveRefTempSet) { INT_ASSERT(call->isResolved() == fn); SET_LINENO(call); Expr* stmt = call->getStmtExpr(); // // calculate a map from actual symbols to formal symbols // SymbolMap map; for_formals_actuals(formal, actual, call) { SymExpr* se = toSymExpr(actual); INT_ASSERT(se); if ((formal->intent & INTENT_REF) && canRemoveRefTempSet.set_in(fn)) { if (se->var->hasFlag(FLAG_REF_TEMP)) { if (CallExpr* move = findRefTempInit(se)) { SymExpr* origSym = NULL; if (CallExpr* addrOf = toCallExpr(move->get(2))) { INT_ASSERT(addrOf->isPrimitive(PRIM_ADDR_OF)); origSym = toSymExpr(addrOf->get(1)); } else { origSym = toSymExpr(move->get(2)); } INT_ASSERT(origSym); map.put(formal, origSym->var); se->var->defPoint->remove(); move->remove(); continue; } } } map.put(formal, se->var); }
void deadVariableElimination(FnSymbol* fn) { Vec<Symbol*> symSet; Vec<SymExpr*> symExprs; collectSymbolSetSymExprVec(fn, symSet, symExprs); Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(symSet, symExprs, defMap, useMap); forv_Vec(Symbol, sym, symSet) { // We're interested only in VarSymbols. if (!isVarSymbol(sym)) continue; // A method must have a _this symbol, even if it is not used. if (sym == fn->_this) continue; if (isDeadVariable(sym, defMap, useMap)) { for_defs(se, defMap, sym) { CallExpr* call = toCallExpr(se->parentExpr); INT_ASSERT(call && (call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN))); Expr* rhs = call->get(2)->remove(); if (!isSymExpr(rhs)) call->replace(rhs); else call->remove(); } sym->defPoint->remove(); } }
forv_Vec(FnSymbol, fn, gFnSymbols) { if (VarSymbol* ret = toVarSymbol(fn->getReturnSymbol())) { // The return value of an initCopy function should not be autodestroyed. // Normally, the return value of a function is autoCopied, but since // autoCopy is typically defined in terms of initCopy, this would lead to // infinite recursion. That is, the return value of initCopy must be // handled specially. if (fn->hasFlag(FLAG_INIT_COPY_FN)) ret->removeFlag(FLAG_INSERT_AUTO_DESTROY); // This is just a workaround for memory management being handled specially // for internally reference-counted types. (sandboxing) TypeSymbol* ts = ret->type->symbol; if (ts->hasFlag(FLAG_ARRAY) || ts->hasFlag(FLAG_DOMAIN)) ret->removeFlag(FLAG_INSERT_AUTO_DESTROY); // Do we need to add other record-wrapped types here? Testing will tell. // NOTE 1: When the value of a record field is established in a default // constructor, it is initialized using a MOVE. That means that ownership // of that value is shared between the formal_tmp and the record field. // If the autodestroy flag is left on that formal temp, then it will be // destroyed which -- for ref-counted types -- can result in a dangling // reference. So here, we look for that case and remove it. if (fn->hasFlag(FLAG_DEFAULT_CONSTRUCTOR)) { Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(fn, defMap, useMap); std::vector<DefExpr*> defs; collectDefExprs(fn, defs); for_vector(DefExpr, def, defs) { if (VarSymbol* var = toVarSymbol(def->sym)) { // Examine only those bearing the explicit autodestroy flag. if (! var->hasFlag(FLAG_INSERT_AUTO_DESTROY)) continue; // Look for a use in a PRIM_SET_MEMBER where the field is a record // type, and remove the flag. // (We don't actually check that var is of record type, because // chpl__autoDestroy() does nothing when applied to all other types. for_uses(se, useMap, var) { CallExpr* call = toCallExpr(se->parentExpr); if (call->isPrimitive(PRIM_SET_MEMBER) && toSymExpr(call->get(3))->var == var) var->removeFlag(FLAG_INSERT_AUTO_DESTROY); } } } freeDefUseMaps(defMap, useMap); }
// Remove the return statement and the def of 'ret'. Return 'ret'. // See also removeRetSymbolAndUses(). static Symbol* removeParIterReturn(BlockStmt* cloneBody, Symbol* retsym) { CallExpr* retexpr = toCallExpr(cloneBody->body.tail); INT_ASSERT(retexpr && retexpr->isPrimitive(PRIM_RETURN)); if (retsym == NULL) { retsym = toSymExpr(retexpr->get(1))->symbol(); INT_ASSERT(retsym->type->symbol->hasFlag(FLAG_ITERATOR_RECORD)); } else { CallExpr* move = toCallExpr(retsym->getSingleDef()->getStmtExpr()); INT_ASSERT(move->isPrimitive(PRIM_MOVE) || move->isPrimitive(PRIM_ASSIGN)); retsym = toSymExpr(move->get(2))->symbol(); move->remove(); } retexpr->remove(); return retsym; }
// // Find the _waitEndCount and _endCountFree calls that comes after 'fromHere'. // Since these two calls come together, it is prettier to insert // our stuff after the latter. // static Expr* findTailInsertionPoint(Expr* fromHere, bool isCoforall) { Expr* curr = fromHere; if (isCoforall) curr = curr->parentExpr; CallExpr* result = NULL; while ((curr = curr->next)) { if (CallExpr* call = toCallExpr(curr)) if (call->isNamed("_waitEndCount")) { result = call; break; } } INT_ASSERT(result); CallExpr* freeEC = toCallExpr(result->next); // Currently these two calls come together. INT_ASSERT(freeEC && freeEC->isNamed("_endCountFree")); return freeEC; }
static void optimizeAnonymousRangeIteration(Expr* iteratorExpr, bool zippered) { if (!zippered) tryToReplaceWithDirectRangeIterator(iteratorExpr); // for zippered iterators, try to replace each iterator of the tuple else if (CallExpr* call = toCallExpr(iteratorExpr)) if (call->isNamed("_build_tuple")) for_actuals(actual, call) tryToReplaceWithDirectRangeIterator(actual); }
void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal) { Expr* returnPrim = fn->body->body.tail; SET_LINENO(returnPrim); CallExpr* returnCall = toCallExpr(returnPrim); Expr* returnValue = returnCall->get(1)->remove(); CallExpr* moveExpr = new CallExpr(PRIM_MOVE, formal, returnValue); returnPrim->insertBefore(moveExpr); }
// // BHARSH TODO: Fix up PRIM_ADDR_OF's return type function to correctly // handle this // // The 'returnInfoRef' function for PRIM_ADDR_OF is currently set up to // always return with the Qualifier QUAL_REF. This function is intended // as a workaround to handle the case where we have a wide-ref in a // PRIM_ADDR_OF: // // (move "wide-ref dest" (addr-of "wide-ref src")) // // Otherwise, 'returnInfoRef' would hide the fact that the actual is a wide-ref // and it would appear as though we had a local reference. // // Note that this case is actually just a copy of a pointer's address, and // will not generate an addrof in the generated code. // static bool isAddrOfWideRefVar(Expr* e) { // ADDR_OF of a wide ref variable is a wide ref (not a ref). if (CallExpr* call = toCallExpr(e)) if (call->isPrimitive(PRIM_ADDR_OF)) if (SymExpr* se = toSymExpr(call->get(1))) if (se->symbol()->qualType().isWideRef()) return true; return false; }
void localizeGlobals() { if (fNoGlobalConstOpt) return; forv_Vec(FnSymbol, fn, gFnSymbols) { Map<Symbol*,VarSymbol*> globals; std::vector<BaseAST*> asts; collect_asts(fn->body, asts); for_vector(BaseAST, ast, asts) { if (SymExpr* se = toSymExpr(ast)) { Symbol* var = se->symbol(); ModuleSymbol* parentmod = toModuleSymbol(var->defPoint->parentSymbol); CallExpr* parentExpr = toCallExpr(se->parentExpr); bool inAddrOf = parentExpr && parentExpr->isPrimitive(PRIM_ADDR_OF); bool lhsOfMove = parentExpr && isMoveOrAssign(parentExpr) && (parentExpr->get(1) == se); // Is var a global constant? // Don't replace the var name in its init function since that's // where we're setting the value. Similarly, dont replace them // inside initStringLiterals // If the parentSymbol is the rootModule, the var is 'void,' // 'false,' '0,' ... // Also don't replace it when it's in an addr of primitive. if (parentmod && fn != parentmod->initFn && fn != initStringLiterals && !inAddrOf && !lhsOfMove && var->hasFlag(FLAG_CONST) && var->defPoint->parentSymbol != rootModule) { VarSymbol* local_global = globals.get(var); SET_LINENO(se); // Set the se line number for output if (!local_global) { const char * newname = astr("local_", var->cname); local_global = newTemp(newname, var->type); fn->insertAtHead(new CallExpr(PRIM_MOVE, local_global, var)); fn->insertAtHead(new DefExpr(local_global)); // Copy string immediates to localized strings so that // we can show the string value in comments next to uses. if (!llvmCodegen) if (VarSymbol* localVarSym = toVarSymbol(var)) if (Immediate* immediate = localVarSym->immediate) if (immediate->const_kind == CONST_KIND_STRING) local_global->immediate = new Immediate(immediate->v_string, immediate->string_kind); globals.put(var, local_global); } se->replace(new SymExpr(toSymbol(local_global))); } } } }
static bool removeIdentityDefs(Symbol* sym) { bool change = false; for_defs(def, defMap, sym) { CallExpr* move = toCallExpr(def->parentExpr); if (move && isMoveOrAssign(move)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->symbol() == rhs->symbol()) { move->remove(); change = true; } } }
static bool removeIdentityDefs(Symbol* sym) { bool change = false; for_defs(def, defMap, sym) { CallExpr* move = toCallExpr(def->parentExpr); if (move && move->isPrimitive(PRIM_MOVE)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->var == rhs->var) { move->remove(); change = true; } } }
/* * Returns true if `e` has no side effects. Checked side effects are: * - Read/write to a global * - Is/contains essential primitive * - If it's a call to functions with ref arguments * - If the LHS of a PRIM_MOVE appears in the exprToMove * * For now, this is a very conservative analysis. A more precise analysis * could distinguish between reads and writes to memory and to take into * account alias analysis. */ bool SafeExprAnalysis::exprHasNoSideEffects(Expr* e, Expr* exprToMove) { if(safeExprCache.count(e) > 0) { return safeExprCache[e]; } if(CallExpr* ce = toCallExpr(e)) { if(!ce->isPrimitive()) { FnSymbol* fnSym = ce->theFnSymbol(); const bool cachedSafeFn = safeFnCache.count(fnSym); if(!cachedSafeFn) { const bool retval = fnHasNoSideEffects(fnSym); safeFnCache[fnSym] = retval; return retval; } return safeFnCache[fnSym]; } else { //primitive if(! isSafePrimitive(ce)){ safeExprCache[e] = false; return false; } else if (exprToMove != NULL) { // // Exposed by AST pattern like this: // |---|------- `exprToMove` // (move T (+ A B)) // (move A B) -------- `ce` // (move B T) // // Without this check could turn into: // // (move A B) // (move B (+ A B)) // // Which is incorrect. // if (ce->isPrimitive(PRIM_MOVE)) { INT_ASSERT(isSymExpr(ce->get(1))); std::vector<SymExpr*> syms; collectSymExprs(exprToMove, syms); for_vector(SymExpr, s, syms) { if (s->symbol() == toSymExpr(ce->get(1))->symbol()) { safeExprCache[e] = false; return false; } } } } } }
// Does 'fn' return the result of a call to 'se' ? static bool isReturnedValue(FnSymbol* fn, SymExpr* se) { Symbol* currSym = fn->getReturnSymbol(); // Traverse the chain of moves. Like in stripReturnScaffolding(). while (true) { if (SymExpr* defSE = currSym->getSingleDef()) if (CallExpr* defMove = toCallExpr(defSE->parentExpr)) if (defMove->isPrimitive(PRIM_MOVE)) { Expr* defSrc = defMove->get(2); if (CallExpr* srcCall = toCallExpr(defSrc)) if (srcCall->baseExpr == se) return true; // found it if (SymExpr* srcSE = toSymExpr(defSrc)) { currSym = srcSE->symbol(); continue; // continue traversing the chain of moves } } // The chain of moves is over. Return false. break; } return false; }
// // Do a breadth first search starting from functions generated for local blocks // for all function calls in each level of the search, if they directly cause // communication, add a local temp that isn't wide. If it is a resolved call, // meaning that it isn't a primitive or external function, clone it and add it // to the queue of functions to handle at the next iteration of the BFS. // static void handleLocalBlocks() { Map<FnSymbol*,FnSymbol*> cache; // cache of localized functions Vec<BlockStmt*> queue; // queue of blocks to localize forv_Vec(BlockStmt, block, gBlockStmts) { if (block->parentSymbol) { // NOAKES 2014/11/25 Transitional. Avoid calling blockInfoGet() if (block->isLoopStmt() == true) { } else if (block->blockInfoGet()) { if (block->blockInfoGet()->isPrimitive(PRIM_BLOCK_LOCAL)) { queue.add(block); } } } } forv_Vec(BlockStmt, block, queue) { std::vector<CallExpr*> calls; collectCallExprs(block, calls); for_vector(CallExpr, call, calls) { localizeCall(call); if (FnSymbol* fn = call->isResolved()) { SET_LINENO(fn); if (FnSymbol* alreadyLocal = cache.get(fn)) { call->baseExpr->replace(new SymExpr(alreadyLocal)); } else { if (!fn->hasFlag(FLAG_EXTERN)) { FnSymbol* local = fn->copy(); local->addFlag(FLAG_LOCAL_FN); local->name = astr("_local_", fn->name); local->cname = astr("_local_", fn->cname); fn->defPoint->insertBefore(new DefExpr(local)); call->baseExpr->replace(new SymExpr(local)); queue.add(local->body); cache.put(fn, local); cache.put(local, local); // to handle recursion if (local->retType->symbol->hasFlag(FLAG_WIDE_REF)) { CallExpr* ret = toCallExpr(local->body->body.tail); INT_ASSERT(ret && ret->isPrimitive(PRIM_RETURN)); // Capture the return expression in a local temp. insertLocalTemp(ret->get(1)); local->retType = ret->get(1)->typeInfo(); } } } } }
// If there is exactly one definition of var by something of reference type, // then return the call that defines it. // Otherwise, return NULL. static CallExpr* findRefDef(Map<Symbol*,Vec<SymExpr*>*>& defMap, Symbol* var) { CallExpr* ret = NULL; for_defs(def, defMap, var) { if (CallExpr* call = toCallExpr(def->parentExpr)) { if (call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN)) if (call->get(2)->isRef()) { if (ret) return NULL; else ret = call; } } } return ret; }
// // Returns true if 'use' appears in a non-side-effecting primitive. // static bool isSafeRefPrimitive(SymExpr* use) { CallExpr* call = toCallExpr(use->parentExpr); INT_ASSERT(call); switch (call->primitive->tag) { case PRIM_ADDR_OF: case PRIM_SET_REFERENCE: case PRIM_DEREF: case PRIM_WIDE_GET_LOCALE: case PRIM_NOOP: case PRIM_UNARY_MINUS: case PRIM_UNARY_PLUS: case PRIM_UNARY_NOT: case PRIM_UNARY_LNOT: case PRIM_ADD: case PRIM_SUBTRACT: case PRIM_MULT: case PRIM_DIV: case PRIM_MOD: case PRIM_LSH: case PRIM_RSH: case PRIM_EQUAL: case PRIM_NOTEQUAL: case PRIM_LESSOREQUAL: case PRIM_GREATEROREQUAL: case PRIM_LESS: case PRIM_GREATER: case PRIM_AND: case PRIM_OR: case PRIM_XOR: case PRIM_POW: case PRIM_MIN: case PRIM_MAX: case PRIM_CHECK_NIL: case PRIM_LOCAL_CHECK: case PRIM_PTR_EQUAL: case PRIM_PTR_NOTEQUAL: case PRIM_SIZEOF_BUNDLE: case PRIM_SIZEOF_DDATA_ELEMENT: case PRIM_WIDE_GET_NODE: return true; default: return false; } }
static bool symExprIsSetByUse(SymExpr* use) { if (CallExpr* call = toCallExpr(use->parentExpr)) { if (FnSymbol* fn = call->resolvedFunction()) { ArgSymbol* formal = actual_to_formal(use); if (formal->intent == INTENT_INOUT || formal->intent == INTENT_OUT) { // Shouldn't this be a Def, not a Use, then? INT_ASSERT(0); return true; } if (formal->type->symbol->hasFlag(FLAG_REF) && (fn->hasFlag(FLAG_ALLOW_REF) || formal->hasFlag(FLAG_WRAP_WRITTEN_FORMAL))) { // This case has to do with wrapper functions (promotion?) return true; } } else if (call->isPrimitive(PRIM_SET_MEMBER)) { // PRIM_SET_MEMBER to set the pointer inside of a reference // counts as "setter" // the below conditional would better be isRefType() if (!call->get(2)->typeInfo()->refType) { return true; } } else if (call->isPrimitive(PRIM_RETURN) || call->isPrimitive(PRIM_YIELD)) { FnSymbol* inFn = toFnSymbol(call->parentSymbol); // It is not necessary to use the 'ref' version // if the function result is returned by 'const ref'. if (inFn->retTag == RET_CONST_REF) return false; // MPF: it seems to cause problems to return false // here when inFn->retTag is RET_VALUE. // TODO: can we add //if (inFn->retTag == RET_VALUE) return false; return true; } } return false; }
void localizeGlobals() { if (fNoGlobalConstOpt) return; forv_Vec(FnSymbol, fn, gFnSymbols) { Map<Symbol*,VarSymbol*> globals; std::vector<BaseAST*> asts; collect_asts(fn->body, asts); for_vector(BaseAST, ast, asts) { if (SymExpr* se = toSymExpr(ast)) { Symbol* var = se->var; ModuleSymbol* parentmod = toModuleSymbol(var->defPoint->parentSymbol); CallExpr* parentExpr = toCallExpr(se->parentExpr); bool inAddrOf = parentExpr && parentExpr->isPrimitive(PRIM_ADDR_OF); // Is var a global constant? // Don't replace the var name in its init function since that's // where we're setting the value. Similarly, dont replace them // inside initStringLiterals // If the parentSymbol is the rootModule, the var is 'void,' // 'false,' '0,' ... // Also don't replace it when it's in an addr of primitive. if (parentmod && fn != parentmod->initFn && fn != initStringLiterals && !inAddrOf && var->hasFlag(FLAG_CONST) && var->defPoint->parentSymbol != rootModule) { VarSymbol* local_global = globals.get(var); SET_LINENO(se); // Set the se line number for output if (!local_global) { const char * newname = astr("local_", var->cname); local_global = newTemp(newname, var->type); fn->insertAtHead(new CallExpr(PRIM_MOVE, local_global, var)); fn->insertAtHead(new DefExpr(local_global)); globals.put(var, local_global); } se->replace(new SymExpr(toSymbol(local_global))); } } } }