static ArgSymbol* addOutErrorArg(FnSymbol* fn) { ArgSymbol* outError = NULL; SET_LINENO(fn); outError = new ArgSymbol(INTENT_REF, "error_out", dtError); outError->addFlag(FLAG_ERROR_VARIABLE); fn->insertFormalAtTail(outError); return outError; }
ArgSymbol* ReturnByRef::addFormal(FnSymbol* fn) { SET_LINENO(fn); Type* type = fn->retType; AggregateType* refType = type->refType; IntentTag intent = blankIntentForType(refType); // Note: other code does strcmps against the name _retArg ArgSymbol* formal = new ArgSymbol(intent, "_retArg", refType); formal->addFlag(FLAG_RETARG); fn->insertFormalAtTail(formal); fn->addFlag(FLAG_FN_RETARG); return formal; }
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; }
// // 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; }
// 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; }
void IpeEnv::describe(const char* pad, int index, LcnSymbol* var) const { const char* symName = var->name; const char* typeName = ""; ArgSymbol* argSym = toArgSymbol(var); VarSymbol* varSym = toVarSymbol(var); if (var->type) typeName = var->type->symbol->name; printf("%s %5d: %-30s", pad, index, symName); if (argSym != NULL) { if (argSym->intent & INTENT_REF) printf("ref %-12s", typeName); else printf("arg %-12s", typeName); } else if (varSym->immediate != NULL) printf("const %-12s", typeName); else if (varSym->hasFlag(FLAG_CONST) == true) printf("const %-12s", typeName); else if (varSym->hasFlag(FLAG_PARAM) == true) printf("param %-12s", typeName); else printf("var %-12s", typeName); if (var->depth() >= 0 && var->offset() >= 0) printf("%4d %4d ", var->depth(), var->offset()); else printf(" "); if (argSym != NULL && (argSym->intent & INTENT_REF) != 0) { if (mFrameData != NULL) { int offset = argSym->offset(); IpeValue* ref = *((IpeValue**) (((char*) mFrameData) + offset)); printf("0x%012lX", (long) ref); } } else if (var->offset() < 0) ; else if (var->type == NULL) ; else if (var->type == gIpeTypeType) printf("%s", symName); else if (var->type == dtBool) { if (mDepth == 0 || mFrameData != NULL) printf("%s", (fetchBool(var) == true) ? "true" : "false"); } else if (var->type == dtInt[INT_SIZE_64]) { if (mDepth == 0 || mFrameData != NULL) printf("%8ld", fetchInteger(var)); } else if (var->type == dtReal[FLOAT_SIZE_64]) { if (mDepth == 0 || mFrameData != NULL) printf(" %8.2f", fetchReal(var)); } else if (var->type == gIpeTypeModule) { if (mDepth == 0 || mFrameData != NULL) { IpeModule* value = (IpeModule*) fetchPtr(var); printf("#<IpeModule %-20s 0x%012lX>", value->name(), (long) value); } } else if (var->type == gIpeTypeProcedure) { if (mDepth == 0 || mFrameData != NULL) { IpeProcedure* value = (IpeProcedure*) fetchPtr(var); if (value->methodCount() == 1) printf("#<IpeProcedure %-20s with %3d method 0x%012lX>", value->name(), value->methodCount(), (long) value); else printf("#<IpeProcedure %-20s with %3d methods 0x%012lX>", value->name(), value->methodCount(), (long) value); } } else printf("Really?? Really??"); printf("\n"); }
// 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; }