tensorflow/compiler/mlir/xla/transforms/legalize_tf_communication.cc
// Rewrites a function body and communication ops inside. Region control flow
// are updated when necessary, to propagate tokens. The function may either be
// rewritten to create a token or take in and return a token, depending on its
// visibility and if there are any callers.
Rewrites a function body and communication ops inside. Region control flow
are updated when necessary, to propagate tokens. The function may either be
rewritten to create a token or take in and return a token, depending on its
visibility and if there are any callers.
Rewrites a function body and communication ops inside . Region control flow are updated when necessary to propagate tokens . The function may either be rewritten to create a token or take in and return a token depending on its visibility and if there are any callers .
LogicalResult RewriteFunction(
    OpBuilder& builder, int64_t& channel_id, ModuleOp module, FuncOp func,
    const llvm::SmallDenseMap<StringRef, FuncToRewrite>& funcs,
    const llvm::SmallPtrSetImpl<Operation*>& control_flow_ops,
    const llvm::SmallPtrSetImpl<Block*>& control_flow_blocks, bool is_clone) {
  MLIRContext* context = module.getContext();
  if (!llvm::hasSingleElement(func.getBody()))
    return func.emitError()
           << "'" << FuncOp::getOperationName()
           << "' ops with more than one block are not supported";
  bool rewrite_block =
      is_clone || (!func.isPublic() && !func.symbolKnownUseEmpty(module));
  Block& func_body = func.front();
  builder.setInsertionPointToStart(&func_body);
  auto token_type = TokenType::get(context);
  Value init_token =
      rewrite_block ? func_body.addArgument(token_type, func.getLoc())
                    : builder.create<CreateTokenOp>(func.getLoc(), token_type)
                          .getResult();
  SmallVector<OpVisitorState, 4> ops_to_visit{
      {llvm::None, init_token, &func_body.front()}};
  while (!ops_to_visit.empty()) {
    OpVisitorState op_to_visit = ops_to_visit.pop_back_val();
    Operation* curr_op = op_to_visit.op;
    Value token = op_to_visit.token;
    Operation* next_op = curr_op->getNextNode();
    if (auto host_compute = dyn_cast<TF::_XlaHostComputeMlirOp>(curr_op)) {
      token = RewriteHostComputeOp(builder, channel_id, host_compute, token);
    } else if (auto send_to_host = dyn_cast<TF::XlaSendToHostOp>(curr_op)) {
      token = RewriteSendToHostOp(builder, channel_id, send_to_host, token);
    } else if (auto recv_from_host = dyn_cast<TF::XlaRecvFromHostOp>(curr_op)) {
      token = RewriteRecvFromHostOp(builder, channel_id, recv_from_host, token);
    } else if (auto call = dyn_cast<mlir::func::CallOp>(curr_op)) {
      auto it = funcs.find(call.getCallee());
      if (it != funcs.end()) {
        FuncOp clone = it->getSecond().clone;
        Optional<StringRef> symbol_name =
            clone ? Optional<StringRef>(clone.getName()) : llvm::None;
        token = RewriteCallOp(builder, call, symbol_name, token);
      }
    } else if (auto region_if = dyn_cast<IfOp>(curr_op)) {
      if (op_to_visit.region_idx || control_flow_ops.contains(region_if)) {
        auto exist_unprocessed_region =
            ProcessRegionIfOp(builder, region_if, op_to_visit.region_idx,
                              ops_to_visit, control_flow_blocks, token);
        if (!exist_unprocessed_region) {
          token = curr_op->getResult(curr_op->getNumResults() - 1);
        } else {
          continue;
        }
      }
    } else if (auto region_while = dyn_cast<WhileOp>(curr_op)) {
      if (op_to_visit.region_idx || control_flow_ops.contains(region_while))
        if (ProcessRegionWhileOp(builder, region_while, op_to_visit.region_idx,
                                 ops_to_visit, control_flow_blocks, token))
          continue;
    } else if (auto region_terminator = dyn_cast<mhlo::ReturnOp>(curr_op)) {
      bool flatten_tuple = isa<mhlo::WhileOp, mhlo::IfOp, mhlo::CaseOp>(
          region_terminator->getParentOp());
      RewriteControlFlowTerminator(builder, region_terminator, token,
                                   flatten_tuple);
      continue;
    } else if (auto func_terminator = dyn_cast<mlir::func::ReturnOp>(curr_op)) {
      if (rewrite_block)
        RewriteFunctionTerminator(builder, func_terminator, token);
      continue;
    }
    ops_to_visit.push_back({llvm::None, token, next_op});
  }
  if (rewrite_block) UpdateFunctionType(builder, func, func_body);
  return success();
}
LogicalResult RewriteFunction ( OpBuilder & builder , int64_t & channel_id , ModuleOp module , FuncOp func , const llvm :: SmallDenseMap < StringRef , FuncToRewrite > & funcs , const llvm :: SmallPtrSetImpl < Operation * > & control_flow_ops , const llvm :: SmallPtrSetImpl < Block * > & control_flow_blocks , bool is_clone ) { MLIRContext * context = module . getContext ( ) ; if ( ! llvm :: hasSingleElement ( func . getBody ( ) ) ) return func . emitError ( ) << " " << FuncOp :: getOperationName ( ) << " " ; bool rewrite_block = is_clone || ( ! func . isPublic ( ) && ! func . symbolKnownUseEmpty ( module ) ) ; Block & func_body = func . front ( ) ; builder . setInsertionPointToStart ( & func_body ) ; auto token_type = TokenType :: get ( context ) ; Value init_token = rewrite_block ? func_body . addArgument ( token_type , func . getLoc ( ) ) : builder . create < CreateTokenOp > ( func . getLoc ( ) , token_type ) . getResult ( ) ; SmallVector < OpVisitorState , 4 > ops_to_visit { { llvm :: None , init_token , & func_body . front ( ) } } ; while ( ! ops_to_visit . empty ( ) ) { OpVisitorState op_to_visit = ops_to_visit . pop_back_val ( ) ; Operation * curr_op = op_to_visit . op ; Value token = op_to_visit . token ; Operation * next_op = curr_op -> getNextNode ( ) ; if ( auto host_compute = dyn_cast < TF :: _XlaHostComputeMlirOp > ( curr_op ) ) { token = RewriteHostComputeOp ( builder , channel_id , host_compute , token ) ; } else if ( auto send_to_host = dyn_cast < TF :: XlaSendToHostOp > ( curr_op ) ) { token = RewriteSendToHostOp ( builder , channel_id , send_to_host , token ) ; } else if ( auto recv_from_host = dyn_cast < TF :: XlaRecvFromHostOp > ( curr_op ) ) { token = RewriteRecvFromHostOp ( builder , channel_id , recv_from_host , token ) ; } else if ( auto call = dyn_cast < mlir :: func :: CallOp > ( curr_op ) ) { auto it = funcs . find ( call . getCallee ( ) ) ; if ( it != funcs . end ( ) ) { FuncOp clone = it -> getSecond ( ) . clone ; Optional < StringRef > symbol_name = clone ? Optional < StringRef > ( clone . getName ( ) ) : llvm :: None ; token = RewriteCallOp ( builder , call , symbol_name , token ) ; } } else if ( auto region_if = dyn_cast < IfOp > ( curr_op ) ) { if ( op_to_visit . region_idx || control_flow_ops . contains ( region_if ) ) { auto exist_unprocessed_region = ProcessRegionIfOp ( builder , region_if , op_to_visit . region_idx , ops_to_visit , control_flow_blocks , token ) ; if ( ! exist_unprocessed_region ) { token = curr_op -> getResult ( curr_op -> getNumResults ( ) - 1 ) ; } else { continue ; } } } else if ( auto region_while = dyn_cast < WhileOp > ( curr_op ) ) { if ( op_to_visit . region_idx || control_flow_ops . contains ( region_while ) ) if ( ProcessRegionWhileOp ( builder , region_while , op_to_visit . region_idx , ops_to_visit , control_flow_blocks , token ) ) continue ; } else if ( auto region_terminator = dyn_cast < mhlo :: ReturnOp > ( curr_op ) ) { bool flatten_tuple = isa < mhlo :: WhileOp , mhlo :: IfOp , mhlo :: CaseOp > ( region_terminator -> getParentOp ( ) ) ; RewriteControlFlowTerminator ( builder , region_terminator , token , flatten_tuple ) ; continue ; } else if ( auto func_terminator = dyn_cast < mlir :: func :: ReturnOp > ( curr_op ) ) { if ( rewrite_block ) RewriteFunctionTerminator ( builder , func_terminator , token ) ; continue ; } ops_to_visit . push_back ( { llvm :: None , token , next_op } ) ; } if ( rewrite_block ) UpdateFunctionType ( builder , func , func_body ) ; return success ( ) ; }
Rewrites a function body and communication ops inside.
Rewrites a function body and communication ops inside .
// If a function is public, it's signature should not be modified, and instead // a token will be created. Otherwise a token block argument is inserted. // Stack to keep track of region based control flow op nesting and current // op to visit. /*region_idx=*/ // Ops may be removed, so the next op is kept track of beforehand. // Only `mlir::func::CallOp` is supported as this requires knowing how to // rewrite arguments and results to a function. // If the function being called is to be cloned, update the call to also // point to the cloned function. // Once all the IfOp regions are processed (i.e. // 'exist_unprocessed_region' == false), select returned token-value // from IfOp as the token to be used for the following op. // There is no next op after the control flow op terminator, simply let // stack have one less element. // There is no next op after the function terminator, simply let stack // have one less element/be empty. // Visit next op. /*region_idx=*/