1 //! Registers, the Universe thereof, and printing.
2 //!
3 //! These are ordered by sequence number, as required in the Universe.  The strange ordering is
4 //! intended to make callee-save registers available before caller-saved ones.  This is a net win
5 //! provided that each function makes at least one onward call.  It'll be a net loss for leaf
6 //! functions, and we should change the ordering in that case, so as to make caller-save regs
7 //! available first.
8 //!
9 //! TODO Maybe have two different universes, one for leaf functions and one for non-leaf functions?
10 //! Also, they will have to be ABI dependent.  Need to find a way to avoid constructing a universe
11 //! for each function we compile.
12 
13 use alloc::vec::Vec;
14 use std::string::String;
15 
16 use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES};
17 
18 use crate::machinst::pretty_print::ShowWithRRU;
19 use crate::settings;
20 
21 // Hardware encodings for a few registers.
22 
23 pub const ENC_RBX: u8 = 3;
24 pub const ENC_RSP: u8 = 4;
25 pub const ENC_RBP: u8 = 5;
26 pub const ENC_R12: u8 = 12;
27 pub const ENC_R13: u8 = 13;
28 pub const ENC_R14: u8 = 14;
29 pub const ENC_R15: u8 = 15;
30 
gpr(enc: u8, index: u8) -> Reg31 fn gpr(enc: u8, index: u8) -> Reg {
32     Reg::new_real(RegClass::I64, enc, index)
33 }
34 
r12() -> Reg35 pub(crate) fn r12() -> Reg {
36     gpr(ENC_R12, 0)
37 }
r13() -> Reg38 pub(crate) fn r13() -> Reg {
39     gpr(ENC_R13, 1)
40 }
r14() -> Reg41 pub(crate) fn r14() -> Reg {
42     gpr(ENC_R14, 2)
43 }
r15() -> Reg44 pub(crate) fn r15() -> Reg {
45     gpr(ENC_R15, 3)
46 }
rbx() -> Reg47 pub(crate) fn rbx() -> Reg {
48     gpr(ENC_RBX, 4)
49 }
rsi() -> Reg50 pub(crate) fn rsi() -> Reg {
51     gpr(6, 5)
52 }
rdi() -> Reg53 pub(crate) fn rdi() -> Reg {
54     gpr(7, 6)
55 }
rax() -> Reg56 pub(crate) fn rax() -> Reg {
57     gpr(0, 7)
58 }
rcx() -> Reg59 pub(crate) fn rcx() -> Reg {
60     gpr(1, 8)
61 }
rdx() -> Reg62 pub(crate) fn rdx() -> Reg {
63     gpr(2, 9)
64 }
r8() -> Reg65 pub(crate) fn r8() -> Reg {
66     gpr(8, 10)
67 }
r9() -> Reg68 pub(crate) fn r9() -> Reg {
69     gpr(9, 11)
70 }
r10() -> Reg71 pub(crate) fn r10() -> Reg {
72     gpr(10, 12)
73 }
r11() -> Reg74 pub(crate) fn r11() -> Reg {
75     gpr(11, 13)
76 }
77 
fpr(enc: u8, index: u8) -> Reg78 fn fpr(enc: u8, index: u8) -> Reg {
79     Reg::new_real(RegClass::V128, enc, index)
80 }
xmm0() -> Reg81 fn xmm0() -> Reg {
82     fpr(0, 14)
83 }
xmm1() -> Reg84 fn xmm1() -> Reg {
85     fpr(1, 15)
86 }
xmm2() -> Reg87 fn xmm2() -> Reg {
88     fpr(2, 16)
89 }
xmm3() -> Reg90 fn xmm3() -> Reg {
91     fpr(3, 17)
92 }
xmm4() -> Reg93 fn xmm4() -> Reg {
94     fpr(4, 18)
95 }
xmm5() -> Reg96 fn xmm5() -> Reg {
97     fpr(5, 19)
98 }
xmm6() -> Reg99 fn xmm6() -> Reg {
100     fpr(6, 20)
101 }
xmm7() -> Reg102 fn xmm7() -> Reg {
103     fpr(7, 21)
104 }
xmm8() -> Reg105 fn xmm8() -> Reg {
106     fpr(8, 22)
107 }
xmm9() -> Reg108 fn xmm9() -> Reg {
109     fpr(9, 23)
110 }
xmm10() -> Reg111 fn xmm10() -> Reg {
112     fpr(10, 24)
113 }
xmm11() -> Reg114 fn xmm11() -> Reg {
115     fpr(11, 25)
116 }
xmm12() -> Reg117 fn xmm12() -> Reg {
118     fpr(12, 26)
119 }
xmm13() -> Reg120 fn xmm13() -> Reg {
121     fpr(13, 27)
122 }
xmm14() -> Reg123 fn xmm14() -> Reg {
124     fpr(14, 28)
125 }
xmm15() -> Reg126 fn xmm15() -> Reg {
127     fpr(15, 29)
128 }
129 
rsp() -> Reg130 pub(crate) fn rsp() -> Reg {
131     gpr(ENC_RSP, 30)
132 }
rbp() -> Reg133 pub(crate) fn rbp() -> Reg {
134     gpr(ENC_RBP, 31)
135 }
136 
137 /// Create the register universe for X64.
138 ///
139 /// The ordering of registers matters, as commented in the file doc comment: assumes the
140 /// calling-convention is SystemV, at the moment.
create_reg_universe_systemv(_flags: &settings::Flags) -> RealRegUniverse141 pub(crate) fn create_reg_universe_systemv(_flags: &settings::Flags) -> RealRegUniverse {
142     let mut regs = Vec::<(RealReg, String)>::new();
143     let mut allocable_by_class = [None; NUM_REG_CLASSES];
144 
145     // Integer regs.
146     let mut base = regs.len();
147 
148     // Callee-saved, in the SystemV x86_64 ABI.
149     regs.push((r12().to_real_reg(), "%r12".into()));
150     regs.push((r13().to_real_reg(), "%r13".into()));
151     regs.push((r14().to_real_reg(), "%r14".into()));
152     regs.push((r15().to_real_reg(), "%r15".into()));
153     regs.push((rbx().to_real_reg(), "%rbx".into()));
154 
155     // Caller-saved, in the SystemV x86_64 ABI.
156     regs.push((rsi().to_real_reg(), "%rsi".into()));
157     regs.push((rdi().to_real_reg(), "%rdi".into()));
158     regs.push((rax().to_real_reg(), "%rax".into()));
159     regs.push((rcx().to_real_reg(), "%rcx".into()));
160     regs.push((rdx().to_real_reg(), "%rdx".into()));
161     regs.push((r8().to_real_reg(), "%r8".into()));
162     regs.push((r9().to_real_reg(), "%r9".into()));
163     regs.push((r10().to_real_reg(), "%r10".into()));
164     regs.push((r11().to_real_reg(), "%r11".into()));
165 
166     allocable_by_class[RegClass::I64.rc_to_usize()] = Some(RegClassInfo {
167         first: base,
168         last: regs.len() - 1,
169         suggested_scratch: Some(r12().get_index()),
170     });
171 
172     // XMM registers
173     base = regs.len();
174     regs.push((xmm0().to_real_reg(), "%xmm0".into()));
175     regs.push((xmm1().to_real_reg(), "%xmm1".into()));
176     regs.push((xmm2().to_real_reg(), "%xmm2".into()));
177     regs.push((xmm3().to_real_reg(), "%xmm3".into()));
178     regs.push((xmm4().to_real_reg(), "%xmm4".into()));
179     regs.push((xmm5().to_real_reg(), "%xmm5".into()));
180     regs.push((xmm6().to_real_reg(), "%xmm6".into()));
181     regs.push((xmm7().to_real_reg(), "%xmm7".into()));
182     regs.push((xmm8().to_real_reg(), "%xmm8".into()));
183     regs.push((xmm9().to_real_reg(), "%xmm9".into()));
184     regs.push((xmm10().to_real_reg(), "%xmm10".into()));
185     regs.push((xmm11().to_real_reg(), "%xmm11".into()));
186     regs.push((xmm12().to_real_reg(), "%xmm12".into()));
187     regs.push((xmm13().to_real_reg(), "%xmm13".into()));
188     regs.push((xmm14().to_real_reg(), "%xmm14".into()));
189     regs.push((xmm15().to_real_reg(), "%xmm15".into()));
190 
191     allocable_by_class[RegClass::V128.rc_to_usize()] = Some(RegClassInfo {
192         first: base,
193         last: regs.len() - 1,
194         suggested_scratch: Some(xmm15().get_index()),
195     });
196 
197     // Other regs, not available to the allocator.
198     let allocable = regs.len();
199     regs.push((rsp().to_real_reg(), "%rsp".into()));
200     regs.push((rbp().to_real_reg(), "%rbp".into()));
201 
202     RealRegUniverse {
203         regs,
204         allocable,
205         allocable_by_class,
206     }
207 }
208 
209 /// If `ireg` denotes an I64-classed reg, make a best-effort attempt to show its name at some
210 /// smaller size (4, 2 or 1 bytes).
show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: u8) -> String211 pub fn show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
212     let mut s = reg.show_rru(mb_rru);
213 
214     if reg.get_class() != RegClass::I64 || size == 8 {
215         // We can't do any better.
216         return s;
217     }
218 
219     if reg.is_real() {
220         // Change (eg) "rax" into "eax", "ax" or "al" as appropriate.  This is something one could
221         // describe diplomatically as "a kludge", but it's only debug code.
222         let remapper = match s.as_str() {
223             "%rax" => Some(["%eax", "%ax", "%al"]),
224             "%rbx" => Some(["%ebx", "%bx", "%bl"]),
225             "%rcx" => Some(["%ecx", "%cx", "%cl"]),
226             "%rdx" => Some(["%edx", "%dx", "%dl"]),
227             "%rsi" => Some(["%esi", "%si", "%sil"]),
228             "%rdi" => Some(["%edi", "%di", "%dil"]),
229             "%rbp" => Some(["%ebp", "%bp", "%bpl"]),
230             "%rsp" => Some(["%esp", "%sp", "%spl"]),
231             "%r8" => Some(["%r8d", "%r8w", "%r8b"]),
232             "%r9" => Some(["%r9d", "%r9w", "%r9b"]),
233             "%r10" => Some(["%r10d", "%r10w", "%r10b"]),
234             "%r11" => Some(["%r11d", "%r11w", "%r11b"]),
235             "%r12" => Some(["%r12d", "%r12w", "%r12b"]),
236             "%r13" => Some(["%r13d", "%r13w", "%r13b"]),
237             "%r14" => Some(["%r14d", "%r14w", "%r14b"]),
238             "%r15" => Some(["%r15d", "%r15w", "%r15b"]),
239             _ => None,
240         };
241         if let Some(smaller_names) = remapper {
242             match size {
243                 4 => s = smaller_names[0].into(),
244                 2 => s = smaller_names[1].into(),
245                 1 => s = smaller_names[2].into(),
246                 _ => panic!("show_ireg_sized: real"),
247             }
248         }
249     } else {
250         // Add a "l", "w" or "b" suffix to RegClass::I64 vregs used at narrower widths.
251         let suffix = match size {
252             4 => "l",
253             2 => "w",
254             1 => "b",
255             _ => panic!("show_ireg_sized: virtual"),
256         };
257         s = s + suffix;
258     }
259 
260     s
261 }
262