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. } } }