// // Return true if this primitive function is safe for fast on optimization // (e.g., no communication, no sync/single accesses) // static bool isFastPrimitive(CallExpr *call, bool isLocal) { INT_ASSERT(call->primitive); // Check primitives for communication switch (call->primitive->tag) { case PRIM_UNKNOWN: // TODO: Return true for PRIM_UNKNOWNs that are side-effect free return false; case PRIM_NOOP: case PRIM_REF_TO_STRING: case PRIM_RETURN: case PRIM_UNARY_MINUS: case PRIM_UNARY_PLUS: case PRIM_UNARY_NOT: case PRIM_UNARY_LNOT: case PRIM_ADD: case PRIM_SUBTRACT: case PRIM_MULT: case PRIM_DIV: case PRIM_MOD: case PRIM_LSH: case PRIM_RSH: case PRIM_EQUAL: case PRIM_NOTEQUAL: case PRIM_LESSOREQUAL: case PRIM_GREATEROREQUAL: case PRIM_LESS: case PRIM_GREATER: case PRIM_AND: case PRIM_OR: case PRIM_XOR: case PRIM_POW: case PRIM_MIN: case PRIM_MAX: case PRIM_GET_MEMBER: case PRIM_GET_SVEC_MEMBER: case PRIM_GET_PRIV_CLASS: case PRIM_NEW_PRIV_CLASS: case PRIM_CHECK_NIL: case PRIM_GET_REAL: case PRIM_GET_IMAG: case PRIM_ADDR_OF: case PRIM_LOCAL_CHECK: case PRIM_INIT_FIELDS: case PRIM_PTR_EQUAL: case PRIM_PTR_NOTEQUAL: case PRIM_CAST: case PRIM_BLOCK_LOCAL: case PRIM_ON_LOCALE_NUM: case PRIM_GET_SERIAL: case PRIM_SET_SERIAL: case PRIM_START_RMEM_FENCE: case PRIM_FINISH_RMEM_FENCE: case PRIM_STRING_COPY: case PRIM_C_STRING_FROM_STRING: case PRIM_CAST_TO_VOID_STAR: case PRIM_SIZEOF: case PRIM_GET_USER_LINE: case PRIM_GET_USER_FILE: DEBUG_PRINTF(" *** OK (default): %s\n", call->primitive->name); 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 (!isCallExpr(call->get(2))) { // callExprs checked in calling function if (!call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_REF) && !call->get(2)->typeInfo()->symbol->hasFlag(FLAG_WIDE_REF)) { DEBUG_PRINTF(" *** OK (PRIM_MOVE 1): %s\n", call->primitive->name); return true; } } else { DEBUG_PRINTF(" *** OK (PRIM_MOVE 2): %s\n", call->primitive->name); // Not necessarily true, but we return true because // the callExpr will be checked in the calling function return true; } break; // I think these can always return true. <hilde> // But that works only if the remote get is removed from code generation. case PRIM_WIDE_GET_LOCALE: case PRIM_WIDE_GET_NODE: case PRIM_WIDE_GET_ADDR: // If this test is true, a remote get is required. if (!(call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_REF) && call->get(1)->getValType()->symbol->hasFlag(FLAG_WIDE_CLASS))) { DEBUG_PRINTF(" *** OK (PRIM_WIDE_GET_LOCALE, etc.): %s\n", call->primitive->name); return true; } break; case PRIM_SET_UNION_ID: case PRIM_GET_UNION_ID: case PRIM_GET_MEMBER_VALUE: case PRIM_GET_SVEC_MEMBER_VALUE: if (!call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_REF)) { return true; DEBUG_PRINTF(" *** OK (PRIM_SET_UNION_ID, etc.): %s\n", call->primitive->name); } break; case PRIM_ARRAY_SET: case PRIM_ARRAY_SET_FIRST: case PRIM_SETCID: case PRIM_TESTCID: case PRIM_GETCID: case PRIM_ARRAY_GET: case PRIM_ARRAY_GET_VALUE: case PRIM_DYNAMIC_CAST: if (!call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_CLASS)) { DEBUG_PRINTF(" *** OK (PRIM_ARRAY_SET, etc.): %s\n", call->primitive->name); return true; } break; case PRIM_DEREF: case PRIM_SET_MEMBER: case PRIM_SET_SVEC_MEMBER: if (!call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_REF) && !call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_CLASS)) { DEBUG_PRINTF(" *** OK (PRIM_DEREF, etc.): %s\n", call->primitive->name); return true; } break; case PRIM_CHPL_COMM_GET: case PRIM_CHPL_COMM_PUT: case PRIM_CHPL_COMM_REMOTE_PREFETCH: case PRIM_CHPL_COMM_GET_STRD: case PRIM_CHPL_COMM_PUT_STRD: // These may involve communication, so are deemed slow. return false; case PRIM_SYNC_INIT: // Maybe fast? case PRIM_SYNC_DESTROY: // Maybe fast? case PRIM_SYNC_LOCK: case PRIM_SYNC_UNLOCK: case PRIM_SYNC_WAIT_FULL: case PRIM_SYNC_WAIT_EMPTY: case PRIM_SYNC_SIGNAL_FULL: case PRIM_SYNC_SIGNAL_EMPTY: case PRIM_SINGLE_INIT: // Maybe fast? case PRIM_SINGLE_DESTROY: // Maybe fast? case PRIM_SINGLE_LOCK: case PRIM_SINGLE_UNLOCK: case PRIM_SINGLE_WAIT_FULL: case PRIM_SINGLE_SIGNAL_FULL: case PRIM_WRITEEF: case PRIM_WRITEFF: case PRIM_WRITEXF: case PRIM_READFE: case PRIM_READFF: case PRIM_READXX: case PRIM_SYNC_IS_FULL: case PRIM_SINGLE_WRITEEF: case PRIM_SINGLE_READFF: case PRIM_SINGLE_READXX: case PRIM_SINGLE_IS_FULL: // These may block, so are deemed slow. return false; case PRIM_NEW: case PRIM_INIT: case PRIM_NO_INIT: case PRIM_TYPE_INIT: case PRIM_LOGICAL_FOLDER: case PRIM_TYPEOF: case PRIM_TYPE_TO_STRING: case PRIM_ENUM_MIN_BITS: case PRIM_ENUM_IS_SIGNED: case PRIM_IS_UNION_TYPE: case PRIM_IS_ATOMIC_TYPE: case PRIM_IS_SYNC_TYPE: case PRIM_IS_SINGLE_TYPE: case PRIM_IS_TUPLE_TYPE: case PRIM_IS_STAR_TUPLE_TYPE: case PRIM_IS_SUBTYPE: case PRIM_TUPLE_EXPAND: case PRIM_TUPLE_AND_EXPAND: case PRIM_QUERY: case PRIM_QUERY_PARAM_FIELD: case PRIM_QUERY_TYPE_FIELD: case PRIM_ERROR: case PRIM_WARNING: case PRIM_BLOCK_PARAM_LOOP: case PRIM_BLOCK_BEGIN: case PRIM_BLOCK_COBEGIN: case PRIM_BLOCK_COFORALL: case PRIM_BLOCK_ON: case PRIM_BLOCK_BEGIN_ON: case PRIM_BLOCK_COBEGIN_ON: case PRIM_BLOCK_COFORALL_ON: case PRIM_BLOCK_UNLOCAL: case PRIM_ACTUALS_LIST: case PRIM_YIELD: case PRIM_USE: case PRIM_USED_MODULES_LIST: case PRIM_WHEN: case PRIM_INT_ERROR: case PRIM_CAPTURE_FN: case PRIM_CREATE_FN_TYPE: case PRIM_NUM_FIELDS: case PRIM_FIELD_NUM_TO_NAME: case PRIM_FIELD_VALUE_BY_NUM: case PRIM_FIELD_ID_BY_NUM: case PRIM_FIELD_VALUE_BY_NAME: INT_FATAL("This primitive should have been removed from the tree by now."); break; // By themselves, loops are considered "fast". case PRIM_BLOCK_WHILEDO_LOOP: case PRIM_BLOCK_DOWHILE_LOOP: case PRIM_BLOCK_FOR_LOOP: case PRIM_BLOCK_C_FOR_LOOP: return true; // These don't block in the Chapel sense, but they may require a system // call so we don't consider them eligible. // case PRIM_FREE_TASK_LIST: case PRIM_ARRAY_ALLOC: case PRIM_ARRAY_FREE: case PRIM_ARRAY_FREE_ELTS: case PRIM_STRING_FROM_C_STRING: return false; // Temporarily unclassified (legacy) cases. // These formerly defaulted to false (slow), so we leave them // here until they are proven fast. case PRIM_GET_END_COUNT: case PRIM_SET_END_COUNT: case PRIM_PROCESS_TASK_LIST: case PRIM_EXECUTE_TASKS_IN_LIST: case PRIM_TO_LEADER: case PRIM_TO_FOLLOWER: case PRIM_DELETE: case PRIM_CALL_DESTRUCTOR: case PRIM_HEAP_REGISTER_GLOBAL_VAR: case PRIM_HEAP_BROADCAST_GLOBAL_VARS: case PRIM_PRIVATE_BROADCAST: case PRIM_RT_ERROR: case PRIM_RT_WARNING: case PRIM_FTABLE_CALL: case PRIM_VIRTUAL_METHOD_CALL: return false; default: INT_FATAL("Unhandled case."); break; } return isLocal; }
// Returns max local frame space to evaluate this expr int locationExpr(Expr* expr, IpeEnv* env) { int retval = 0; if (DefExpr* defExpr = toDefExpr(expr)) { VarSymbol* var = toVarSymbol(defExpr->sym); int delta = 8; // NOAKES Size of every type is currently 8 INT_ASSERT(var); env->locationSet(var); retval = delta; } else if (isCallExpr(expr) == true) retval = 0; else if (CondStmt* stmt = toCondStmt(expr)) { if (stmt->elseStmt == NULL) { retval = locationExpr(stmt->thenStmt, env); } else { int thenSize = locationExpr(stmt->thenStmt, env); int elseSize = locationExpr(stmt->elseStmt, env); retval = (thenSize > elseSize) ? thenSize : elseSize; } } else if (WhileDoStmt* stmt = toWhileDoStmt(expr)) { Expr* body = stmt->body.get(1); INT_ASSERT(stmt->body.length == 1); INT_ASSERT(isBlockStmt(body)); retval = locationExpr(body, env); } else if (BlockStmt* stmt = toBlockStmt(expr)) { IpeBlockStmt* ipeStmt = (IpeBlockStmt*) stmt; int maxFrame = 0; IpeEnv env(ipeStmt->scopeGet()); for (int i = 1; i <= ipeStmt->body.length; i++) { int localSize = locationExpr(ipeStmt->body.get(i), &env); if (localSize > maxFrame) maxFrame = localSize; } retval = maxFrame; } else { AstDumpToNode logger(stdout, 3); printf(" locationExpr(Expr*, IpeEnv* env) unsupported\n"); printf(" "); expr->accept(&logger); printf("\n\n"); env->describe(3); printf("\n\n"); INT_ASSERT(false); } return retval; }
// // Return NOT_FAST, NOT_LOCAL, IS_LOCAL, or IS_FAST. // static int classifyPrimitive(CallExpr *call) { INT_ASSERT(call->primitive); // Check primitives for suitability for executeOnFast and for communication switch (call->primitive->tag) { case PRIM_UNKNOWN: // TODO: Return FAST_AND_LOCAL for PRIM_UNKNOWNs that are side-effect free return NOT_FAST_NOT_LOCAL; case PRIM_NOOP: case PRIM_REF_TO_STRING: case PRIM_RETURN: case PRIM_UNARY_MINUS: case PRIM_UNARY_PLUS: case PRIM_UNARY_NOT: case PRIM_UNARY_LNOT: case PRIM_ADD: case PRIM_SUBTRACT: case PRIM_MULT: case PRIM_DIV: case PRIM_MOD: case PRIM_LSH: case PRIM_RSH: case PRIM_EQUAL: case PRIM_NOTEQUAL: case PRIM_LESSOREQUAL: case PRIM_GREATEROREQUAL: case PRIM_LESS: case PRIM_GREATER: case PRIM_AND: case PRIM_OR: case PRIM_XOR: case PRIM_POW: case PRIM_MIN: case PRIM_MAX: case PRIM_GET_MEMBER: case PRIM_GET_SVEC_MEMBER: case PRIM_NEW_PRIV_CLASS: case PRIM_CHECK_NIL: case PRIM_GET_REAL: case PRIM_GET_IMAG: case PRIM_ADDR_OF: case PRIM_SET_REFERENCE: case PRIM_LOCAL_CHECK: case PRIM_INIT_FIELDS: case PRIM_PTR_EQUAL: case PRIM_PTR_NOTEQUAL: case PRIM_CAST: case PRIM_BLOCK_LOCAL: case PRIM_ON_LOCALE_NUM: case PRIM_GET_SERIAL: case PRIM_SET_SERIAL: case PRIM_START_RMEM_FENCE: case PRIM_FINISH_RMEM_FENCE: case PRIM_CAST_TO_VOID_STAR: case PRIM_SIZEOF_BUNDLE: case PRIM_SIZEOF_DDATA_ELEMENT: case PRIM_GET_USER_LINE: case PRIM_GET_USER_FILE: case PRIM_LOOKUP_FILENAME: case PRIM_STACK_ALLOCATE_CLASS: case PRIM_CLASS_NAME_BY_ID: case PRIM_INVARIANT_START: case PRIM_NO_ALIAS_SET: case PRIM_COPIES_NO_ALIAS_SET: return FAST_AND_LOCAL; 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 (isCallExpr(call->get(2))) { // callExprs checked in calling function // Not necessarily true, but we return true because // the callExpr will be checked in the calling function return FAST_AND_LOCAL; } else { bool arg1wide = call->get(1)->isWideRef(); bool arg2wide = call->get(2)->isWideRef(); // If neither argument is a wide reference, OK: no communication if (!arg1wide && !arg2wide) { return FAST_AND_LOCAL; } if (call->isPrimitive(PRIM_MOVE)) { bool arg1ref = call->get(1)->isRef(); bool arg2ref = call->get(2)->isRef(); // Handle (move tmp:ref, other_tmp:wide_ref) // and (move tmp:wide_ref, other_tmp:ref) // these does not require communication and merely adjust // the wideness of the ref. if ((arg1wide && arg2ref) || (arg1ref && arg2wide)) { return FAST_AND_LOCAL; } } // Otherwise, communication is required if we're not in a local block return FAST_NOT_LOCAL; } case PRIM_WIDE_MAKE: return FAST_NOT_LOCAL; // I think these can always return true. <hilde> // But that works only if the remote get is removed from code generation. case PRIM_WIDE_GET_LOCALE: case PRIM_WIDE_GET_NODE: case PRIM_WIDE_GET_ADDR: // If this test is true, a remote get is required. if (!(call->get(1)->isWideRef() && call->get(1)->getValType()->symbol->hasFlag(FLAG_WIDE_CLASS))) { return FAST_AND_LOCAL; } return FAST_NOT_LOCAL; case PRIM_ARRAY_SHIFT_BASE_POINTER: // SHIFT_BASE_POINTER is fast as long as none of the // arguments are wide references. if (call->get(1)->isWideRef() || call->get(2)->isWideRef() || call->get(3)->isWideRef()) return FAST_NOT_LOCAL; else return FAST_AND_LOCAL; case PRIM_SET_UNION_ID: case PRIM_GET_UNION_ID: case PRIM_GET_MEMBER_VALUE: case PRIM_GET_SVEC_MEMBER_VALUE: if (!call->get(1)->isWideRef()) { return FAST_AND_LOCAL; } return FAST_NOT_LOCAL; case PRIM_ARRAY_SET: case PRIM_ARRAY_SET_FIRST: case PRIM_SETCID: case PRIM_TESTCID: case PRIM_GETCID: case PRIM_ARRAY_GET: case PRIM_ARRAY_GET_VALUE: case PRIM_DYNAMIC_CAST: if (!call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_CLASS)) { return FAST_AND_LOCAL; } return FAST_NOT_LOCAL; case PRIM_DEREF: case PRIM_SET_MEMBER: case PRIM_SET_SVEC_MEMBER: if (!call->get(1)->isWideRef() && !call->get(1)->typeInfo()->symbol->hasFlag(FLAG_WIDE_CLASS)) { return FAST_AND_LOCAL; } return FAST_NOT_LOCAL; case PRIM_CHPL_COMM_GET: case PRIM_CHPL_COMM_BUFF_GET: case PRIM_CHPL_COMM_PUT: case PRIM_CHPL_COMM_ARRAY_GET: case PRIM_CHPL_COMM_ARRAY_PUT: case PRIM_CHPL_COMM_REMOTE_PREFETCH: case PRIM_CHPL_COMM_GET_STRD: case PRIM_CHPL_COMM_PUT_STRD: // These involve communication // MPF: Couldn't these be fast if in a local block? // Shouldn't this be return FAST_NOT_LOCAL ? return NOT_FAST_NOT_LOCAL; case PRIM_REDUCE_ASSIGN: case PRIM_NEW: case PRIM_INIT: case PRIM_INIT_FIELD: case PRIM_INIT_VAR: case PRIM_NO_INIT: case PRIM_TYPE_INIT: case PRIM_LOGICAL_FOLDER: case PRIM_LIFETIME_OF: case PRIM_TYPEOF: case PRIM_STATIC_TYPEOF: case PRIM_SCALAR_PROMOTION_TYPE: case PRIM_STATIC_FIELD_TYPE: case PRIM_TYPE_TO_STRING: case PRIM_IS_CLASS_TYPE: case PRIM_IS_RECORD_TYPE: case PRIM_IS_UNION_TYPE: case PRIM_IS_ATOMIC_TYPE: case PRIM_IS_EXTERN_TYPE: case PRIM_IS_TUPLE_TYPE: case PRIM_IS_STAR_TUPLE_TYPE: case PRIM_IS_SUBTYPE: case PRIM_IS_SUBTYPE_ALLOW_VALUES: case PRIM_IS_PROPER_SUBTYPE: case PRIM_IS_WIDE_PTR: case PRIM_TUPLE_EXPAND: case PRIM_QUERY: case PRIM_QUERY_PARAM_FIELD: case PRIM_QUERY_TYPE_FIELD: case PRIM_ERROR: case PRIM_WARNING: case PRIM_BLOCK_PARAM_LOOP: case PRIM_BLOCK_BEGIN: case PRIM_BLOCK_COBEGIN: case PRIM_BLOCK_COFORALL: case PRIM_BLOCK_ON: case PRIM_BLOCK_BEGIN_ON: case PRIM_BLOCK_COBEGIN_ON: case PRIM_BLOCK_COFORALL_ON: case PRIM_BLOCK_UNLOCAL: case PRIM_ACTUALS_LIST: case PRIM_YIELD: case PRIM_USED_MODULES_LIST: case PRIM_WHEN: case PRIM_CAPTURE_FN_FOR_C: case PRIM_CAPTURE_FN_FOR_CHPL: case PRIM_CREATE_FN_TYPE: case PRIM_NUM_FIELDS: case PRIM_IS_POD: case PRIM_FIELD_NUM_TO_NAME: case PRIM_FIELD_NAME_TO_NUM: case PRIM_FIELD_BY_NUM: case PRIM_TO_STANDALONE: case PRIM_IS_REF_ITER_TYPE: case PRIM_COERCE: case PRIM_CALL_RESOLVES: case PRIM_METHOD_CALL_RESOLVES: case PRIM_GET_COMPILER_VAR: case PRIM_ZIP: case PRIM_REQUIRE: case NUM_KNOWN_PRIMS: case PRIM_ITERATOR_RECORD_FIELD_VALUE_BY_FORMAL: case PRIM_ITERATOR_RECORD_SET_SHAPE: case PRIM_THROW: case PRIM_TRY_EXPR: case PRIM_TRYBANG_EXPR: case PRIM_CHECK_ERROR: case PRIM_TO_UNMANAGED_CLASS: case PRIM_TO_BORROWED_CLASS: case PRIM_NEEDS_AUTO_DESTROY: case PRIM_AUTO_DESTROY_RUNTIME_TYPE: case PRIM_GET_RUNTIME_TYPE_FIELD: INT_FATAL("This primitive should have been removed from the tree by now."); break; // By themselves, loops are considered "fast". case PRIM_BLOCK_WHILEDO_LOOP: case PRIM_BLOCK_DOWHILE_LOOP: case PRIM_BLOCK_FOR_LOOP: case PRIM_BLOCK_C_FOR_LOOP: return FAST_AND_LOCAL; // These don't block in the Chapel sense, but they may require a system // call so we don't consider them fast-eligible. // However, they are communication free. // case PRIM_STRING_COPY: return LOCAL_NOT_FAST; case PRIM_GET_END_COUNT: case PRIM_SET_END_COUNT: case PRIM_GET_DYNAMIC_END_COUNT: case PRIM_SET_DYNAMIC_END_COUNT: return FAST_AND_LOCAL; // Temporarily unclassified (legacy) cases. // These formerly defaulted to false (slow), so we leave them // here until they are proven fast. case PRIM_TO_LEADER: case PRIM_TO_FOLLOWER: case PRIM_CALL_DESTRUCTOR: case PRIM_HEAP_REGISTER_GLOBAL_VAR: case PRIM_HEAP_BROADCAST_GLOBAL_VARS: case PRIM_PRIVATE_BROADCAST: case PRIM_RT_ERROR: case PRIM_RT_WARNING: case PRIM_FTABLE_CALL: case PRIM_VIRTUAL_METHOD_CALL: case PRIM_INT_ERROR: return NOT_FAST_NOT_LOCAL; // no default, so that it is usually a C compilation // error when a primitive is added but not included here. } // At the end of the switch statement. // We get here if there is an unhandled primitive. INT_FATAL("Unhandled case."); return NOT_FAST_NOT_LOCAL; }