static IntentTag constIntentForType(Type* t) { if (isSyncType(t) || isRecordWrappedType(t) || // domain, array, or distribution isRecord(t) || // may eventually want to decide based on size is_string_type(t)) { return INTENT_CONST_REF; } else if (is_bool_type(t) || is_int_type(t) || is_uint_type(t) || is_real_type(t) || is_imag_type(t) || is_complex_type(t) || is_enum_type(t) || isClass(t) || isUnion(t) || isAtomicType(t) || t == dtOpaque || t == dtTaskID || t == dtFile || t == dtTaskList || t == dtNil || t == dtStringC || t == dtStringCopy || t->symbol->hasFlag(FLAG_EXTERN)) { return INTENT_CONST_IN; } INT_FATAL(t, "Unhandled type in constIntentForType()"); return INTENT_CONST; }
void buildDefaultFunctions() { build_chpl_entry_points(); SET_LINENO(rootModule); // todo - remove reset_ast_loc() calls below? std::vector<BaseAST*> asts; collect_asts(rootModule, asts); for_vector(BaseAST, ast, asts) { if (TypeSymbol* type = toTypeSymbol(ast)) { // Here we build default functions that are always generated (even when // the type symbol has FLAG_NO_DEFAULT_FUNCTIONS attached). if (AggregateType* ct = toAggregateType(type->type)) { buildFieldAccessorFunctions(ct); if (!ct->symbol->hasFlag(FLAG_REF)) buildDefaultDestructor(ct); // Classes should use the nil:<type> _defaultOf method unless they // do not inherit from object. For those types and records, call // we need a more complicated _defaultOf method generated by the // compiler if (!ct->isClass() || ct->symbol->hasFlag(FLAG_NO_OBJECT)) build_record_init_function(ct); } if (type->hasFlag(FLAG_NO_DEFAULT_FUNCTIONS)) continue; // Here we build default functions that respect the "no default // functions" pragma. if (AggregateType* ct = toAggregateType(type->type)) { buildDefaultReadWriteFunctions(ct); if (isRecord(ct)) { if (!isRecordWrappedType(ct)) { build_record_equality_function(ct); build_record_inequality_function(ct); } build_record_assignment_function(ct); build_record_cast_function(ct); build_record_copy_function(ct); build_record_hash_function(ct); } if (isUnion(ct)) build_union_assignment_function(ct); } else if (EnumType* et = toEnumType(type->type)) { //buildDefaultReadFunction(et); buildStringCastFunction(et); build_enum_cast_function(et); build_enum_assignment_function(et); build_enum_first_function(et); build_enum_enumerate_function(et); } else { // The type is a simple type. // Other simple types are handled explicitly in the module code. // But to avoid putting a catch-all case there to implement assignment // for extern types that are simple (as far as we can tell), we build // definitions for those assignments here. if (type->hasFlag(FLAG_EXTERN)) { build_extern_init_function(type->type); build_extern_assignment_function(type->type); } } } } }
// 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; }