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(); } }
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(); }
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; }
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 && move->isPrimitive(PRIM_MOVE)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->var == rhs->var) { move->remove(); change = true; } } }
// // 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(); } } } } }
// // Consider a function that takes a formal of type Record by const ref // and that returns that value from the function. The compiler inserts // a PRIM_MOVE operation. // // This work-around inserts an autoCopy to compensate // void ReturnByRef::updateAssignmentsFromRefArgToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* lhs = toSymExpr(move->get(1)); SymExpr* rhs = toSymExpr(move->get(2)); if (lhs != NULL && rhs != NULL) { VarSymbol* symLhs = toVarSymbol(lhs->symbol()); ArgSymbol* symRhs = toArgSymbol(rhs->symbol()); if (symLhs != NULL && symRhs != NULL) { if (isUserDefinedRecord(symLhs->type) == true && symRhs->type == symLhs->type) { if (symLhs->hasFlag(FLAG_ARG_THIS) == false && (symRhs->intent == INTENT_REF || symRhs->intent == INTENT_CONST_REF)) { SET_LINENO(move); CallExpr* autoCopy = NULL; rhs->remove(); autoCopy = new CallExpr(autoCopyMap.get(symRhs->type), rhs); move->insertAtTail(autoCopy); } } } } } } }
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))); } } } }
void FnSymbol::verify() { Symbol::verify(); if (astTag != E_FnSymbol) { INT_FATAL(this, "Bad FnSymbol::astTag"); } if (_this && _this->defPoint->parentSymbol != this) INT_FATAL(this, "Each method must contain a 'this' declaration."); if (normalized) { CallExpr* last = toCallExpr(body->body.last()); if (last == NULL || last->isPrimitive(PRIM_RETURN) == false) { INT_FATAL(this, "Last statement in normalized function is not a return"); } } if (formals.parent != this) { INT_FATAL(this, "Bad AList::parent in FnSymbol"); } if (where && where->parentSymbol != this) { INT_FATAL(this, "Bad FnSymbol::where::parentSymbol"); } if (retExprType && retExprType->parentSymbol != this) { INT_FATAL(this, "Bad FnSymbol::retExprType::parentSymbol"); } if (body && body->parentSymbol != this) { INT_FATAL(this, "Bad FnSymbol::body::parentSymbol"); } for_alist(fExpr, formals) { DefExpr* argDef = toDefExpr(fExpr); INT_ASSERT(argDef); INT_ASSERT(isArgSymbol(argDef->sym)); }
// // Attempts to replace references with the variables the references point to, // provided the references have a single definition. // // For example: // var foo : int; // ref A : int; // (move A (addr-of foo)) // // (move B (deref A)) ---> (move B foo) // void eliminateSingleAssignmentReference(Map<Symbol*,Vec<SymExpr*>*>& defMap, Map<Symbol*,Vec<SymExpr*>*>& useMap, Symbol* var) { if (CallExpr* move = findRefDef(defMap, var)) { if (CallExpr* rhs = toCallExpr(move->get(2))) { if (rhs->isPrimitive(PRIM_ADDR_OF) || rhs->isPrimitive(PRIM_SET_REFERENCE)) { bool stillAlive = false; for_uses(se, useMap, var) { CallExpr* parent = toCallExpr(se->parentExpr); SET_LINENO(se); if (parent && (parent->isPrimitive(PRIM_DEREF) || isDerefMove(parent))) { SymExpr* se = toSymExpr(rhs->get(1)->copy()); INT_ASSERT(se); Expr* toReplace = parent; if (isMoveOrAssign(parent)) { toReplace = parent->get(2); } toReplace->replace(se); ++s_ref_repl_count; addUse(useMap, se); } else if (parent && (parent->isPrimitive(PRIM_GET_MEMBER_VALUE) || parent->isPrimitive(PRIM_GET_MEMBER) || parent->isPrimitive(PRIM_GET_MEMBER_VALUE) || parent->isPrimitive(PRIM_GET_MEMBER))) { SymExpr* se = toSymExpr(rhs->get(1)->copy()); INT_ASSERT(se); parent->get(1)->replace(se); ++s_ref_repl_count; addUse(useMap, se); } else if (parent && (parent->isPrimitive(PRIM_MOVE) || parent->isPrimitive(PRIM_SET_REFERENCE))) { CallExpr* rhsCopy = rhs->copy(); if (parent->isPrimitive(PRIM_SET_REFERENCE)) { // Essentially a pointer copy like a (move refA refB) parent = toCallExpr(parent->parentExpr); INT_ASSERT(parent && isMoveOrAssign(parent)); } parent->get(2)->replace(rhsCopy); ++s_ref_repl_count; SymExpr* se = toSymExpr(rhsCopy->get(1)); INT_ASSERT(se); addUse(useMap, se); // BHARSH TODO: Is it possible to handle the following case safely // for PRIM_ASSIGN? // // ref i_foo : T; // (move i_foo (set reference bar)) // (= call_tmp i_foo) // // Should that turn into (= call_tmp bar)? } else if (parent && parent->isPrimitive(PRIM_ASSIGN) && parent->get(1) == se) { // for_defs should handle this case } else if (parent && parent->isResolved()) { stillAlive = true; // TODO -- a reference argument can be passed directly } else { stillAlive = true; } } for_defs(se, defMap, var) { CallExpr* parent = toCallExpr(se->parentExpr); SET_LINENO(se); if (parent == move) continue; if (parent && isMoveOrAssign(parent)) { SymExpr* se = toSymExpr(rhs->get(1)->copy()); INT_ASSERT(se); parent->get(1)->replace(se); ++s_ref_repl_count; addDef(defMap, se); } else stillAlive = true; } if (!stillAlive) { var->defPoint->remove(); Vec<SymExpr*>* defs = defMap.get(var); if (defs == NULL) { INT_FATAL(var, "Expected var to be defined"); } // Remove the first definition from the AST. defs->v[0]->getStmtExpr()->remove(); } } else if (rhs->isPrimitive(PRIM_GET_MEMBER) ||
static bool inferRefToConst(Symbol* sym) { INT_ASSERT(sym->isRef()); bool isConstRef = sym->qualType().getQual() == QUAL_CONST_REF; const bool wasRefToConst = sym->hasFlag(FLAG_REF_TO_CONST); ConstInfo* info = infoMap[sym]; // If this ref isn't const, then it can't point to a const thing if (info == NULL) { return false; } else if (info->finalizedRefToConst || wasRefToConst || !isConstRef) { return wasRefToConst; } bool isFirstCall = false; if (info->alreadyCalled == false) { isFirstCall = true; info->alreadyCalled = true; } bool isRefToConst = true; if (isArgSymbol(sym)) { // Check each call and set isRefToConst to false if any actual is not a ref // to a const. FnSymbol* fn = toFnSymbol(sym->defPoint->parentSymbol); if (fn->hasFlag(FLAG_VIRTUAL) || fn->hasFlag(FLAG_EXPORT) || fn->hasFlag(FLAG_EXTERN)) { // Not sure how to best handle virtual calls, so simply set false for now // // For export or extern functions, return false because we don't have // all the information about how the function is called. isRefToConst = false; } else { // Need this part to be re-entrant in case of recursive functions while (info->fnUses != NULL && isRefToConst) { SymExpr* se = info->fnUses; info->fnUses = se->symbolSymExprsNext; CallExpr* call = toCallExpr(se->parentExpr); INT_ASSERT(call && call->isResolved()); Symbol* actual = toSymExpr(formal_to_actual(call, sym))->symbol(); if (actual->isRef()) { // I don't think we technically need to skip if the actual is the // same symbol as the formal, but it makes things simpler. if (actual != sym && !inferRefToConst(actual)) { isRefToConst = false; } } else { // Passing a non-ref actual to a reference formal is currently // considered to be the same as an addr-of if (actual->qualType().getQual() != QUAL_CONST_VAL) { isRefToConst = false; } } } } } while (info->hasMore() && isRefToConst) { SymExpr* use = info->next(); CallExpr* call = toCallExpr(use->parentExpr); if (call == NULL) continue; if (isMoveOrAssign(call)) { if (use == call->get(1)) { if (SymExpr* se = toSymExpr(call->get(2))) { if (se->isRef() && !inferRefToConst(se->symbol())) { isRefToConst = false; } } else { CallExpr* RHS = toCallExpr(call->get(2)); INT_ASSERT(RHS); if (RHS->isPrimitive(PRIM_ADDR_OF) || RHS->isPrimitive(PRIM_SET_REFERENCE)) { SymExpr* src = toSymExpr(RHS->get(1)); if (src->isRef()) { if (!inferRefToConst(src->symbol())) { isRefToConst = false; } } else { if (src->symbol()->qualType().getQual() != QUAL_CONST_VAL) { isRefToConst = false; } } } else { isRefToConst = false; } } } } else if (call->isResolved()) { isRefToConst = true; } else { isRefToConst = isSafeRefPrimitive(use); } } if (isFirstCall) { if (isRefToConst) { INT_ASSERT(info->finalizedRefToConst == false); sym->addFlag(FLAG_REF_TO_CONST); } info->reset(); info->finalizedRefToConst = true; } else if (!isRefToConst) { info->finalizedRefToConst = true; } return isRefToConst; }
// Note: This function is currently not recursive static bool inferConst(Symbol* sym) { INT_ASSERT(!sym->isRef()); const bool wasConstVal = sym->qualType().getQual() == QUAL_CONST_VAL; ConstInfo* info = infoMap[sym]; // 'info' may be null if the argument is never used. In that case we can // consider 'sym' to be a const-ref. By letting the rest of the function // proceed, we'll fix up the qualifier for such symbols at the end. if (info == NULL) { return true; } else if (info->finalizedConstness || wasConstVal) { return wasConstVal; } bool isConstVal = true; int numDefs = 0; while (info->hasMore() && isConstVal) { SymExpr* use = info->next(); CallExpr* call = toCallExpr(use->parentExpr); if (call == NULL) { // Could be a DefExpr, or the condition for a while loop. // BHARSH: I'm not sure of all the possibilities continue; } CallExpr* parent = toCallExpr(call->parentExpr); if (call->isResolved()) { ArgSymbol* form = actual_to_formal(use); // // If 'sym' is constructed through a _retArg, we can consider that to // be a single 'def'. // if (form->hasFlag(FLAG_RETARG)) { numDefs += 1; } else if (form->isRef()) { if (!inferConstRef(form)) { isConstVal = false; } } } else if (parent && isMoveOrAssign(parent)) { if (call->isPrimitive(PRIM_ADDR_OF) || call->isPrimitive(PRIM_SET_REFERENCE)) { Symbol* LHS = toSymExpr(parent->get(1))->symbol(); INT_ASSERT(LHS->isRef()); if (onlyUsedForRetarg(LHS, parent)) { numDefs += 1; } else if (!inferConstRef(LHS)) { isConstVal = false; } } } else if (isMoveOrAssign(call)) { if (use == call->get(1)) { numDefs += 1; } } else { // To be safe, exit the loop with 'false' if we're unsure of how to // handle a primitive. isConstVal = false; } if (numDefs > 1) { isConstVal = false; } } if (isConstVal && !info->finalizedConstness) { if (ArgSymbol* arg = toArgSymbol(sym)) { INT_ASSERT(arg->intent & INTENT_FLAG_IN); arg->intent = INTENT_CONST_IN; } else { INT_ASSERT(isVarSymbol(sym)); sym->qual = QUAL_CONST_VAL; } } info->reset(); info->finalizedConstness = true; return isConstVal; }
// // Returns 'true' if 'sym' is (or should be) a const-ref. // If 'sym' can be a const-ref, but is not, this function will change either // the intent or qual of the Symbol to const-ref. // static bool inferConstRef(Symbol* sym) { INT_ASSERT(sym->isRef()); bool wasConstRef = sym->qualType().getQual() == QUAL_CONST_REF; if (sym->defPoint->parentSymbol->hasFlag(FLAG_EXTERN)) { return wasConstRef; } ConstInfo* info = infoMap[sym]; // 'info' may be null if the argument is never used. In that case we can // consider 'sym' to be a const-ref. By letting the rest of the function // proceed, we'll fix up the qualifier for such symbols at the end. if (info == NULL) { return true; } else if (info->finalizedConstness || wasConstRef) { return wasConstRef; } bool isFirstCall = false; if (info->alreadyCalled == false) { isFirstCall = true; info->alreadyCalled = true; } bool isConstRef = true; while (info->hasMore() && isConstRef) { SymExpr* use = info->next(); CallExpr* call = toCallExpr(use->parentExpr); INT_ASSERT(call); CallExpr* parent = toCallExpr(call->parentExpr); if (call->isResolved()) { ArgSymbol* form = actual_to_formal(use); if (form->isRef() && !inferConstRef(form)) { isConstRef = false; } } else if (parent && isMoveOrAssign(parent)) { if (!canRHSBeConstRef(parent, use)) { isConstRef = false; } } else if (call->isPrimitive(PRIM_MOVE)) { // // Handles three cases: // 1) MOVE use value - writing to a reference, so 'use' cannot be const // 2) MOVE ref use - if the LHS is not const, use cannot be const either // 3) MOVE value use - a dereference of 'use' // if (use == call->get(1)) { // CASE 1 if (!call->get(2)->isRef()) { isConstRef = false; } } else { // 'use' is the RHS of a MOVE if (call->get(1)->isRef()) { // CASE 2 SymExpr* se = toSymExpr(call->get(1)); INT_ASSERT(se); if (!inferConstRef(se->symbol())) { isConstRef = false; } } // else CASE 3: do nothing because isConstRef is already true } } else if (call->isPrimitive(PRIM_ASSIGN)) { if (use == call->get(1)) { isConstRef = false; } } else if (call->isPrimitive(PRIM_SET_MEMBER) || call->isPrimitive(PRIM_SET_SVEC_MEMBER)) { // BHARSH 2016-11-02 // In the expr (set_member base member rhs), // If use == base, I take the conservative approach and decide that 'use' // is not a const-ref. I'm not sure that we've decided what const means // for fields yet, so this seems safest. // // If use == rhs, then we would need to do analysis for the member field. // That's beyond the scope of what I'm attempting at the moment, so to // be safe we'll return false for that case. if (use == call->get(1) || use == call->get(3)) { isConstRef = false; } else { // use == member // If 'rhs' is not a ref, then we're writing into 'use'. Otherwise it's // a pointer copy and we don't care if 'rhs' is writable. if (!call->get(3)->isRef()) { isConstRef = false; } } } else { // To be safe, exit the loop with 'false' if we're unsure of how to // handle a primitive. isConstRef = false; } } if (isFirstCall) { if (isConstRef) { INT_ASSERT(info->finalizedConstness == false); if (ArgSymbol* arg = toArgSymbol(sym)) { arg->intent = INTENT_CONST_REF; } else { INT_ASSERT(isVarSymbol(sym)); sym->qual = QUAL_CONST_REF; } } info->reset(); info->finalizedConstness = true; } else if (!isConstRef) { info->finalizedConstness = true; } return isConstRef; }
// This routine returns true if the value of the given symbol may have changed // due to execution of the containing expression. // If the symbol is a reference, this means that the address to which the // symbol points will be changed, not the value contained in that address. See // isRefUse() for that case. // To be conservative, the routine should return true by default and then // select the cases where we are sure nothing has changed. static bool needsKilling(SymExpr* se, std::set<Symbol*>& liveRefs) { INT_ASSERT(se->isRef() == false); if (toGotoStmt(se->parentExpr)) { return false; } if (toCondStmt(se->parentExpr)) { return false; } if (toBlockStmt(se->parentExpr)) { return false; } if (isDefExpr(se->parentExpr)) { return false; } CallExpr* call = toCallExpr(se->parentExpr); if (FnSymbol* fn = call->resolvedFunction()) { // Skip the "base" symbol. if (se->symbol() == fn) { return false; } ArgSymbol* arg = actual_to_formal(se); if (arg->intent == INTENT_OUT || arg->intent == INTENT_INOUT || arg->intent == INTENT_REF || arg->hasFlag(FLAG_ARG_THIS)) // Todo: replace with arg intent check? { liveRefs.insert(se->symbol()); return true; } if (isRecordWrappedType(arg->type)) { return true; } return false; } else { const bool isFirstActual = call->get(1) == se; if ((call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN)) && isFirstActual) { return true; } if (isOpEqualPrim(call) && isFirstActual) { return true; } if (call->isPrimitive(PRIM_SET_MEMBER) && isFirstActual) { return true; } if (call->isPrimitive(PRIM_ARRAY_SET) || call->isPrimitive(PRIM_ARRAY_SET_FIRST)) { if (isFirstActual) { return true; } return false; } if (call->isPrimitive(PRIM_GET_MEMBER)) { // This creates an alias to a portion of the first arg. // We could track this as a reference and invalidate a pair containing // this symbol when the ref is dereferenced. But for now, we want to // preserve the mapping ref = &value in the RefMap, so having a (ref, // value) pair also possibly mean ref = &(value.subfield) does not quite // fit. // We could keep the ability to do (deref ref) <- value substitution by // keeping a separate map for "true" references, or by performing those // substitutions in a separate pass. // For now, we treat subfield extraction as evidence of a future change // to the symbol itself, and use that fact to remove it from // consideration in copy propagation. if (isFirstActual) { // We select just the case where the referent is passed by value, // because in the other case, the address of the object is not // returned, so that means that the address (i.e. the value of the // reference variable) does not change. return true; } return false; } if (call->isPrimitive(PRIM_ADDR_OF) || call->isPrimitive(PRIM_SET_REFERENCE)) { liveRefs.insert(se->symbol()); return true; } return false; } INT_ASSERT(0); // Should never get here. return true; }
void ReturnByRef::updateAssignmentsFromRefTypeToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(fn, defMap, useMap); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* symLhs = toSymExpr (move->get(1)); CallExpr* callRhs = toCallExpr(move->get(2)); if (symLhs && callRhs && callRhs->isPrimitive(PRIM_DEREF)) { VarSymbol* varLhs = toVarSymbol(symLhs->symbol()); SymExpr* symRhs = toSymExpr(callRhs->get(1)); VarSymbol* varRhs = toVarSymbol(symRhs->symbol()); // MPF 2016-10-02: It seems to me that this code should also handle the // case that symRhs is an ArgSymbol, but adding that caused problems // in the handling of out argument intents. if (varLhs != NULL && varRhs != NULL) { if (isUserDefinedRecord(varLhs->type) == true && varRhs->type == varLhs->type->refType) { // HARSHBARGER 2015-12-11: // `init_untyped_var` in the `normalize` pass may insert an // initCopy, which means that we should not insert an autocopy // for that same variable. bool initCopied = false; for_uses(use, useMap, varLhs) { if (CallExpr* call = toCallExpr(use->parentExpr)) { if (FnSymbol* parentFn = call->isResolved()) { if (parentFn->hasFlag(FLAG_INIT_COPY_FN)) { initCopied = true; break; } } } } if (!initCopied) { SET_LINENO(move); SymExpr* lhsCopy0 = symLhs->copy(); SymExpr* lhsCopy1 = symLhs->copy(); FnSymbol* autoCopy = autoCopyMap.get(varLhs->type); CallExpr* copyExpr = new CallExpr(autoCopy, lhsCopy0); CallExpr* moveExpr = new CallExpr(PRIM_MOVE,lhsCopy1, copyExpr); move->insertAfter(moveExpr); } } } }
BlockStmt* ForLoop::buildForLoop(Expr* indices, Expr* iteratorExpr, BlockStmt* body, bool coforall, bool zippered) { VarSymbol* index = newTemp("_indexOfInterest"); VarSymbol* iterator = newTemp("_iterator"); CallExpr* iterInit = 0; CallExpr* iterMove = 0; ForLoop* loop = new ForLoop(index, iterator, body, zippered); LabelSymbol* continueLabel = new LabelSymbol("_continueLabel"); LabelSymbol* breakLabel = new LabelSymbol("_breakLabel"); BlockStmt* retval = new BlockStmt(); iterator->addFlag(FLAG_EXPR_TEMP); // Unzippered loop, treat all objects (including tuples) the same if (zippered == false) { iterInit = new CallExpr(PRIM_MOVE, iterator, new CallExpr("_getIterator", iteratorExpr)); // try to optimize anonymous range iteration tryToReplaceWithDirectRangeIterator(iteratorExpr); } // Zippered loop: Expand args to a tuple with an iterator for each element. else { CallExpr* zipExpr = toCallExpr(iteratorExpr); if (zipExpr && zipExpr->isPrimitive(PRIM_ZIP)) { // The PRIM_ZIP indicates this is a new-style zip() AST. // Expand arguments to a tuple with appropriate iterators for each value. // // Specifically, change: // zip(a, b, c, ...) // into the tuple: // (_getIterator(a), _getIterator(b), _getIterator(c), ...) // // (ultimately, we will probably want to make this style of // rewrite into a utility function for the other get*Zip // functions as we convert parallel loops over to use PRIM_ZIP). // zipExpr->primitive = NULL; // remove the primitive // If there's just one argument... if (zipExpr->argList.length == 1) { Expr* zipArg = zipExpr->argList.only(); CallExpr* zipArgCall = toCallExpr(zipArg); // ...and it is a tuple expansion '(...t)' then remove the // tuple expansion primitive and simply pass the tuple itself // to _getIteratorZip(). This will not require any more // tuples than the user introduced themselves. // if (zipArgCall && zipArgCall->isPrimitive(PRIM_TUPLE_EXPAND)) { zipExpr->baseExpr = new UnresolvedSymExpr("_getIteratorZip"); Expr* tupleArg = zipArgCall->argList.only(); tupleArg->remove(); zipArgCall->replace(tupleArg); } else { // ...otherwise, make the expression into a _getIterator() // call zipExpr->baseExpr = new UnresolvedSymExpr("_getIterator"); // try to optimize anonymous range iteration tryToReplaceWithDirectRangeIterator(zipArg); } } else { // // Otherwise, if there's more than one argument, build up the // tuple by applying _getIterator() to each element. // zipExpr->baseExpr = new UnresolvedSymExpr("_build_tuple"); Expr* arg = zipExpr->argList.first(); while (arg) { Expr* next = arg->next; Expr* argCopy = arg->copy(); arg->replace(new CallExpr("_getIterator", argCopy)); // try to optimize anonymous range iteration tryToReplaceWithDirectRangeIterator(argCopy); arg = next; } } iterInit = new CallExpr(PRIM_MOVE, iterator, zipExpr); assert(zipExpr == iteratorExpr); } else { // // This is an old-style zippered loop so handle it in the old style // iterInit = new CallExpr(PRIM_MOVE, iterator, new CallExpr("_getIteratorZip", iteratorExpr)); // try to optimize anonymous range iteration if (CallExpr* call = toCallExpr(iteratorExpr)) if (call->isNamed("_build_tuple")) for_actuals(actual, call) tryToReplaceWithDirectRangeIterator(actual); } } index->addFlag(FLAG_INDEX_OF_INTEREST); iterMove = new CallExpr(PRIM_MOVE, index, new CallExpr("iteratorIndex", iterator)); if (indices == 0) indices = new UnresolvedSymExpr("chpl__elidedIdx"); checkIndices(indices); destructureIndices(loop, indices, new SymExpr(index), coforall); if (coforall) index->addFlag(FLAG_COFORALL_INDEX_VAR); loop->mContinueLabel = continueLabel; loop->mBreakLabel = breakLabel; loop->insertAtTail(new DefExpr(continueLabel)); retval->insertAtTail(new DefExpr(index)); retval->insertAtTail(new DefExpr(iterator)); retval->insertAtTail(iterInit); retval->insertAtTail(new BlockStmt(iterMove, BLOCK_TYPE)); retval->insertAtTail(loop); retval->insertAtTail(new DefExpr(breakLabel)); retval->insertAtTail(new CallExpr("_freeIterator", iterator)); return retval; }