// // Determine the index type for a ParamForLoop. // // This implementation creates a range with low/high values and then // asks for its type. // Type* ParamForLoop::indexType() { SymExpr* lse = lowExprGet(); SymExpr* hse = highExprGet(); CallExpr* range = new CallExpr("chpl_build_bounded_range", lse->copy(), hse->copy()); Type* idxType = 0; insertBefore(range); resolveCall(range); if (FnSymbol* sym = range->isResolved()) { resolveFormals(sym); DefExpr* formal = toDefExpr(sym->formals.get(1)); if (toArgSymbol(formal->sym)->typeExpr) idxType = toArgSymbol(formal->sym)->typeExpr->body.tail->typeInfo(); else idxType = formal->sym->type; range->remove(); } else { INT_FATAL("unresolved range"); } return idxType; }
void ResolutionCandidate::resolveTypeConstructor(CallInfo& info) { SET_LINENO(fn); // Ignore tuple constructors; they were generated // with their type constructors. if (fn->hasFlag(FLAG_PARTIAL_TUPLE) == false) { CallExpr* typeConstructorCall = new CallExpr(astr("_type", fn->name)); for_formals(formal, fn) { if (formal->hasFlag(FLAG_IS_MEME) == false) { if (fn->_this->type->symbol->hasFlag(FLAG_TUPLE)) { if (formal->instantiatedFrom != NULL) { typeConstructorCall->insertAtTail(formal->type->symbol); } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) { typeConstructorCall->insertAtTail(paramMap.get(formal)); } } else { if (strcmp(formal->name, "outer") == 0 || formal->type == dtMethodToken) { typeConstructorCall->insertAtTail(formal); } else if (formal->instantiatedFrom != NULL) { SymExpr* se = new SymExpr(formal->type->symbol); NamedExpr* ne = new NamedExpr(formal->name, se); typeConstructorCall->insertAtTail(ne); } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) { SymExpr* se = new SymExpr(paramMap.get(formal)); NamedExpr* ne = new NamedExpr(formal->name, se); typeConstructorCall->insertAtTail(ne); } } } } info.call->insertBefore(typeConstructorCall); // If instead we call resolveCallAndCallee(typeConstructorCall) // then the line number reported in an error would change // e.g.: domains/deitz/test_generic_class_of_sparse_domain // or: classes/diten/multipledestructor resolveCall(typeConstructorCall); INT_ASSERT(typeConstructorCall->isResolved()); resolveFunction(typeConstructorCall->resolvedFunction()); fn->_this->type = typeConstructorCall->resolvedFunction()->retType; typeConstructorCall->remove(); }
void printCallStackCalls() { printf("\n" "callStack %d elms\n\n", callStack.n); for (int i = 0; i < callStack.n; i++) { CallExpr* call = callStack.v[i]; FnSymbol* cfn = call->isResolved(); printf("%d %d %s <- %d %s\n", i, cfn ? cfn->id : 0, cfn ? cfn->name: "<no callee>", call ? call->id : 0, call ? call->stringLoc() : "<no call>"); } printf("\n"); }
// // 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; }
// // 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; }