| 199 | } |
| 200 | |
| 201 | Status LowerWhileHelper::CreateEnterNodes() { |
| 202 | // Note: `Node::input_edge` runs in O(num_inputs) so we use |
| 203 | // `Node::input_edges` instead so that below loop runs in O(num_inputs) time |
| 204 | // and not O(num_inputs^2). |
| 205 | std::vector<const Edge*> edges; |
| 206 | TF_RETURN_IF_ERROR(while_op_->input_edges(&edges)); |
| 207 | for (const Edge* edge : edges) { |
| 208 | Node* enter_node; |
| 209 | TF_RETURN_IF_ERROR(NodeBuilder(NewName("enter"), "Enter", |
| 210 | graph_->op_registry(), &debug_info_) |
| 211 | .Input(NodeOut(edge->src(), edge->src_output())) |
| 212 | .Attr("frame_name", name_) |
| 213 | .Attr("parallel_iterations", parallel_iterations_) |
| 214 | .Device(while_op_->requested_device()) |
| 215 | .Finalize(graph_, &enter_node)); |
| 216 | enter_nodes_[edge->dst_input()] = enter_node; |
| 217 | } |
| 218 | // Create a NoOp node that takes incoming control inputs of the original While |
| 219 | // op as control inputs and use it as a control input for all Enter nodes. |
| 220 | std::vector<Node*> control_inputs; |
| 221 | for (const Edge* e : while_op_->in_edges()) { |
| 222 | if (e->IsControlEdge()) { |
| 223 | control_inputs.push_back(e->src()); |
| 224 | } |
| 225 | } |
| 226 | if (!control_inputs.empty()) { |
| 227 | Node* incoming_control_node; |
| 228 | TF_RETURN_IF_ERROR(NodeBuilder(NewName("LoopControlInputs"), "NoOp", |
| 229 | graph_->op_registry(), &debug_info_) |
| 230 | .ControlInputs(control_inputs) |
| 231 | .Device(while_op_->requested_device()) |
| 232 | .Finalize(graph_, &incoming_control_node)); |
| 233 | for (Node* n : enter_nodes_) { |
| 234 | graph_->AddControlEdge(incoming_control_node, n); |
| 235 | } |
| 236 | } |
| 237 | return Status::OK(); |
| 238 | } |
| 239 | |
| 240 | Status LowerWhileHelper::CreateMergeNodes() { |
| 241 | for (Node* enter_node : enter_nodes_) { |
nothing calls this directly
no test coverage detected