1 //! Registers, the Universe thereof, and printing.
2 //!
3 //! These are ordered by sequence number, as required in the Universe.
4 //!
5 //! The caller-saved registers are placed first in order to prefer not to clobber (requiring
6 //! saves/restores in prologue/epilogue code) when possible. Note that there is no other heuristic
7 //! in the backend that will apply such pressure; the register allocator's cost heuristics are not
8 //! aware of the cost of clobber-save/restore code.
9 //!
10 //! One might worry that this pessimizes code with many callsites, where using caller-saves causes
11 //! us to have to save them (as we are the caller) frequently. However, the register allocator
12 //! *should be* aware of *this* cost, because it sees that the call instruction modifies all of the
13 //! caller-saved (i.e., callee-clobbered) registers.
14 //!
15 //! Hence, this ordering encodes pressure in one direction (prefer not to clobber registers that we
16 //! ourselves have to save) and this is balanaced against the RA's pressure in the other direction
17 //! at callsites.
18 
19 use crate::settings;
20 use alloc::vec::Vec;
21 use regalloc::{
22     PrettyPrint, RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES,
23 };
24 use std::string::String;
25 
26 // Hardware encodings (note the special rax, rcx, rdx, rbx order).
27 
28 pub const ENC_RAX: u8 = 0;
29 pub const ENC_RCX: u8 = 1;
30 pub const ENC_RDX: u8 = 2;
31 pub const ENC_RBX: u8 = 3;
32 pub const ENC_RSP: u8 = 4;
33 pub const ENC_RBP: u8 = 5;
34 pub const ENC_RSI: u8 = 6;
35 pub const ENC_RDI: u8 = 7;
36 pub const ENC_R8: u8 = 8;
37 pub const ENC_R9: u8 = 9;
38 pub const ENC_R10: u8 = 10;
39 pub const ENC_R11: u8 = 11;
40 pub const ENC_R12: u8 = 12;
41 pub const ENC_R13: u8 = 13;
42 pub const ENC_R14: u8 = 14;
43 pub const ENC_R15: u8 = 15;
44 
gpr(enc: u8, index: u8) -> Reg45 fn gpr(enc: u8, index: u8) -> Reg {
46     Reg::new_real(RegClass::I64, enc, index)
47 }
48 
rsi() -> Reg49 pub(crate) fn rsi() -> Reg {
50     gpr(ENC_RSI, 16)
51 }
rdi() -> Reg52 pub(crate) fn rdi() -> Reg {
53     gpr(ENC_RDI, 17)
54 }
rax() -> Reg55 pub(crate) fn rax() -> Reg {
56     gpr(ENC_RAX, 18)
57 }
rcx() -> Reg58 pub(crate) fn rcx() -> Reg {
59     gpr(ENC_RCX, 19)
60 }
rdx() -> Reg61 pub(crate) fn rdx() -> Reg {
62     gpr(ENC_RDX, 20)
63 }
r8() -> Reg64 pub(crate) fn r8() -> Reg {
65     gpr(ENC_R8, 21)
66 }
r9() -> Reg67 pub(crate) fn r9() -> Reg {
68     gpr(ENC_R9, 22)
69 }
r10() -> Reg70 pub(crate) fn r10() -> Reg {
71     gpr(ENC_R10, 23)
72 }
r11() -> Reg73 pub(crate) fn r11() -> Reg {
74     gpr(ENC_R11, 24)
75 }
r12() -> Reg76 pub(crate) fn r12() -> Reg {
77     gpr(ENC_R12, 25)
78 }
r13() -> Reg79 pub(crate) fn r13() -> Reg {
80     gpr(ENC_R13, 26)
81 }
r14() -> Reg82 pub(crate) fn r14() -> Reg {
83     gpr(ENC_R14, 27)
84 }
rbx() -> Reg85 pub(crate) fn rbx() -> Reg {
86     gpr(ENC_RBX, 28)
87 }
88 
r15() -> Reg89 pub(crate) fn r15() -> Reg {
90     // r15 is put aside since this is the pinned register.
91     gpr(ENC_R15, 29)
92 }
93 
94 /// The pinned register on this architecture.
95 /// It must be the same as Spidermonkey's HeapReg, as found in this file.
96 /// https://searchfox.org/mozilla-central/source/js/src/jit/x64/Assembler-x64.h#99
pinned_reg() -> Reg97 pub(crate) fn pinned_reg() -> Reg {
98     r15()
99 }
100 
fpr(enc: u8, index: u8) -> Reg101 fn fpr(enc: u8, index: u8) -> Reg {
102     Reg::new_real(RegClass::V128, enc, index)
103 }
104 
xmm0() -> Reg105 pub(crate) fn xmm0() -> Reg {
106     fpr(0, 0)
107 }
xmm1() -> Reg108 pub(crate) fn xmm1() -> Reg {
109     fpr(1, 1)
110 }
xmm2() -> Reg111 pub(crate) fn xmm2() -> Reg {
112     fpr(2, 2)
113 }
xmm3() -> Reg114 pub(crate) fn xmm3() -> Reg {
115     fpr(3, 3)
116 }
xmm4() -> Reg117 pub(crate) fn xmm4() -> Reg {
118     fpr(4, 4)
119 }
xmm5() -> Reg120 pub(crate) fn xmm5() -> Reg {
121     fpr(5, 5)
122 }
xmm6() -> Reg123 pub(crate) fn xmm6() -> Reg {
124     fpr(6, 6)
125 }
xmm7() -> Reg126 pub(crate) fn xmm7() -> Reg {
127     fpr(7, 7)
128 }
xmm8() -> Reg129 pub(crate) fn xmm8() -> Reg {
130     fpr(8, 8)
131 }
xmm9() -> Reg132 pub(crate) fn xmm9() -> Reg {
133     fpr(9, 9)
134 }
xmm10() -> Reg135 pub(crate) fn xmm10() -> Reg {
136     fpr(10, 10)
137 }
xmm11() -> Reg138 pub(crate) fn xmm11() -> Reg {
139     fpr(11, 11)
140 }
xmm12() -> Reg141 pub(crate) fn xmm12() -> Reg {
142     fpr(12, 12)
143 }
xmm13() -> Reg144 pub(crate) fn xmm13() -> Reg {
145     fpr(13, 13)
146 }
xmm14() -> Reg147 pub(crate) fn xmm14() -> Reg {
148     fpr(14, 14)
149 }
xmm15() -> Reg150 pub(crate) fn xmm15() -> Reg {
151     fpr(15, 15)
152 }
153 
rsp() -> Reg154 pub(crate) fn rsp() -> Reg {
155     gpr(ENC_RSP, 30)
156 }
rbp() -> Reg157 pub(crate) fn rbp() -> Reg {
158     gpr(ENC_RBP, 31)
159 }
160 
161 /// Create the register universe for X64.
162 ///
163 /// The ordering of registers matters, as commented in the file doc comment: assumes the
164 /// calling-convention is SystemV, at the moment.
create_reg_universe_systemv(flags: &settings::Flags) -> RealRegUniverse165 pub(crate) fn create_reg_universe_systemv(flags: &settings::Flags) -> RealRegUniverse {
166     let mut regs = Vec::<(RealReg, String)>::new();
167     let mut allocable_by_class = [None; NUM_REG_CLASSES];
168 
169     let use_pinned_reg = flags.enable_pinned_reg();
170 
171     // XMM registers
172     let first_fpr = regs.len();
173     regs.push((xmm0().to_real_reg(), "%xmm0".into()));
174     regs.push((xmm1().to_real_reg(), "%xmm1".into()));
175     regs.push((xmm2().to_real_reg(), "%xmm2".into()));
176     regs.push((xmm3().to_real_reg(), "%xmm3".into()));
177     regs.push((xmm4().to_real_reg(), "%xmm4".into()));
178     regs.push((xmm5().to_real_reg(), "%xmm5".into()));
179     regs.push((xmm6().to_real_reg(), "%xmm6".into()));
180     regs.push((xmm7().to_real_reg(), "%xmm7".into()));
181     regs.push((xmm8().to_real_reg(), "%xmm8".into()));
182     regs.push((xmm9().to_real_reg(), "%xmm9".into()));
183     regs.push((xmm10().to_real_reg(), "%xmm10".into()));
184     regs.push((xmm11().to_real_reg(), "%xmm11".into()));
185     regs.push((xmm12().to_real_reg(), "%xmm12".into()));
186     regs.push((xmm13().to_real_reg(), "%xmm13".into()));
187     regs.push((xmm14().to_real_reg(), "%xmm14".into()));
188     regs.push((xmm15().to_real_reg(), "%xmm15".into()));
189     let last_fpr = regs.len() - 1;
190 
191     // Integer regs.
192     let first_gpr = regs.len();
193 
194     // Caller-saved, in the SystemV x86_64 ABI.
195     regs.push((rsi().to_real_reg(), "%rsi".into()));
196     regs.push((rdi().to_real_reg(), "%rdi".into()));
197     regs.push((rax().to_real_reg(), "%rax".into()));
198     regs.push((rcx().to_real_reg(), "%rcx".into()));
199     regs.push((rdx().to_real_reg(), "%rdx".into()));
200     regs.push((r8().to_real_reg(), "%r8".into()));
201     regs.push((r9().to_real_reg(), "%r9".into()));
202     regs.push((r10().to_real_reg(), "%r10".into()));
203     regs.push((r11().to_real_reg(), "%r11".into()));
204 
205     // Callee-saved, in the SystemV x86_64 ABI.
206     regs.push((r12().to_real_reg(), "%r12".into()));
207     regs.push((r13().to_real_reg(), "%r13".into()));
208     regs.push((r14().to_real_reg(), "%r14".into()));
209 
210     regs.push((rbx().to_real_reg(), "%rbx".into()));
211 
212     // Other regs, not available to the allocator.
213     debug_assert_eq!(r15(), pinned_reg());
214     let allocable = if use_pinned_reg {
215         // The pinned register is not allocatable in this case, so record the length before adding
216         // it.
217         let len = regs.len();
218         regs.push((r15().to_real_reg(), "%r15/pinned".into()));
219         len
220     } else {
221         regs.push((r15().to_real_reg(), "%r15".into()));
222         regs.len()
223     };
224     let last_gpr = allocable - 1;
225 
226     regs.push((rsp().to_real_reg(), "%rsp".into()));
227     regs.push((rbp().to_real_reg(), "%rbp".into()));
228 
229     allocable_by_class[RegClass::I64.rc_to_usize()] = Some(RegClassInfo {
230         first: first_gpr,
231         last: last_gpr,
232         suggested_scratch: Some(r12().get_index()),
233     });
234     allocable_by_class[RegClass::V128.rc_to_usize()] = Some(RegClassInfo {
235         first: first_fpr,
236         last: last_fpr,
237         suggested_scratch: Some(xmm15().get_index()),
238     });
239 
240     // Sanity-check: the index passed to the Reg ctor must match the order in the register list.
241     for (i, reg) in regs.iter().enumerate() {
242         assert_eq!(i, reg.0.get_index());
243     }
244 
245     RealRegUniverse {
246         regs,
247         allocable,
248         allocable_by_class,
249     }
250 }
251 
252 /// If `ireg` denotes an I64-classed reg, make a best-effort attempt to show its name at some
253 /// smaller size (4, 2 or 1 bytes).
show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: u8) -> String254 pub fn show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
255     let mut s = reg.show_rru(mb_rru);
256 
257     if reg.get_class() != RegClass::I64 || size == 8 {
258         // We can't do any better.
259         return s;
260     }
261 
262     if reg.is_real() {
263         // Change (eg) "rax" into "eax", "ax" or "al" as appropriate.  This is something one could
264         // describe diplomatically as "a kludge", but it's only debug code.
265         let remapper = match s.as_str() {
266             "%rax" => Some(["%eax", "%ax", "%al"]),
267             "%rbx" => Some(["%ebx", "%bx", "%bl"]),
268             "%rcx" => Some(["%ecx", "%cx", "%cl"]),
269             "%rdx" => Some(["%edx", "%dx", "%dl"]),
270             "%rsi" => Some(["%esi", "%si", "%sil"]),
271             "%rdi" => Some(["%edi", "%di", "%dil"]),
272             "%rbp" => Some(["%ebp", "%bp", "%bpl"]),
273             "%rsp" => Some(["%esp", "%sp", "%spl"]),
274             "%r8" => Some(["%r8d", "%r8w", "%r8b"]),
275             "%r9" => Some(["%r9d", "%r9w", "%r9b"]),
276             "%r10" => Some(["%r10d", "%r10w", "%r10b"]),
277             "%r11" => Some(["%r11d", "%r11w", "%r11b"]),
278             "%r12" => Some(["%r12d", "%r12w", "%r12b"]),
279             "%r13" => Some(["%r13d", "%r13w", "%r13b"]),
280             "%r14" => Some(["%r14d", "%r14w", "%r14b"]),
281             "%r15" => Some(["%r15d", "%r15w", "%r15b"]),
282             _ => None,
283         };
284         if let Some(smaller_names) = remapper {
285             match size {
286                 4 => s = smaller_names[0].into(),
287                 2 => s = smaller_names[1].into(),
288                 1 => s = smaller_names[2].into(),
289                 _ => panic!("show_ireg_sized: real"),
290             }
291         }
292     } else {
293         // Add a "l", "w" or "b" suffix to RegClass::I64 vregs used at narrower widths.
294         let suffix = match size {
295             4 => "l",
296             2 => "w",
297             1 => "b",
298             _ => panic!("show_ireg_sized: virtual"),
299         };
300         s = s + suffix;
301     }
302 
303     s
304 }
305