Performs various optimizations on branches pointing at the current label.
(&mut self, ctrl_plane: &mut ControlPlane)
| 997 | |
| 998 | /// Performs various optimizations on branches pointing at the current label. |
| 999 | pub fn optimize_branches(&mut self, ctrl_plane: &mut ControlPlane) { |
| 1000 | if ctrl_plane.get_decision() { |
| 1001 | return; |
| 1002 | } |
| 1003 | |
| 1004 | self.lazily_clear_labels_at_tail(); |
| 1005 | // Invariants valid at this point. |
| 1006 | |
| 1007 | trace!( |
| 1008 | "enter optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}", |
| 1009 | self.latest_branches, self.labels_at_tail, self.pending_fixup_records |
| 1010 | ); |
| 1011 | |
| 1012 | // We continue to munch on branches at the tail of the buffer until no |
| 1013 | // more rules apply. Note that the loop only continues if a branch is |
| 1014 | // actually truncated (or if labels are redirected away from a branch), |
| 1015 | // so this always makes progress. |
| 1016 | while let Some(b) = self.latest_branches.last() { |
| 1017 | let cur_off = self.cur_offset(); |
| 1018 | trace!("optimize_branches: last branch {:?} at off {}", b, cur_off); |
| 1019 | // If there has been any code emission since the end of the last branch or |
| 1020 | // label definition, then there's nothing we can edit (because we |
| 1021 | // don't move code once placed, only back up and overwrite), so |
| 1022 | // clear the records and finish. |
| 1023 | if b.end < cur_off { |
| 1024 | break; |
| 1025 | } |
| 1026 | |
| 1027 | // If the "labels at this branch" list on this branch is |
| 1028 | // longer than a threshold, don't do any simplification, |
| 1029 | // and let the branch remain to separate those labels from |
| 1030 | // the current tail. This avoids quadratic behavior (see |
| 1031 | // #3468): otherwise, if a long string of "goto next; |
| 1032 | // next:" patterns are emitted, all of the labels will |
| 1033 | // coalesce into a long list of aliases for the current |
| 1034 | // buffer tail. We must track all aliases of the current |
| 1035 | // tail for correctness, but we are also allowed to skip |
| 1036 | // optimization (removal) of any branch, so we take the |
| 1037 | // escape hatch here and let it stand. In effect this |
| 1038 | // "spreads" the many thousands of labels in the |
| 1039 | // pathological case among an actual (harmless but |
| 1040 | // suboptimal) instruction once per N labels. |
| 1041 | if b.labels_at_this_branch.len() > LABEL_LIST_THRESHOLD { |
| 1042 | break; |
| 1043 | } |
| 1044 | |
| 1045 | // Invariant: we are looking at a branch that ends at the tail of |
| 1046 | // the buffer. |
| 1047 | |
| 1048 | // For any branch, conditional or unconditional: |
| 1049 | // - If the target is a label at the current offset, then remove |
| 1050 | // the conditional branch, and reset all labels that targeted |
| 1051 | // the current offset (end of branch) to the truncated |
| 1052 | // end-of-code. |
| 1053 | // |
| 1054 | // Preserves execution semantics: a branch to its own fallthrough |
| 1055 | // address is equivalent to a no-op; in both cases, nextPC is the |
| 1056 | // fallthrough. |
no test coverage detected