// 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; }