1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
//! Branch relaxation and offset computation. //! //! # EBB header offsets //! //! Before we can generate binary machine code for branch instructions, we need to know the final //! offsets of all the EBB headers in the function. This information is encoded in the //! `func.offsets` table. //! //! # Branch relaxation //! //! Branch relaxation is the process of ensuring that all branches in the function have enough //! range to encode their destination. It is common to have multiple branch encodings in an ISA. //! For example, x86 branches can have either an 8-bit or a 32-bit displacement. //! //! On RISC architectures, it can happen that conditional branches have a shorter range than //! unconditional branches: //! //! ```cton //! brz v1, ebb17 //! ``` //! //! can be transformed into: //! //! ```cton //! brnz v1, ebb23 //! jump ebb17 //! ebb23: //! ``` use binemit::CodeOffset; use cursor::{Cursor, FuncCursor}; use ir::{Function, InstructionData, Opcode}; use isa::{EncInfo, TargetIsa}; use iterators::IteratorExtras; use result::CtonError; /// Relax branches and compute the final layout of EBB headers in `func`. /// /// Fill in the `func.offsets` table so the function is ready for binary emission. pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> Result<CodeOffset, CtonError> { let encinfo = isa.encoding_info(); // Clear all offsets so we can recognize EBBs that haven't been visited yet. func.offsets.clear(); func.offsets.resize(func.dfg.num_ebbs()); // Start by inserting fall through instructions. fallthroughs(func); let mut offset = 0; // The relaxation algorithm iterates to convergence. let mut go_again = true; while go_again { go_again = false; offset = 0; // Visit all instructions in layout order let mut cur = FuncCursor::new(func); while let Some(ebb) = cur.next_ebb() { // Record the offset for `ebb` and make sure we iterate until offsets are stable. if cur.func.offsets[ebb] != offset { debug_assert!( cur.func.offsets[ebb] < offset, "Code shrinking during relaxation" ); cur.func.offsets[ebb] = offset; go_again = true; } while let Some(inst) = cur.next_inst() { let enc = cur.func.encodings[inst]; let size = encinfo.bytes(enc); // See if this might be a branch that is out of range. if let Some(range) = encinfo.branch_range(enc) { if let Some(dest) = cur.func.dfg[inst].branch_destination() { let dest_offset = cur.func.offsets[dest]; // This could be an out-of-range branch. // Relax it unless the destination offset has not been computed yet. if !range.contains(offset, dest_offset) && (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block()) { offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa); continue; } } } offset += size; } } } Ok(offset) } /// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any /// existing `fallthrough` instructions are correct. fn fallthroughs(func: &mut Function) { for (ebb, succ) in func.layout.ebbs().adjacent_pairs() { let term = func.layout.last_inst(ebb).expect("EBB has no terminator."); if let InstructionData::Jump { ref mut opcode, destination, .. } = func.dfg[term] { match *opcode { Opcode::Fallthrough => { // Somebody used a fall-through instruction before the branch relaxation pass. // Make sure it is correct, i.e. the destination is the layout successor. debug_assert_eq!(destination, succ, "Illegal fall-through in {}", ebb) } Opcode::Jump => { // If this is a jump to the successor EBB, change it to a fall-through. if destination == succ { *opcode = Opcode::Fallthrough; func.encodings[term] = Default::default(); } } _ => {} } } } } /// Relax the branch instruction at `pos` so it can cover the range `offset - dest_offset`. /// /// Return the size of the replacement instructions up to and including the location where `pos` is /// left. fn relax_branch( cur: &mut FuncCursor, offset: CodeOffset, dest_offset: CodeOffset, encinfo: &EncInfo, isa: &TargetIsa, ) -> CodeOffset { let inst = cur.current_inst().unwrap(); dbg!( "Relaxing [{}] {} for {:#x}-{:#x} range", encinfo.display(cur.func.encodings[inst]), cur.func.dfg.display_inst(inst, isa), offset, dest_offset ); // Pick the first encoding that can handle the branch range. let dfg = &cur.func.dfg; let ctrl_type = dfg.ctrl_typevar(inst); if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type).find( |&enc| { let range = encinfo.branch_range(enc).expect("Branch with no range"); if !range.contains(offset, dest_offset) { dbg!(" trying [{}]: out of range", encinfo.display(enc)); false } else if encinfo.operand_constraints(enc) != encinfo.operand_constraints(cur.func.encodings[inst]) { // Conservatively give up if the encoding has different constraints // than the original, so that we don't risk picking a new encoding // which the existing operands don't satisfy. We can't check for // validity directly because we don't have a RegDiversions active so // we don't know which registers are actually in use. dbg!(" trying [{}]: constraints differ", encinfo.display(enc)); false } else { dbg!(" trying [{}]: OK", encinfo.display(enc)); true } }, ) { cur.func.encodings[inst] = enc; return encinfo.bytes(enc); } // Note: On some RISC ISAs, conditional branches have shorter range than unconditional // branches, so one way of extending the range of a conditional branch is to invert its // condition and make it branch over an unconditional jump which has the larger range. // // Splitting the EBB is problematic this late because there may be register diversions in // effect across the conditional branch, and they can't survive the control flow edge to a new // EBB. We have two options for handling that: // // 1. Set a flag on the new EBB that indicates it wants the preserve the register diversions of // its layout predecessor, or // 2. Use an encoding macro for the branch-over-jump pattern so we don't need to split the EBB. // // It seems that 1. would allow us to share code among RISC ISAs that need this. // // We can't allow register diversions to survive from the layout predecessor because the layout // predecessor could contain kill points for some values that are live in this EBB, and // diversions are not automatically cancelled when the live range of a value ends. // This assumes solution 2. above: panic!("No branch in range for {:#x}-{:#x}", offset, dest_offset); }