// // Do a breadth first search starting from functions generated for local blocks // for all function calls in each level of the search, if they directly cause // communication, add a local temp that isn't wide. If it is a resolved call, // meaning that it isn't a primitive or external function, clone it and add it // to the queue of functions to handle at the next iteration of the BFS. // static void handleLocalBlocks() { Map<FnSymbol*,FnSymbol*> cache; // cache of localized functions Vec<BlockStmt*> queue; // queue of blocks to localize forv_Vec(BlockStmt, block, gBlockStmts) { if (block->parentSymbol) { // NOAKES 2014/11/25 Transitional. Avoid calling blockInfoGet() if (block->isLoopStmt() == true) { } else if (block->blockInfoGet()) { if (block->blockInfoGet()->isPrimitive(PRIM_BLOCK_LOCAL)) { queue.add(block); } } } } forv_Vec(BlockStmt, block, queue) { std::vector<CallExpr*> calls; collectCallExprs(block, calls); for_vector(CallExpr, call, calls) { localizeCall(call); if (FnSymbol* fn = call->isResolved()) { SET_LINENO(fn); if (FnSymbol* alreadyLocal = cache.get(fn)) { call->baseExpr->replace(new SymExpr(alreadyLocal)); } else { if (!fn->hasFlag(FLAG_EXTERN)) { FnSymbol* local = fn->copy(); local->addFlag(FLAG_LOCAL_FN); local->name = astr("_local_", fn->name); local->cname = astr("_local_", fn->cname); fn->defPoint->insertBefore(new DefExpr(local)); call->baseExpr->replace(new SymExpr(local)); queue.add(local->body); cache.put(fn, local); cache.put(local, local); // to handle recursion if (local->retType->symbol->hasFlag(FLAG_WIDE_REF)) { CallExpr* ret = toCallExpr(local->body->body.tail); INT_ASSERT(ret && ret->isPrimitive(PRIM_RETURN)); // Capture the return expression in a local temp. insertLocalTemp(ret->get(1)); local->retType = ret->get(1)->typeInfo(); } } } } }
// // Consider a function that takes a formal of type Record by const ref // and that returns that value from the function. The compiler inserts // a PRIM_MOVE operation. // // This work-around inserts an autoCopy to compensate // void ReturnByRef::updateAssignmentsFromRefArgToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* lhs = toSymExpr(move->get(1)); SymExpr* rhs = toSymExpr(move->get(2)); if (lhs != NULL && rhs != NULL) { VarSymbol* symLhs = toVarSymbol(lhs->symbol()); ArgSymbol* symRhs = toArgSymbol(rhs->symbol()); if (symLhs != NULL && symRhs != NULL) { if (isUserDefinedRecord(symLhs->type) == true && symRhs->type == symLhs->type) { if (symLhs->hasFlag(FLAG_ARG_THIS) == false && (symRhs->intent == INTENT_REF || symRhs->intent == INTENT_CONST_REF)) { SET_LINENO(move); CallExpr* autoCopy = NULL; rhs->remove(); autoCopy = new CallExpr(autoCopyMap.get(symRhs->type), rhs); move->insertAtTail(autoCopy); } } } } } } }
void ReturnByRef::updateAssignmentsFromRefTypeToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(fn, defMap, useMap); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* symLhs = toSymExpr (move->get(1)); CallExpr* callRhs = toCallExpr(move->get(2)); if (symLhs && callRhs && callRhs->isPrimitive(PRIM_DEREF)) { VarSymbol* varLhs = toVarSymbol(symLhs->symbol()); SymExpr* symRhs = toSymExpr(callRhs->get(1)); VarSymbol* varRhs = toVarSymbol(symRhs->symbol()); // MPF 2016-10-02: It seems to me that this code should also handle the // case that symRhs is an ArgSymbol, but adding that caused problems // in the handling of out argument intents. if (varLhs != NULL && varRhs != NULL) { if (isUserDefinedRecord(varLhs->type) == true && varRhs->type == varLhs->type->refType) { // HARSHBARGER 2015-12-11: // `init_untyped_var` in the `normalize` pass may insert an // initCopy, which means that we should not insert an autocopy // for that same variable. bool initCopied = false; for_uses(use, useMap, varLhs) { if (CallExpr* call = toCallExpr(use->parentExpr)) { if (FnSymbol* parentFn = call->isResolved()) { if (parentFn->hasFlag(FLAG_INIT_COPY_FN)) { initCopied = true; break; } } } } if (!initCopied) { SET_LINENO(move); SymExpr* lhsCopy0 = symLhs->copy(); SymExpr* lhsCopy1 = symLhs->copy(); FnSymbol* autoCopy = autoCopyMap.get(varLhs->type); CallExpr* copyExpr = new CallExpr(autoCopy, lhsCopy0); CallExpr* moveExpr = new CallExpr(PRIM_MOVE,lhsCopy1, copyExpr); move->insertAfter(moveExpr); } } } }
static bool markFastSafeFn(FnSymbol *fn, int recurse, Vec<FnSymbol*> *visited) { if (fn->hasFlag(FLAG_FAST_ON)) return true; if (fn->hasFlag(FLAG_EXPORT)) return true; if (fn->hasFlag(FLAG_EXTERN)) { // consider a pragma to indicate that it would be "fast" return false; } if (fn->hasFlag(FLAG_NON_BLOCKING)) return false; visited->add_exclusive(fn); std::vector<CallExpr*> calls; collectCallExprs(fn, calls); for_vector(CallExpr, call, calls) { DEBUG_PRINTF("\tcall %p (id=%d): ", call, call->id); bool isLocal = fn->hasFlag(FLAG_LOCAL_FN) || inLocalBlock(call); if (!call->primitive) { DEBUG_PRINTF("(non-primitive CALL)\n"); if ((recurse>0) && call->isResolved()) { if (call->isResolved()->hasFlag(FLAG_ON_BLOCK)) { visited->add_exclusive(call->isResolved()); call->isResolved()->removeFlag(FLAG_FAST_ON); DEBUG_PRINTF("%d: recurse FAILED (nested on block, id=%d).\n", recurse-1, call->id); return false; } if (!visited->in(call->isResolved())) { DEBUG_PRINTF("%d: recurse %p (block=%p, id=%d)\n", recurse-1, call->isResolved(), call->isResolved()->body, call->isResolved()->id); DEBUG_PRINTF("\tlength=%d\n", call->isResolved()->body->length()); if (!markFastSafeFn(call->isResolved(), recurse-1, visited)) { DEBUG_PRINTF("%d: recurse FAILED (id=%d).\n", recurse-1, call->id); return false; } } else { DEBUG_PRINTF("%d: recurse ALREADY VISITED %p (id=%d)\n", recurse-1, call->isResolved(), call->isResolved()->id); DEBUG_PRINTF("\tlength=%d\n", call->isResolved()->body->length()); } DEBUG_PRINTF("%d: recurse DONE.\n", recurse-1); } else { // No function calls allowed DEBUG_PRINTF("%d: recurse FAILED (%s, id=%d).\n", recurse-1, recurse == 1 ? "too deep" : "function not resolved", call->id); return false; } } else if (isFastPrimitive(call, isLocal)) { DEBUG_PRINTF(" (FAST primitive CALL)\n"); } else { DEBUG_PRINTF("%d: FAILED (non-FAST primitive CALL: %s, id=%d)\n", recurse-1, call->primitive->name, call->id); return false; } }
static int markFastSafeFn(FnSymbol *fn, int recurse, std::set<FnSymbol*>& visited) { // First, handle functions we've already visited. if (visited.count(fn) != 0) { if (fn->hasFlag(FLAG_FAST_ON)) return FAST_AND_LOCAL; if (fn->hasFlag(FLAG_LOCAL_FN)) return LOCAL_NOT_FAST; return NOT_FAST_NOT_LOCAL; } // Now, add fn to the set of visited functions, // since we will categorize it now. visited.insert(fn); // Next, classify extern functions if (fn->hasFlag(FLAG_EXTERN)) { if (fn->hasFlag(FLAG_FAST_ON_SAFE_EXTERN)) { // Make sure the FAST_ON and LOCAL_FN flags are set. fn->addFlag(FLAG_FAST_ON); fn->addFlag(FLAG_LOCAL_FN); return FAST_AND_LOCAL; } else if(fn->hasFlag(FLAG_LOCAL_FN)) { return LOCAL_NOT_FAST; } else { // Other extern functions are not fast or local. return NOT_FAST_NOT_LOCAL; } } // Next, go through function bodies. // We will set maybefast=false if we see something in the function // that is local but not suitable for a signal handler // (mostly allocation or locking). // We will return NOT_FAST_NOT_LOCAL immediately if we see something // in the function that is not local. bool maybefast = true; if (fn->hasFlag(FLAG_NON_BLOCKING)) maybefast = false; std::vector<CallExpr*> calls; collectCallExprs(fn, calls); for_vector(CallExpr, call, calls) { bool inLocal = fn->hasFlag(FLAG_LOCAL_FN) || inLocalBlock(call); if (call->primitive) { int is = classifyPrimitive(call, inLocal); if (!isLocal(is)) { // FAST_NOT_LOCAL or NOT_FAST_NOT_LOCAL return NOT_FAST_NOT_LOCAL; } // is == FAST_AND_LOCAL requires no action if (is == LOCAL_NOT_FAST) { maybefast = false; } } else { if (recurse<=0 || !call->isResolved()) { // didn't resolve or past too much recursion. // No function calls allowed return NOT_FAST_NOT_LOCAL; } else { // Handle nested 'on' statements if (call->resolvedFunction()->hasFlag(FLAG_ON_BLOCK)) { if (inLocal) { maybefast = false; } else { return NOT_FAST_NOT_LOCAL; } } // is the call to a fast/local function? int is = markFastSafeFn(call->resolvedFunction(), recurse - 1, visited); // Remove NOT_LOCAL parts if it's in a local block is = setLocal(is, inLocal); if (!isLocal(is)) { return NOT_FAST_NOT_LOCAL; } if (is == LOCAL_NOT_FAST) { maybefast = false; } // otherwise, possibly still fast. } } }