Example #1
0
void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal)
{
  Expr*     returnPrim  = fn->body->body.tail;

  SET_LINENO(returnPrim);

  CallExpr* returnCall  = toCallExpr(returnPrim);
  Expr*     returnValue = returnCall->get(1)->remove();
  CallExpr* moveExpr    = new CallExpr(PRIM_ASSIGN, formal, returnValue);


  Expr* expr = returnPrim;
  // Walk backwards while the previous element is an autoDestroy call
  while (expr->prev != NULL) {
    bool stop = true;
    if (CallExpr* call = toCallExpr(expr->prev))
      if (FnSymbol* calledFn = call->isResolved())
        if (calledFn->hasFlag(FLAG_AUTO_DESTROY_FN))
          stop = false;

    if (stop) break;

    expr = expr->prev;
  }

  Expr* returnOrFirstAutoDestroy = expr;

  // Add the move to return before the first autoDestroy
  // At this point we could also invoke some other function
  // if that turns out to be necessary. It might well be
  // necessary in order to return array slices by value.
  returnOrFirstAutoDestroy->insertBefore(moveExpr);
}
Example #2
0
/*
 * Attempts to replace iteration over simple anonymous ranges with calls to
 * direct iterators that take low, high and stride as arguments. This is to
 * avoid the cost of constructing ranges, and if the stride is known at compile
 * time, provide a more optimized iterator that uses "<, <=, >, or >=" as the
 * relational operator.
 *
 * This is only meant to replace anonymous range iteration for "simple" bounded
 * ranges. Simple means it's a range of the form "low..high" or "low..high by
 * stride". Anything more complex is ignored with the thinking that this should
 * optimize the most common range iterators, but it could be expanded to handle
 * more cases.
 *
 * An alternative is to update scalar replacement of aggregates to work on
 * ranges, which should be able to achieve similar results as this optimization
 * while handling all ranges, including non-anonymous ranges.
 *
 * Will optimize things like:
 * - "for i in 1..10"
 * - "for i in 1..10+1"
 * - "var lo=1, hi=10;for i in lo..hi"
 * - "for i in 1..10 by 2"
 * - "for (i, j) in zip(1..10 by 2, 1..10 by -2)"
 * - "for (i, j) in zip(A, 1..10 by 2)" // will optimize range iter still
 * - "coforall i in 1..10 by 2"         // works for coforalls as well
 *
 * Will not optimize ranges like:
 * - "for in in (1..)"             // doesn't handle unbounded ranges
 * - "for i in 1..10 by 2 by 2"    // doesn't handle more than one by operator
 * - "for i in 1..10 align 2"      // doesn't handle align operator
 * - "for i in 1..#10"             // doesn't handle # operator
 * - "var r = 1..10"; for i in r"  // not an anonymous range
 * - "forall i in 1..10"           // does not get applied to foralls
 *
 * Note that this function is pretty fragile because it relies on names of
 * functions/iterators as well as the arguments and order of those
 * functions/iterators but there's not really a way around it this early in
 * compilation. If the iterator can't be replaced, it is left unchanged.
 */
static void tryToReplaceWithDirectRangeIterator(Expr* iteratorExpr)
{
  CallExpr* range = NULL;
  Expr* stride = NULL;
  if (CallExpr* call = toCallExpr(iteratorExpr))
  {
    // grab the stride if we have a strided range
    if (call->isNamed("chpl_by"))
    {
      range = toCallExpr(call->get(1)->copy());
      stride = toExpr(call->get(2)->copy());
    }
    // assume the call is the range (checked below) and set default stride
    else
    {
      range = call;
      stride = new SymExpr(new_IntSymbol(1));
    }
    // see if we're looking at a builder for a bounded range. the builder is
    // iteratable since range has these() iterators
    if (range && range->isNamed("chpl_build_bounded_range"))
    {
      // replace the range construction with a direct range iterator
      Expr* low = range->get(1)->copy();
      Expr* high = range->get(2)->copy();
      iteratorExpr->replace(new CallExpr("chpl_direct_range_iter", low, high, stride));
    }
  }
}
Example #3
0
  forv_Vec(DefExpr, def, gDefExprs) {
    if (toVarSymbol(def->sym)) {
      // The test for FLAG_TEMP allows compiler-generated (temporary) variables
      // to be declared without an explicit type or initializer expression.
      if ((!def->init || def->init->isNoInitExpr())
          && !def->exprType && !def->sym->hasFlag(FLAG_TEMP))
        if (isBlockStmt(def->parentExpr) && !isArgSymbol(def->parentSymbol))
          if (def->parentExpr != rootModule->block && def->parentExpr != stringLiteralModule->block)
            if (!def->sym->hasFlag(FLAG_INDEX_VAR))
              USR_FATAL_CONT(def->sym,
                             "Variable '%s' is not initialized or has no type",
                             def->sym->name);
    }

    //
    // This test checks to see if query domains (e.g., '[?D]') are
    // used in places other than formal argument type specifiers.
    //
    if (!isFnSymbol(def->parentSymbol)) {
      if (CallExpr* type = toCallExpr(def->exprType)) {
        if (type->isNamed("chpl__buildArrayRuntimeType")) {
          if (CallExpr* domainExpr = toCallExpr(type->get(1))) {
            DefExpr* queryDomain = toDefExpr(domainExpr->get(1));
            if (queryDomain) {
              USR_FATAL_CONT(queryDomain,
                             "Domain query expressions may currently only be used in formal argument types");
            }
          }
        }
      }
    }

    checkPrivateDecls(def);
  }
