| 1419 | } |
| 1420 | |
| 1421 | Status FunctionalizeCond::FunctionalizeInternal() { |
| 1422 | // The general approach for converting a tf.cond (as lowered via switch/merge |
| 1423 | // nodes) to a functional if is as follows: |
| 1424 | // 1. Determine the topological order and collect all the switch and merge |
| 1425 | // nodes in the graph; |
| 1426 | // 2. Compute the predicates and dominance structure for all the nodes in the |
| 1427 | // graph - this includes which predicate must be true for a op to execute |
| 1428 | // (predicate values are considered directly rather than attempting to |
| 1429 | // determine deeper equivalence). We shall refer to this structure as the |
| 1430 | // CondState; |
| 1431 | // 3. Sort the merge nodes by nesting depth; |
| 1432 | // 4. Extract merge nodes together that have the same CondState and |
| 1433 | // AncestorState from the innermost to the outermost into IfOps; |
| 1434 | // Note: In the above only nodes that feed into a merge node will be |
| 1435 | // considered for functionalization. |
| 1436 | |
| 1437 | // Perform a DFS over the graph and |
| 1438 | // * Determine the reverse topological order of the nodes (there should be no |
| 1439 | // cycles at this point so the post-order numbering corresponds to the |
| 1440 | // reverse topological sorting); |
| 1441 | // * Record reverse topological for merge and switch nodes; |
| 1442 | std::vector<Node*> rev_topo_order; |
| 1443 | std::vector<Node*> merge_order; |
| 1444 | DFS(*graph_, nullptr, [&](Node* n) { |
| 1445 | if (IsSwitch(n)) { |
| 1446 | AddSwitchId(n->id()); |
| 1447 | } |
| 1448 | if (IsMerge(n)) { |
| 1449 | merge_order.push_back(n); |
| 1450 | } |
| 1451 | if (n->IsOp()) { |
| 1452 | rev_topo_order.push_back(n); |
| 1453 | } |
| 1454 | }); |
| 1455 | |
| 1456 | // No merges to functionalize. |
| 1457 | if (merge_order.empty()) { |
| 1458 | // No merges mean no switch values consumed (as only considering values |
| 1459 | // fetchable as output of merge); |
| 1460 | DeleteReachableAndDeadNodes(merge_order); |
| 1461 | return Status::OK(); |
| 1462 | } |
| 1463 | |
| 1464 | TF_RETURN_IF_ERROR(DetermineStates(std::move(rev_topo_order))); |
| 1465 | if (VLOG_IS_ON(4)) DumpGraphWithCondState("id"); |
| 1466 | |
| 1467 | // Sort the merge nodes from innermost outwards. |
| 1468 | SortMergeNodes(&merge_order); |
| 1469 | |
| 1470 | // Cluster merge nodes by (CondId, AncestorId, predicate) in order of |
| 1471 | // nesting. (CondId, AncestorId) is not enough, e.g. |
| 1472 | // pred1 = array_ops.placeholder(dtypes.bool, name='pred1') |
| 1473 | // pred2 = array_ops.placeholder(dtypes.bool, name='pred2') |
| 1474 | // cond1 = control_flow_ops.cond(pred1, ...) |
| 1475 | // cond2 = control_flow_ops.cond(pred2, ...) |
| 1476 | // cond3 = control_flow_ops.cond(pred1, use cond1 and cond2) |
| 1477 | // cond4 = control_flow_ops.cond(pred2, use cond1 and cond2) |
| 1478 | // cond3 and cond4 have the same (CondId, AncestorId), but they should not |
no test coverage detected