Exemple #1
0
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;
}