// Determines if the expression is in the header of a Loop expression
//
// After normalization, the conditional test for a WhileStmt is expressed as
// a SymExpr inside a primitive CallExpr.
//
// The init, test, incr clauses of a C-For loop are expressed as BlockStmts
// that contain the relevant expressions.  This function only returns true
// for the expressions inside the BlockStmt and not the BlockStmt itself
//
// TODO should this be updated to only look for exprs in the test segment that
// are conditional primitives?
//
static bool isInLoopHeader(Expr* expr) {
  bool retval = false;

  if (expr->parentExpr == NULL) {
    retval = false;

  } else if (CallExpr* call = toCallExpr(expr->parentExpr)) {
    if (call->isPrimitive(PRIM_BLOCK_WHILEDO_LOOP) ||
        call->isPrimitive(PRIM_BLOCK_DOWHILE_LOOP)) {
      retval = true;
    }

  } else if (expr->parentExpr->parentExpr == NULL) {
    retval = false;

  } else if (CallExpr* call = toCallExpr(expr->parentExpr->parentExpr)) {

    if (call->isPrimitive(PRIM_BLOCK_C_FOR_LOOP))
      retval = true;

  } else {
    retval = false;
  }

  return retval;
}
Example #5
0
void BlockStmt::replaceChild(Expr* old_ast, Expr* new_ast) {
  if (old_ast == blockInfo)
    blockInfo = toCallExpr(new_ast);
  else if (old_ast == modUses)
    modUses = toCallExpr(new_ast);
  else
    INT_FATAL(this, "Unexpected case in BlockStmt::replaceChild");
}
Example #6
0
// Insert pairs into available copies map
// Also, record references when they are created.
static void extractReferences(Expr* expr,
                              RefMap& refs)
{
  // We're only interested in call expressions.
  if (CallExpr* call = toCallExpr(expr))
  {
    // Only the move primitive creates an available pair.
    if (call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN))
    {
      SymExpr* lhe = toSymExpr(call->get(1)); // Left-Hand Expression
      Symbol* lhs = lhe->var; // Left-Hand Symbol

      if (SymExpr* rhe = toSymExpr(call->get(2))) // Right-Hand Expression
      {
        Symbol* rhs = rhe->var; // Right-Hand Symbol

        if (lhs->type->symbol->hasFlag(FLAG_REF) &&
            rhs->type->symbol->hasFlag(FLAG_REF))
        {
          // This is a ref <- ref assignment.
          // Which means that the lhs is now also a reference to whatever the
          // rhs refers to.
          RefMap::iterator refDef = refs.find(rhs);
          // Refs can come from outside the function (e.g. through arguments),
          // so are not necessarily defined within the function.
          if (refDef != refs.end())
          {
            Symbol* val = refDef->second;
#if DEBUG_CP
            if (debug > 0)
              printf("Creating ref (%s[%d], %s[%d])\n",
                     lhs->name, lhs->id, val->name, val->id);
#endif
            refs.insert(RefMapElem(lhs, val));
          }
        }
      }

      if (CallExpr* rhc = toCallExpr(call->get(2)))
      {
        if (rhc->isPrimitive(PRIM_ADDR_OF))
        {
          SymExpr* rhe = toSymExpr(rhc->get(1));

          // Create the pair lhs <- &rhs.
          Symbol* lhs = lhe->var;
          Symbol* rhs = rhe->var;
#if DEBUG_CP
          if (debug > 0)
            printf("Creating ref (%s[%d], %s[%d])\n",
                   lhs->name, lhs->id, rhs->name, rhs->id);
#endif
          refs.insert(RefMapElem(lhs, rhs));
        }
      }
    }
  }
}
Example #7
0
void collectMyCallExprs(BaseAST* ast, Vec<CallExpr*>& callExprs,
                        FnSymbol* parent_fn) {
  AST_CHILDREN_CALL(ast, collectMyCallExprs, callExprs, parent_fn);
  if (CallExpr* callExpr = toCallExpr(ast))
    if (callExpr->parentSymbol == parent_fn)
      callExprs.add(callExpr);
}
Example #8
0
static void
view_ast(BaseAST* ast, bool number = false, int mark = -1, int indent = 0) {
  if (!ast)
    return;
  if (Expr* expr = toExpr(ast)) {
    printf("\n");
    for (int i = 0; i < indent; i++)
      printf(" ");
    printf("(");
    if (ast->id == mark)
      printf("***");
    if (number)
      printf("%d ", ast->id);
    printf("%s", expr->astTagAsString());

    if (isBlockStmt(expr))
      if (FnSymbol* fn = toFnSymbol(expr->parentSymbol))
        if (expr == fn->where)
          printf(" where");

    if (GotoStmt *gs= toGotoStmt(ast)) {
      printf( " ");
      view_ast(gs->label, number, mark, indent+1);
    }

    if (CallExpr* call = toCallExpr(expr))
      if (call->primitive)
        printf(" %s", call->primitive->name);

    if (NamedExpr* named = toNamedExpr(expr))
      printf(" \"%s\"", named->name);

    if (toDefExpr(expr))
      printf(" ");

    int64_t i;
    const char *str;
    if (get_int(expr, &i)) {
      printf(" %" PRId64, i);
    } else if (get_string(expr, &str)) {
      printf(" \"%s\"", str);
    }

    if (SymExpr* sym = toSymExpr(expr)) {
      printf(" ");
      view_sym(sym->var, number, mark);
    } else if (UnresolvedSymExpr* sym = toUnresolvedSymExpr(expr)) {
      printf(" '%s'", sym->unresolved);
    }
  }

  if (Symbol* sym = toSymbol(ast)) {
    view_sym(sym, number, mark);
  }

  AST_CHILDREN_CALL(ast, view_ast, number, mark, indent+2);

  if (toExpr(ast))
    printf(")");
}
Example #9
0
//
// Returns true if 'ref' is only used in the following cases:
//
//   1) Used in 'defCall', usually a PRIM_ADDR_OF or PRIM_SET_REFERENCE
//
//   2) Passed to an initializer in the 'this' slot.
//
//   3) Initialized from a function call, as represented by return-by-ref.
//
// An initializer can thwart detection of a 'const' thing because it takes the
// "this" argument by ref. Normally such a pattern would cause us to assume
// the variable was not const, but in this case we know it is a single def.
//
// defCall: The CallExpr where 'ref' is set from a PRIM_ADDR_OF or
// PRIM_SET_REFERENCE. This call will be ignored while considering uses of
// the 'ref' Symbol.
//
static bool onlyUsedForInitOrRetarg(Symbol* ref, CallExpr* defCall) {
  bool seenInitOrRetarg = false;

  INT_ASSERT(ref->isRef());
  INT_ASSERT(defCall != NULL);

  for_SymbolSymExprs(use, ref) {
    if (use->parentExpr == defCall)
      continue;

    CallExpr* call = toCallExpr(use->parentExpr);

    if (FnSymbol*  fn = call->resolvedFunction()) {
      ArgSymbol* form = actual_to_formal(use);
      if (passedToInitOrRetarg(use, form, fn)) {
        INT_ASSERT(!seenInitOrRetarg); // init/retarg happens just once
        seenInitOrRetarg = true;
      } else {
        return false;  // a use other than what we are looking for
      }
    }
  }

  return true;
}
Example #10
0
static void removeVoidReturn(BlockStmt* cloneBody) {
  CallExpr* retexpr = toCallExpr(cloneBody->body.tail);
  INT_ASSERT(retexpr && retexpr->isPrimitive(PRIM_RETURN));
  INT_ASSERT(toSymExpr(retexpr->get(1))->symbol() == gVoid);

  retexpr->remove();
}
void collectMyCallExprsSTL(BaseAST* ast, std::vector<CallExpr*>& callExprs,
                           FnSymbol* parent_fn) {
  AST_CHILDREN_CALL(ast, collectMyCallExprsSTL, callExprs, parent_fn);
  if (CallExpr* callExpr = toCallExpr(ast))
    if (callExpr->parentSymbol == parent_fn)
      callExprs.push_back(callExpr);
}
Example #12
0
//
// Assumes 'parent' is a PRIM_MOVE or PRIM_ASSIGN
//
// Returns false if the LHS of the move/assign indicates that the rhs cannot
// be a const-ref. For example, if we have a case like this:
//
// (move A, (set-reference B))
//
// where 'A' and 'B' are references, B cannot be a const-ref if A is not a
// const-ref.
//
// In the case of a dereference, (move A, (deref B)), this function will return
// true because we're simply reading B.
//
static bool canRHSBeConstRef(CallExpr* parent, SymExpr* use) {
  INT_ASSERT(isMoveOrAssign(parent));
  SymExpr* LHS = toSymExpr(parent->get(1));
  CallExpr* rhs = toCallExpr(parent->get(2));
  INT_ASSERT(rhs);
  switch (rhs->primitive->tag) {
    case PRIM_GET_MEMBER:
    case PRIM_GET_MEMBER_VALUE:
    case PRIM_GET_SVEC_MEMBER:
    case PRIM_GET_SVEC_MEMBER_VALUE:
    case PRIM_GET_REAL:
    case PRIM_GET_IMAG:
    case PRIM_ADDR_OF:
    case PRIM_SET_REFERENCE: {
      // If LHS is a reference and is not a const-ref, the reference in 'rhs'
      // should not be considered a const-ref either.
      //
      // For the get-member primitives, I intend this to be a safe approach
      // until we know what const-ref means for fields. Basically, if any field
      // might be modified I do not consider the base object to be const-ref.
      //
      // Note that the get-*-value primitives may return a reference if the
      // field is a reference.
      if (LHS->isRef()) {
        if (!inferConstRef(LHS->symbol())) {
          return false;
        }
      }
    }
    default:
      break;
  }
  return isSafeRefPrimitive(use);
}
//
// inlines the function called by 'call' at that call site
//
static void
inlineCall(FnSymbol* fn, CallExpr* call, Vec<FnSymbol*>& canRemoveRefTempSet) {
  INT_ASSERT(call->isResolved() == fn);
  SET_LINENO(call);

  Expr* stmt = call->getStmtExpr();

  //
  // calculate a map from actual symbols to formal symbols
  //
  SymbolMap map;
  for_formals_actuals(formal, actual, call) {
    SymExpr* se = toSymExpr(actual);
    INT_ASSERT(se);
    if ((formal->intent & INTENT_REF) && canRemoveRefTempSet.set_in(fn)) {
      if (se->var->hasFlag(FLAG_REF_TEMP)) {
        if (CallExpr* move = findRefTempInit(se)) {
          SymExpr* origSym = NULL;
          if (CallExpr* addrOf = toCallExpr(move->get(2))) {
            INT_ASSERT(addrOf->isPrimitive(PRIM_ADDR_OF));
            origSym = toSymExpr(addrOf->get(1));
          } else {
            origSym = toSymExpr(move->get(2));
          }
          INT_ASSERT(origSym);
          map.put(formal, origSym->var);
          se->var->defPoint->remove();
          move->remove();
          continue;
        }
      }
    }
    map.put(formal, se->var);
  }
void deadVariableElimination(FnSymbol* fn) {
  Vec<Symbol*> symSet;
  Vec<SymExpr*> symExprs;
  collectSymbolSetSymExprVec(fn, symSet, symExprs);

  Map<Symbol*,Vec<SymExpr*>*> defMap;
  Map<Symbol*,Vec<SymExpr*>*> useMap;
  buildDefUseMaps(symSet, symExprs, defMap, useMap);

  forv_Vec(Symbol, sym, symSet)
  {
    // We're interested only in VarSymbols.
    if (!isVarSymbol(sym))
      continue;

    // A method must have a _this symbol, even if it is not used.
    if (sym == fn->_this)
      continue;

    if (isDeadVariable(sym, defMap, useMap)) {
      for_defs(se, defMap, sym) {
        CallExpr* call = toCallExpr(se->parentExpr);
        INT_ASSERT(call &&
                   (call->isPrimitive(PRIM_MOVE) ||
                    call->isPrimitive(PRIM_ASSIGN)));
        Expr* rhs = call->get(2)->remove();
        if (!isSymExpr(rhs))
          call->replace(rhs);
        else
          call->remove();
      }
      sym->defPoint->remove();
    }
  }
Example #15
0
  forv_Vec(FnSymbol, fn, gFnSymbols)
  {
    if (VarSymbol* ret = toVarSymbol(fn->getReturnSymbol()))
    {
      // The return value of an initCopy function should not be autodestroyed.
      // Normally, the return value of a function is autoCopied, but since
      // autoCopy is typically defined in terms of initCopy, this would lead to
      // infinite recursion.  That is, the return value of initCopy must be
      // handled specially.
      if (fn->hasFlag(FLAG_INIT_COPY_FN))
        ret->removeFlag(FLAG_INSERT_AUTO_DESTROY);

      // This is just a workaround for memory management being handled specially
      // for internally reference-counted types. (sandboxing)
      TypeSymbol* ts = ret->type->symbol;
      if (ts->hasFlag(FLAG_ARRAY) ||
          ts->hasFlag(FLAG_DOMAIN))
        ret->removeFlag(FLAG_INSERT_AUTO_DESTROY);
      // Do we need to add other record-wrapped types here?  Testing will tell.

      // NOTE 1: When the value of a record field is established in a default
      // constructor, it is initialized using a MOVE.  That means that ownership
      // of that value is shared between the formal_tmp and the record field.
      // If the autodestroy flag is left on that formal temp, then it will be
      // destroyed which -- for ref-counted types -- can result in a dangling
      // reference.  So here, we look for that case and remove it.  
      if (fn->hasFlag(FLAG_DEFAULT_CONSTRUCTOR))
      {
        Map<Symbol*,Vec<SymExpr*>*> defMap;
        Map<Symbol*,Vec<SymExpr*>*> useMap;
        buildDefUseMaps(fn, defMap, useMap);

        std::vector<DefExpr*> defs;
        collectDefExprs(fn, defs);

        for_vector(DefExpr, def, defs)
        {
          if (VarSymbol* var = toVarSymbol(def->sym))
          {
            // Examine only those bearing the explicit autodestroy flag.
            if (! var->hasFlag(FLAG_INSERT_AUTO_DESTROY))
              continue;

            // Look for a use in a PRIM_SET_MEMBER where the field is a record
            // type, and remove the flag.
            // (We don't actually check that var is of record type, because
            // chpl__autoDestroy() does nothing when applied to all other types.
            for_uses(se, useMap, var)
            {
              CallExpr* call = toCallExpr(se->parentExpr);
              if (call->isPrimitive(PRIM_SET_MEMBER) &&
                  toSymExpr(call->get(3))->var == var)
                var->removeFlag(FLAG_INSERT_AUTO_DESTROY);
            }
          }
        }

        freeDefUseMaps(defMap, useMap);
      }
Example #16
0
// Remove the return statement and the def of 'ret'. Return 'ret'.
// See also removeRetSymbolAndUses().
static Symbol* removeParIterReturn(BlockStmt* cloneBody, Symbol* retsym) {
  CallExpr* retexpr = toCallExpr(cloneBody->body.tail);
  INT_ASSERT(retexpr && retexpr->isPrimitive(PRIM_RETURN));
  if (retsym == NULL) {
    retsym = toSymExpr(retexpr->get(1))->symbol();
    INT_ASSERT(retsym->type->symbol->hasFlag(FLAG_ITERATOR_RECORD));

  } else {
    CallExpr* move = toCallExpr(retsym->getSingleDef()->getStmtExpr());
    INT_ASSERT(move->isPrimitive(PRIM_MOVE) || move->isPrimitive(PRIM_ASSIGN));
    retsym = toSymExpr(move->get(2))->symbol();
    move->remove();
  }

  retexpr->remove();
  return retsym;
}
Example #17
0
//
// Find the _waitEndCount and _endCountFree calls that comes after 'fromHere'.
// Since these two calls come together, it is prettier to insert
// our stuff after the latter.
//
static Expr* findTailInsertionPoint(Expr* fromHere, bool isCoforall) {
  Expr* curr = fromHere;
  if (isCoforall) curr = curr->parentExpr;
  CallExpr* result = NULL;
  while ((curr = curr->next)) {
    if (CallExpr* call = toCallExpr(curr))
      if (call->isNamed("_waitEndCount")) {
        result = call;
        break;
      }
  }
  INT_ASSERT(result);
  CallExpr* freeEC = toCallExpr(result->next);
  // Currently these two calls come together.
  INT_ASSERT(freeEC && freeEC->isNamed("_endCountFree"));
  return freeEC;
}
Example #18
0
static void optimizeAnonymousRangeIteration(Expr* iteratorExpr, bool zippered)
{
  if (!zippered)
    tryToReplaceWithDirectRangeIterator(iteratorExpr);
  // for zippered iterators, try to replace each iterator of the tuple
  else
    if (CallExpr* call = toCallExpr(iteratorExpr))
      if (call->isNamed("_build_tuple"))
        for_actuals(actual, call)
          tryToReplaceWithDirectRangeIterator(actual);
}
void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal)
{
  Expr*     returnPrim  = fn->body->body.tail;

  SET_LINENO(returnPrim);

  CallExpr* returnCall  = toCallExpr(returnPrim);
  Expr*     returnValue = returnCall->get(1)->remove();
  CallExpr* moveExpr    = new CallExpr(PRIM_MOVE, formal, returnValue);

  returnPrim->insertBefore(moveExpr);
}
Example #20
0
//
// BHARSH TODO: Fix up PRIM_ADDR_OF's return type function to correctly
// handle this
//
// The 'returnInfoRef' function for PRIM_ADDR_OF is currently set up to
// always return with the Qualifier QUAL_REF. This function is intended
// as a workaround to handle the case where we have a wide-ref in a
// PRIM_ADDR_OF:
//
// (move "wide-ref dest" (addr-of "wide-ref src"))
//
// Otherwise, 'returnInfoRef' would hide the fact that the actual is a wide-ref
// and it would appear as though we had a local reference.
//
// Note that this case is actually just a copy of a pointer's address, and
// will not generate an addrof in the generated code.
//
static
bool isAddrOfWideRefVar(Expr* e)
{
  // ADDR_OF of a wide ref variable is a wide ref (not a ref).
  if (CallExpr* call = toCallExpr(e))
    if (call->isPrimitive(PRIM_ADDR_OF))
      if (SymExpr* se = toSymExpr(call->get(1)))
        if (se->symbol()->qualType().isWideRef())
          return true;

  return false;
}
Example #21
0
void localizeGlobals() {
  if (fNoGlobalConstOpt) return;
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    Map<Symbol*,VarSymbol*> globals;
    std::vector<BaseAST*> asts;
    collect_asts(fn->body, asts);
    for_vector(BaseAST, ast, asts) {
      if (SymExpr* se = toSymExpr(ast)) {
        Symbol* var = se->symbol();
        ModuleSymbol* parentmod = toModuleSymbol(var->defPoint->parentSymbol);
        CallExpr* parentExpr = toCallExpr(se->parentExpr);
        bool inAddrOf = parentExpr && parentExpr->isPrimitive(PRIM_ADDR_OF);
        bool lhsOfMove = parentExpr && isMoveOrAssign(parentExpr) && (parentExpr->get(1) == se);

        // Is var a global constant?
        // Don't replace the var name in its init function since that's
        //      where we're setting the value. Similarly, dont replace them
        //      inside initStringLiterals
        // If the parentSymbol is the rootModule, the var is 'void,'
        //      'false,' '0,' ...
        // Also don't replace it when it's in an addr of primitive.
        if (parentmod &&
            fn != parentmod->initFn &&
            fn != initStringLiterals &&
            !inAddrOf &&
            !lhsOfMove &&
            var->hasFlag(FLAG_CONST) &&
            var->defPoint->parentSymbol != rootModule) {
          VarSymbol* local_global = globals.get(var);
          SET_LINENO(se); // Set the se line number for output
          if (!local_global) {
            const char * newname = astr("local_", var->cname);
            local_global = newTemp(newname, var->type);
            fn->insertAtHead(new CallExpr(PRIM_MOVE, local_global, var));
            fn->insertAtHead(new DefExpr(local_global));

            // Copy string immediates to localized strings so that
            // we can show the string value in comments next to uses.
            if (!llvmCodegen)
              if (VarSymbol* localVarSym = toVarSymbol(var))
                if (Immediate* immediate = localVarSym->immediate)
                  if (immediate->const_kind == CONST_KIND_STRING)
                    local_global->immediate =
                      new Immediate(immediate->v_string, immediate->string_kind);

            globals.put(var, local_global);
          }
          se->replace(new SymExpr(toSymbol(local_global)));
        }
      }
    }
  }
Example #22
0
static bool
removeIdentityDefs(Symbol* sym) {
  bool change = false;

  for_defs(def, defMap, sym) {
    CallExpr* move = toCallExpr(def->parentExpr);
    if (move && isMoveOrAssign(move)) {
      SymExpr* rhs = toSymExpr(move->get(2));
      if (rhs && def->symbol() == rhs->symbol()) {
        move->remove();
        change = true;
      }
    }
  }
Example #23
0
static bool
removeIdentityDefs(Symbol* sym) {
  bool change = false;

  for_defs(def, defMap, sym) {
    CallExpr* move = toCallExpr(def->parentExpr);
    if (move && move->isPrimitive(PRIM_MOVE)) {
      SymExpr* rhs = toSymExpr(move->get(2));
      if (rhs && def->var == rhs->var) {
        move->remove();
        change = true;
      }
    }
  }
Example #24
0
/*
 * Returns true if `e` has no side effects. Checked side effects are:
 *  - Read/write to a global
 *  - Is/contains essential primitive
 *  - If it's a call to functions with ref arguments
 *  - If the LHS of a PRIM_MOVE appears in the exprToMove
 *
 * For now, this is a very conservative analysis. A more precise analysis
 * could distinguish between reads and writes to memory and to take into
 * account alias analysis.
 */
bool SafeExprAnalysis::exprHasNoSideEffects(Expr* e, Expr* exprToMove) {
  if(safeExprCache.count(e) > 0) {
    return safeExprCache[e];
  }
  if(CallExpr* ce = toCallExpr(e)) {
    if(!ce->isPrimitive()) {
      FnSymbol* fnSym = ce->theFnSymbol();
      const bool cachedSafeFn = safeFnCache.count(fnSym);
      if(!cachedSafeFn) {
        const bool retval = fnHasNoSideEffects(fnSym);
        safeFnCache[fnSym] = retval;
        return retval;
      }
      return safeFnCache[fnSym];
    }
    else {
      //primitive
      if(! isSafePrimitive(ce)){
        safeExprCache[e] = false;
        return false;
      }
      else if (exprToMove != NULL) {
        //
        // Exposed by AST pattern like this:
        //          |---|------- `exprToMove`
        // (move T (+ A B))
        // (move A B) -------- `ce`
        // (move B T)
        //
        // Without this check could turn into:
        //
        // (move A B)
        // (move B (+ A B))
        //
        // Which is incorrect.
        //
        if (ce->isPrimitive(PRIM_MOVE)) {
          INT_ASSERT(isSymExpr(ce->get(1)));
          std::vector<SymExpr*> syms;
          collectSymExprs(exprToMove, syms);
          for_vector(SymExpr, s, syms) {
            if (s->symbol() == toSymExpr(ce->get(1))->symbol()) {
              safeExprCache[e] = false;
              return false;
            }
          }
        }
      }
    }
  }
Example #25
0
// Does 'fn' return the result of a call to 'se' ?
static bool isReturnedValue(FnSymbol* fn, SymExpr* se) {
  Symbol* currSym  = fn->getReturnSymbol();

  // Traverse the chain of moves. Like in stripReturnScaffolding().
  while (true) {
    if (SymExpr* defSE = currSym->getSingleDef())
      if (CallExpr* defMove = toCallExpr(defSE->parentExpr))
        if (defMove->isPrimitive(PRIM_MOVE)) {
          Expr* defSrc = defMove->get(2);

          if (CallExpr* srcCall = toCallExpr(defSrc))
            if (srcCall->baseExpr == se)
              return true;  // found it

          if (SymExpr* srcSE = toSymExpr(defSrc)) {
            currSym = srcSE->symbol();
            continue;  // continue traversing the chain of moves
          }
        }
    // The chain of moves is over. Return false.
    break;
  }
  return false;
}
//
// 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();
            }
          }
        }
      }
    }
Example #27
0
// If there is exactly one definition of var by something of reference type, 
// then return the call that defines it.
// Otherwise, return NULL.
static CallExpr*
findRefDef(Map<Symbol*,Vec<SymExpr*>*>& defMap, Symbol* var) {
  CallExpr* ret = NULL;
  for_defs(def, defMap, var) {
    if (CallExpr* call = toCallExpr(def->parentExpr)) {
      if (call->isPrimitive(PRIM_MOVE) || call->isPrimitive(PRIM_ASSIGN))
        if (call->get(2)->isRef()) {
          if (ret)
            return NULL;
          else
            ret = call;
        }
    }
  }
  return ret;
}
Example #28
0
//
// Returns true if 'use' appears in a non-side-effecting primitive.
//
static bool isSafeRefPrimitive(SymExpr* use) {
  CallExpr* call = toCallExpr(use->parentExpr);
  INT_ASSERT(call);

  switch (call->primitive->tag) {
    case PRIM_ADDR_OF:
    case PRIM_SET_REFERENCE:
    case PRIM_DEREF:
    case PRIM_WIDE_GET_LOCALE:
    case PRIM_NOOP:
    case PRIM_UNARY_MINUS:
    case PRIM_UNARY_PLUS:
    case PRIM_UNARY_NOT:
    case PRIM_UNARY_LNOT:
    case PRIM_ADD:
    case PRIM_SUBTRACT:
    case PRIM_MULT:
    case PRIM_DIV:
    case PRIM_MOD:
    case PRIM_LSH:
    case PRIM_RSH:
    case PRIM_EQUAL:
    case PRIM_NOTEQUAL:
    case PRIM_LESSOREQUAL:
    case PRIM_GREATEROREQUAL:
    case PRIM_LESS:
    case PRIM_GREATER:
    case PRIM_AND:
    case PRIM_OR:
    case PRIM_XOR:
    case PRIM_POW:
    case PRIM_MIN:
    case PRIM_MAX:
    case PRIM_CHECK_NIL:
    case PRIM_LOCAL_CHECK:
    case PRIM_PTR_EQUAL:
    case PRIM_PTR_NOTEQUAL:
    case PRIM_SIZEOF_BUNDLE:
    case PRIM_SIZEOF_DDATA_ELEMENT:
    case PRIM_WIDE_GET_NODE:
      return true;
    default:
      return false;
  }
}
Example #29
0
static bool
symExprIsSetByUse(SymExpr* use) {
  if (CallExpr* call = toCallExpr(use->parentExpr)) {
    if (FnSymbol* fn = call->resolvedFunction()) {
      ArgSymbol* formal = actual_to_formal(use);

      if (formal->intent == INTENT_INOUT || formal->intent == INTENT_OUT) {
        // Shouldn't this be a Def, not a Use, then?
        INT_ASSERT(0);
        return true;
      }

      if (formal->type->symbol->hasFlag(FLAG_REF) &&
          (fn->hasFlag(FLAG_ALLOW_REF) ||
           formal->hasFlag(FLAG_WRAP_WRITTEN_FORMAL))) {
        // This case has to do with wrapper functions (promotion?)
        return true;
      }

    } else if (call->isPrimitive(PRIM_SET_MEMBER)) {
      // PRIM_SET_MEMBER to set the pointer inside of a reference
      // counts as "setter"
      // the below conditional would better be isRefType()
      if (!call->get(2)->typeInfo()->refType) {
        return true;
      }

    } else if (call->isPrimitive(PRIM_RETURN) ||
               call->isPrimitive(PRIM_YIELD)) {
      FnSymbol* inFn = toFnSymbol(call->parentSymbol);

      // It is not necessary to use the 'ref' version
      // if the function result is returned by 'const ref'.
      if (inFn->retTag == RET_CONST_REF) return false;
      // MPF: it seems to cause problems to return false
      // here when inFn->retTag is RET_VALUE.
      // TODO: can we add
      //if (inFn->retTag == RET_VALUE) return false;
      return true;
    }
  }

  return false;
}
Example #30
0
void localizeGlobals() {
  if (fNoGlobalConstOpt) return;
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    Map<Symbol*,VarSymbol*> globals;
    std::vector<BaseAST*> asts;
    collect_asts(fn->body, asts);
    for_vector(BaseAST, ast, asts) {
      if (SymExpr* se = toSymExpr(ast)) {
        Symbol* var = se->var;
        ModuleSymbol* parentmod = toModuleSymbol(var->defPoint->parentSymbol);
        CallExpr* parentExpr = toCallExpr(se->parentExpr);
        bool inAddrOf = parentExpr && parentExpr->isPrimitive(PRIM_ADDR_OF);

        // Is var a global constant?
        // Don't replace the var name in its init function since that's
        //      where we're setting the value. Similarly, dont replace them
        //      inside initStringLiterals
        // If the parentSymbol is the rootModule, the var is 'void,'
        //      'false,' '0,' ...
        // Also don't replace it when it's in an addr of primitive.
        if (parentmod &&
            fn != parentmod->initFn &&
            fn != initStringLiterals &&
            !inAddrOf &&
            var->hasFlag(FLAG_CONST) &&
            var->defPoint->parentSymbol != rootModule) {
          VarSymbol* local_global = globals.get(var);
          SET_LINENO(se); // Set the se line number for output
          if (!local_global) {
            const char * newname = astr("local_", var->cname);
            local_global = newTemp(newname, var->type);
            fn->insertAtHead(new CallExpr(PRIM_MOVE, local_global, var));
            fn->insertAtHead(new DefExpr(local_global));

            globals.put(var, local_global);
          }
          se->replace(new SymExpr(toSymbol(local_global)));
        }
      }
    }
  }