// // 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); }
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; } } }
// // 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; }