// // The argument expr is a use of a wide reference. Insert a check to ensure // that it is on the current locale, then drop its wideness by moving the // addr field into a non-wide of otherwise the same type. Then, replace its // use with the non-wide version. // static void insertLocalTemp(Expr* expr) { SymExpr* se = toSymExpr(expr); Expr* stmt = expr->getStmtExpr(); INT_ASSERT(se && stmt); SET_LINENO(se); VarSymbol* var = newTemp(astr("local_", se->var->name), se->var->type->getField("addr")->type); if (!fNoLocalChecks) { stmt->insertBefore(new CallExpr(PRIM_LOCAL_CHECK, se->copy())); } stmt->insertBefore(new DefExpr(var)); stmt->insertBefore(new CallExpr(PRIM_MOVE, var, se->copy())); se->replace(new SymExpr(var)); }
static void checkErrorHandling(FnSymbol* fn, implicitThrowsReasons_t* reasons) { if (strcmp(fn->name, "deinit") == 0) { if (fn->throwsError()) USR_FATAL_CONT(fn, "deinit is not permitted to throw"); } error_checking_mode_t mode = computeErrorCheckingMode(fn); INT_ASSERT(mode != ERROR_MODE_UNKNOWN); ErrorCheckingVisitor visit(fn->throwsError(), mode, reasons); fn->body->accept(&visit); }
/* * 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; } } } } } }
// // 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_VALUE: case PRIM_GET_SVEC_MEMBER_VALUE: if (LHS->isRef() == false && isClass(LHS->typeInfo()) == false) { return true; } // fallthrough case PRIM_GET_MEMBER: case PRIM_GET_SVEC_MEMBER: 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()) { return inferConstRef(LHS->symbol()); } } default: break; } return isSafeRefPrimitive(use); }
void IpeEnv::storeFoo(LcnSymbol* sym, IpeValue value) { #if 0 if (sym->type == dtBools[BOOL_SIZE_SYS]) { storeBool(sym, value.boolGet()); } else if (sym->type == dtInt[INT_SIZE_64]) { storeInteger(sym, value.integerGet()); } else if (sym->type == dtReal[FLOAT_SIZE_64]) { storeReal(sym, value.realGet()); } else { AstDumpToNode logger(stdout, 3); printf("\n\n"); printf(" IpeEnv::store(LcnSymbol*) Unsupported\n"); printf(" "); sym->accept(&logger); printf("\n\n"); INT_ASSERT(false); } #endif INT_ASSERT(false); }
// // ref: The reference symbol we will test to see if it is only used as an // actual where the corresponding formal has FLAG_RETARG. // // 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 onlyUsedForRetarg(Symbol* ref, CallExpr* defCall) { bool isRetArgOnly = true; INT_ASSERT(ref->isRef()); INT_ASSERT(defCall != NULL); for_SymbolSymExprs(use, ref) { if (use->parentExpr == defCall) { continue; } CallExpr* call = toCallExpr(use->parentExpr); if (call->isResolved()) { ArgSymbol* form = actual_to_formal(use); if (form->hasFlag(FLAG_RETARG) == false) { isRetArgOnly = false; } } else { isRetArgOnly = false; } } return isRetArgOnly; }
// // 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(); } } } } }
static void lowerErrorHandling(FnSymbol* fn) { ArgSymbol* outError = NULL; LabelSymbol* epilogue = NULL; if (fn->throwsError()) { SET_LINENO(fn); outError = addOutErrorArg(fn); epilogue = fn->getOrCreateEpilogueLabel(); INT_ASSERT(epilogue); // throws requires an epilogue } ErrorHandlingVisitor visitor = ErrorHandlingVisitor(outError, epilogue); fn->accept(&visitor); }
// This is intended to mimick Expr::remove(), without 'this' being an Expr. void ForallIntents::removeFI(Expr* parentB) { // If this fails need to use trace_remove() instead of remove_help() // - see Expr::remove(). INT_ASSERT(parentB->parentSymbol); // "Remove" all ASTs that 'this' contains. #define REMOVE(dest) if (dest) remove_help(dest, 'r') for_vector(Expr, var, fiVars) REMOVE(var); for_riSpecs_vector(ri, this) REMOVE(ri); REMOVE(iterRec); REMOVE(leadIdx); REMOVE(leadIdxCopy); #undef REMOVE }
std::string genMakefileEnvCache(void) { std::string result; std::map<std::string, const char*>::iterator env; for (env = envMap.begin(); env != envMap.end(); ++env) { const std::string& key = env->first; const char* oldPrefix = "CHPL_"; const char* newPrefix = "CHPL_MAKE_"; INT_ASSERT(key.substr(0, strlen(oldPrefix)) == oldPrefix); std::string keySuffix = key.substr(strlen(oldPrefix), std::string::npos); std::string chpl_make_key = newPrefix + keySuffix; result += chpl_make_key + "=" + std::string(env->second) + "|"; } return result; }
// This is a placeholder void IpeModule::initialize() { if (mState == kLoaded || mState == kResolving) INT_ASSERT(false); else if (mState == kInitializing || mState == kInitialized) ; else { mState = kInitializing; mEnv->initializeUsedModules(); mState = kInitialized; } }
static Type* returnInfoArrayIndexValue(CallExpr* call) { SymExpr* sym = toSymExpr(call->get(1)); INT_ASSERT(sym); Type* type = sym->var->type; if (type->symbol->hasFlag(FLAG_WIDE_CLASS)) type = type->getField("addr")->type; if (!type->substitutions.n) INT_FATAL(call, "bad primitive"); // Is this conditional necessary? Can just assume condition is true? if (type->symbol->hasFlag(FLAG_DATA_CLASS)) { return toTypeSymbol(getDataClassType(type->symbol))->type; } else { return toTypeSymbol(type->substitutions.v[0].value)->type; } }
// // 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; } }
IpeModuleRoot* IpeModuleRoot::allocate() { if (sRootModule == 0) { ModuleSymbol* modSym = createDeclaration(); sRootModule = new IpeModuleRoot(modSym); sRootModule->init(); } else { printf("Fatal error:: IpeModuleRoot:allocate has already been executed\n"); INT_ASSERT(false); } return sRootModule; }
// Find the block stmt that encloses the target of this gotoStmt static BlockStmt* findBlockForTarget(GotoStmt* stmt) { BlockStmt* retval = NULL; if (stmt != NULL && stmt->isGotoReturn() == false) { SymExpr* labelSymExpr = toSymExpr(stmt->label); Expr* ptr = labelSymExpr->symbol()->defPoint; while (ptr != NULL && isBlockStmt(ptr) == false) { ptr = ptr->parentExpr; } retval = toBlockStmt(ptr); INT_ASSERT(retval); } return retval; }
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; }
static TFITag it2tfi(Expr* ref, IntentTag intent) { switch (intent) { case INTENT_IN: return TFI_IN; case INTENT_CONST: return TFI_CONST; case INTENT_CONST_IN: return TFI_CONST_IN; case INTENT_REF: return TFI_REF; case INTENT_CONST_REF: return TFI_CONST_REF; case INTENT_BLANK: return TFI_DEFAULT; case INTENT_OUT: case INTENT_INOUT: case INTENT_PARAM: case INTENT_TYPE: USR_FATAL_CONT(ref, "%s is not supported in a 'with' clause", intentDescrString(intent)); return TFI_DEFAULT; } INT_ASSERT(false); // unexpected IntentTag; 'intent' contains garbage? return TFI_DEFAULT; // dummy }
Expr* postFold(Expr* expr) { SET_LINENO(expr); Expr* retval = expr; INT_ASSERT(expr->inTree()); if (CallExpr* call = toCallExpr(expr)) { if (call->isResolved() == true) { retval = postFoldNormal(call); } else if (call->isPrimitive() == true) { retval = postFoldPrimop(call); } } else if (SymExpr* sym = toSymExpr(expr)) { retval = postFoldSymExpr(sym); } return retval; }
int IpeScopeMethod::locationSet(ArgSymbol* arg) const { int offset = 0; int retval = -1; for (size_t i = 0; i < mVariables.size() && retval == -1; i++) { if (mVariables[i] == arg) { retval = offset; arg->locationSet(1, offset); } else { offset = offset + 8; } } INT_ASSERT(retval >= 0); return retval; }
const char* IpeModuleRoot::pathNameForFile(ModTag moduleType, const char* fileName) { const char* retval = NULL; switch (moduleType) { case MOD_INTERNAL: retval = pathNameForInternalFile(fileName); break; case MOD_STANDARD: retval = pathNameForStandardFile(fileName); break; case MOD_USER: retval = NULL; INT_ASSERT(false); break; } return retval; }
IpeModule* IpeModuleRoot::create(ModuleSymbol* sym) { IpeModule* retval = NULL; switch (sym->modTag) { case MOD_INTERNAL: retval = new IpeModuleInternal(sRootModule, sym); break; case MOD_STANDARD: retval = new IpeModuleStandard(sRootModule, sym); break; case MOD_USER: retval = NULL; INT_ASSERT(false); break; } return retval; }
static bool feedsIntoForallIterableExpr(FnSymbol* fn, SymExpr* use) { if (CallExpr* call = callingExpr(use)) { if (isForallIterExpr(call)) return true; if (FnSymbol* useFn = toFnSymbol(use->parentSymbol)) // Does 'useFn' return the value of 'use' ? if (useFn->retType == fn->retType && isReturnedValue(useFn, use)) { bool forForall = false; bool otherUse = false; for_SymbolSymExprs(use2, useFn) { CallExpr* call2 = callingExpr(use2); if (isForallIterExpr(call2)) forForall = true; else otherUse = true; } INT_ASSERT(!(forForall && otherUse)); return forForall; }
// If vectorization is enabled and this loop is order independent, codegen // CHPL_PRAGMA_IVDEP. This method is a no-op if vectorization is off, or the // loop is not order independent. void LoopStmt::codegenOrderIndependence() { if (fNoVectorize == false && isOrderIndependent()) { GenInfo* info = gGenInfo; // Note: This *must* match the macro definitions provided in the runtime std:: string ivdepStr = "CHPL_PRAGMA_IVDEP"; if (fReportOrderIndependentLoops) { ModuleSymbol *mod = toModuleSymbol(this->getModule()); INT_ASSERT(mod); if (developer || mod->modTag == MOD_USER) { printf("Adding %s to %s for %s:%d\n", ivdepStr.c_str(), this->astTagAsString(), mod->name, this->linenum()); } } info->cStatements.push_back(ivdepStr+'\n'); } }
// Scan the SymExprs in the current block, looking for ones that can be replaced. for_vector(SymExpr, se, symExprs) { if (se->isRef()) continue; // Replace an alias with its definition, using the current set of // available pairs. if (isUse(se)) { // See if there is an (alias,def) pair. AvailableMap::iterator alias_def_pair = available.find(se->symbol()); // If so, replace the alias with its definition. if (alias_def_pair != available.end()) { DEBUG_COPYPROP("Replacing %s[%d] with %s[%d]\n", alias_def_pair->first->name, alias_def_pair->first->id, alias_def_pair->second->name, alias_def_pair->second->id); INT_ASSERT(alias_def_pair->first != alias_def_pair->second); se->setSymbol(alias_def_pair->second); ++s_repl_count; } } }
// // Removes gotos where the label immediately follows the goto and // unused labels // void removeUnnecessaryGotos(FnSymbol* fn) { std::vector<BaseAST*> asts; std::set<BaseAST*> labels; collect_asts_STL(fn, asts); for_vector(BaseAST, ast, asts) { if (GotoStmt* gotoStmt = toGotoStmt(ast)) { DefExpr* def = toDefExpr(gotoStmt->next); SymExpr* label = toSymExpr(gotoStmt->label); INT_ASSERT(label); if (def && def->sym == label->var) gotoStmt->remove(); else labels.insert(label->var); } } for_vector(BaseAST, ast2, asts) { if (DefExpr* def = toDefExpr(ast2)) if (LabelSymbol* label = toLabelSymbol(def->sym)) if (labels.find(label) == labels.end()) def->remove(); } }
// binary search over intervals static int i_find(Intervals *i, int x) { INT_ASSERT(i->n); int l = 0, h = i->n; Lrecurse: if (h <= l + 2) { if (h <= l) return -(l + 1); if (x < i->v[l] || x > i->v[l + 1]) return -(l + 1); return h; } int m = (((h - l) / 4) * 2) + l; if (x > i->v[m + 1]) { l = m; goto Lrecurse; } if (x < i->v[m]) { h = m; goto Lrecurse; } return (l + 1); }
// // A forall-intents variation on isOuterVar() in createTaskFunctions.cpp: // Is 'sym' defined outside 'block'? // static bool isOuterVar(Symbol* sym, BlockStmt* block) { if (sym->isParameter() || // includes isImmediate() sym->hasFlag(FLAG_TEMP) || // exclude these // Consts need no special semantics for begin/cobegin/coforall/on. // Implementation-wise, it is uniform with consts in nested functions. sym->hasFlag(FLAG_CONST) || // NB 'type' formals do not have INTENT_TYPE sym->hasFlag(FLAG_TYPE_VARIABLE) // 'type' aliases or formals ) { // these are either not variables or not defined outside of 'fn' return false; } DefExpr* defPt = sym->defPoint; Expr* parentExp = defPt->parentExpr; while (true) { if (!parentExp) { Symbol* parentSym = defPt->parentSymbol; if (isModuleSymbol(parentSym)) // We reached the outermost level and did not come across 'block'. return true; defPt = parentSym->defPoint; parentExp = defPt->parentExpr; continue; } if (parentExp == block) return false; parentExp = parentExp->parentExpr; } INT_ASSERT(false); return false; // dummy }
// // 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; }