Builds a graph for the loop body.
| 155 | |
| 156 | // Builds a graph for the loop body. |
| 157 | Status BuildLoopBody(const Graph& graph, WhileLoopFrame* frame, |
| 158 | DataTypeVector* arg_types, |
| 159 | std::unique_ptr<Graph>* body_output) { |
| 160 | VLOG(2) << "Building loop body for " << frame->name; |
| 161 | *body_output = absl::make_unique<Graph>(graph.op_registry()); |
| 162 | Graph* output = body_output->get(); |
| 163 | |
| 164 | // Map from nodes in the original graph to the condition graph. |
| 165 | std::vector<Node*> node_map(graph.num_node_ids(), nullptr); |
| 166 | std::vector<bool> squash_src_outputs(graph.num_node_ids(), false); |
| 167 | |
| 168 | // Build one _Arg node for each Enter node. |
| 169 | std::vector<Node*> next_iterations; |
| 170 | next_iterations.reserve(frame->args.size()); |
| 171 | arg_types->reserve(frame->args.size()); |
| 172 | for (int i = 0; i < frame->args.size(); ++i) { |
| 173 | const WhileLoopArg& arg = frame->args[i]; |
| 174 | |
| 175 | DataType dtype = arg.enter->input_type(0); |
| 176 | arg_types->push_back(dtype); |
| 177 | |
| 178 | TF_ASSIGN_OR_RETURN(Node * arg_node, BuildArgNode(output, dtype, i)); |
| 179 | TF_ASSIGN_OR_RETURN(Node * retval_node, BuildRetvalNode(output, dtype, i)); |
| 180 | if (arg.is_loop_invariant) { |
| 181 | // Argument is loop-invariant. Forward it from the Arg to the Retval. |
| 182 | node_map[arg.enter->id()] = arg_node; |
| 183 | output->AddEdge(arg_node, 0, retval_node, 0); |
| 184 | } else { |
| 185 | // Argument is loop-varying. |
| 186 | if (dtype == DT_RESOURCE) { |
| 187 | // DT_RESOURCE arguments should always be loop-invariant in the graphs |
| 188 | // generated from TF. |
| 189 | return errors::Unimplemented("Loop-varying DT_RESOURCE Enter node ", |
| 190 | arg.enter->name(), " is currently not", |
| 191 | " supported."); |
| 192 | } |
| 193 | node_map[arg.switch_node->id()] = arg_node; |
| 194 | // The Switch node has two outputs, but _Arg only has one. This tells |
| 195 | // the CopySubgraph function to rewrite the output number of edges from |
| 196 | // the _Arg node to be 0 rather than copying the output number from the |
| 197 | // Switch node. |
| 198 | squash_src_outputs[arg.switch_node->id()] = true; |
| 199 | node_map[arg.next_iteration->id()] = retval_node; |
| 200 | next_iterations.push_back(arg.next_iteration); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | // Performs a reverse DFS, copying nodes and edges to the output graph. |
| 205 | // The _Arg and _Retval nodes were added unconditionally above, so we are |
| 206 | // guaranteed to get the correct function signature. |
| 207 | TF_RETURN_IF_ERROR(CopySubgraph(graph, frame, std::move(next_iterations), |
| 208 | squash_src_outputs, &node_map, output)); |
| 209 | |
| 210 | return Status::OK(); |
| 211 | } |
| 212 | |
| 213 | // Copy the FunctionDef of given function from lookup_library to library, if |
| 214 | // it can be found in lookup_library but is missing from library. |
no test coverage detected