// 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; }
// Returns true if the symbol is read in the containing expression, // false otherwise. If the operand is used as an address, // that does not count as a 'read', so false is returned in that case. static bool isUse(SymExpr* se) { if (toGotoStmt(se->parentExpr)) { return false; } if (toCondStmt(se->parentExpr)) { return true; } if (toBlockStmt(se->parentExpr)) { return true; } 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; } // A "normal" call. ArgSymbol* arg = actual_to_formal(se); if (arg->intent == INTENT_OUT || arg->intent == INTENT_REF) { return false; } else if (arg->intent == INTENT_CONST_REF && isRecord(arg->type)) { // We can currently return a const reference to a field in a record // passed by const ref. return false; } } else { INT_ASSERT(call->primitive); const bool isFirstActual = call->get(1) == se; switch(call->primitive->tag) { default: return true; case PRIM_MOVE: case PRIM_ASSIGN: case PRIM_ADD_ASSIGN: case PRIM_SUBTRACT_ASSIGN: case PRIM_MULT_ASSIGN: case PRIM_DIV_ASSIGN: case PRIM_MOD_ASSIGN: case PRIM_LSH_ASSIGN: case PRIM_RSH_ASSIGN: case PRIM_AND_ASSIGN: case PRIM_OR_ASSIGN: case PRIM_XOR_ASSIGN: if (isFirstActual) { return false; } return true; case PRIM_ARRAY_ALLOC: case PRIM_ADDR_OF: case PRIM_SET_REFERENCE: return false; // See Note #2. case PRIM_PRIVATE_BROADCAST: // The operand is used by name (it must be a manifest constant). // Thus it acts more like an address than a value. return false; case PRIM_CHPL_COMM_GET: case PRIM_CHPL_COMM_PUT: case PRIM_CHPL_COMM_ARRAY_GET: case PRIM_CHPL_COMM_ARRAY_PUT: case PRIM_CHPL_COMM_GET_STRD: case PRIM_CHPL_COMM_PUT_STRD: // ('comm_get/put' locAddr locale widePtr len) // The first and third operands are treated as addresses. // The second and fourth are values if (se == call->get(2) || se == call->get(4)) { return true; } return false; case PRIM_CHPL_COMM_REMOTE_PREFETCH: // comm prefetch locale widePtr len // second argument is an address // first and third are values. if (isFirstActual || se == call->get(3)) { return true; } return false; case PRIM_SET_MEMBER: // The first operand works like a reference, and the second is a field // name. Only the third is a replaceable use. if (se == call->get(3)) { return true; } return false; case PRIM_GET_MEMBER: case PRIM_GET_MEMBER_VALUE: if (isFirstActual) { return false; } return true; case PRIM_ARRAY_SET: case PRIM_ARRAY_SET_FIRST: case PRIM_ARRAY_GET: case PRIM_ARRAY_GET_VALUE: // The first operand is treated like a reference. if (isFirstActual) { return false; } return true; case PRIM_SET_UNION_ID: // The first operand is treated like a reference. if (isFirstActual) { return false; } return true; } } return true; }