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