void ReturnByRef::returnByRefCollectCalls(RefMap& calls, FnSet& fns) { RefMap::iterator iter; forv_Vec(CallExpr, call, gCallExprs) { // Only transform calls that are still in the AST tree // (defer statement bodies have been removed at this point // in this pass) if (call->inTree()) { // Only transform calls to transformable functions // The common case is a user-level call to a resolved function // Also handle the PRIMOP for a virtual method call if (FnSymbol* fn = call->resolvedOrVirtualFunction()) { TransformationKind tfKind = transformableFunctionKind(fn); if (tfKind == TF_FULL) { RefMap::iterator iter = calls.find(fn->id); ReturnByRef* info = NULL; if (iter == calls.end()) { info = new ReturnByRef(fn); calls[fn->id] = info; } else { info = iter->second; } info->addCall(call); } else if (tfKind == TF_ASGN) { fns.insert(fn); } } } } }
// The argument is assumed to be an assignment function. // It is deemed to be simple if it contains only 'move', 'addr of', '=', // 'return' primitives and calls only simple assignments (recursively). // We also require that it return void (for now). // The FLAG_TRIVIAL_ASSIGNMENT is used to ensure that we visit each such // function only once. static bool isTrivial(FnSymbol* fn, FnSet& visited) { // Visit each function only once. if (visited.find(fn) != visited.end()) { // Found it. if (fn->hasFlag(FLAG_TRIVIAL_ASSIGNMENT)) return true; else return false; } visited.insert(fn); // We assume that compiler-generated assignments are field-by-field copies. // Note that this overloads the meaning of this flag with: // "Assignment operations flagged as 'compiler generated' shall contain only // field assignments and assignment primitives." // We can then assume that all nested calls are field extractions or // assignments, and we only have to check the latter. if (! fn->hasFlag(FLAG_COMPILER_GENERATED)) return false; // After all compiler-supplied assignments use the new signature, this can // become an assert. if (fn->retType != dtVoid) return false; // The base argument types must match. ArgSymbol* lhs = fn->getFormal(1); ArgSymbol* rhs = fn->getFormal(2); if (lhs->type->getValType() != rhs->type->getValType()) return false; // Assume this is a field-by-field copy. // If none of the fields of this record (or tuple) has a user-defined // assignment (recursively), then it can be replaced by a bulk copy. // Traverse the call expressions in the body of the function. // These should all be one of the allowed primitives or calls to simple // assignments. If anything else, the assignment is "interesting", and // cannot be replaced. std::vector<BaseAST*> asts; collect_asts_STL(fn->body, asts); for_vector(BaseAST, ast, asts) { if (CallExpr* call = toCallExpr(ast)) { if (FnSymbol* fieldAsgn = call->isResolved()) { if (isAssignment(fieldAsgn)) if (! isTrivial(fieldAsgn, visited)) return false; } else { // This is a primitive. switch(call->primitive->tag) { default: return false; // These are the allowable primitives. case PRIM_MOVE: case PRIM_ASSIGN: case PRIM_ADDR_OF: case PRIM_DEREF: case PRIM_GET_MEMBER: case PRIM_GET_MEMBER_VALUE: case PRIM_SET_MEMBER: case PRIM_RETURN: // We expect return _void. break; } } } } fn->addFlag(FLAG_TRIVIAL_ASSIGNMENT); return true; }