rucc_cost/table.rs
1//! The table a target fills in, and the machinery that makes filling it in completely mandatory.
2//!
3//! Section 40.13 names the failure this module is built around. A target's table is incomplete, a
4//! field is left at zero, and a zero cost makes an operation free, so every heuristic that consults
5//! it goes wrong in the same direction and none of them look broken. The section is explicit about
6//! what a defence has to do: it "must check every field, not merely that the struct was
7//! constructed".
8//!
9//! Rust will not do that on its own. A struct literal that names every field compiles, and so does
10//! one that names half of them and ends in `..Default::default()`, and the second is how a table
11//! ends up with a zero nobody chose. So [`CostTable`] has no `Default`, no public fields to
12//! construct it through, and one way in: a builder that remembers which fields were set and
13//! refuses to hand over a table while any are missing. The completeness check is the constructor,
14//! which means it cannot be the test somebody forgot to write.
15//!
16//! # Two groups, per section 40.3
17//!
18//! GCC's `struct processor_costs` has about 107 fields split in two, and the comment at
19//! `gcc/config/i386/i386.h:114` says why: the register allocator's costs for moving a value between
20//! two places and the expression evaluator's costs for the same operations are different questions
21//! with different answers. The fields below keep that split by name, `move_*` for the allocator and
22//! everything else for the evaluator.
23//!
24//! This table is smaller than GCC's, and the parts that are missing are missing because rucc has no
25//! pass that would read them yet: the vector and mask register costs, the gather and scatter
26//! formulas, the `memcpy` and `memset` strategy tables, the cache and prefetch parameters, and the
27//! alignment strings. Each of those belongs with the pass that needs it, and adding a field nobody
28//! reads is adding a number nobody will check.
29
30use crate::{Bytes, Cycles};
31
32/// An integer width, in the four sizes the machine has instructions for.
33///
34/// Four rather than GCC's five. `mult_init[5]` and `divide[5]` on x86-64 index by mode up to
35/// `TImode`, and rucc lowers 128-bit arithmetic to calls or to pairs of 64-bit operations rather
36/// than costing it as one instruction, so a fifth entry would be a number no pass could use
37/// honestly.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub enum Width {
40 /// One byte.
41 W8,
42 /// Two bytes.
43 W16,
44 /// Four bytes.
45 W32,
46 /// Eight bytes.
47 W64,
48}
49
50impl Width {
51 /// Every width, in order, for a table to be written against.
52 pub const ALL: [Self; 4] = [Self::W8, Self::W16, Self::W32, Self::W64];
53
54 /// Where this width sits in a width-indexed field.
55 #[must_use]
56 pub const fn index(self) -> usize {
57 self as usize
58 }
59
60 /// The width in bits, for a caller that has one and wants the other.
61 #[must_use]
62 pub const fn bits(self) -> u32 {
63 match self {
64 Self::W8 => 8,
65 Self::W16 => 16,
66 Self::W32 => 32,
67 Self::W64 => 64,
68 }
69 }
70
71 /// The width a number of bits names, or nothing for a width the machine has no name for.
72 #[must_use]
73 pub const fn from_bits(bits: u32) -> Option<Self> {
74 match bits {
75 8 => Some(Self::W8),
76 16 => Some(Self::W16),
77 32 => Some(Self::W32),
78 64 => Some(Self::W64),
79 _ => None,
80 }
81 }
82}
83
84/// The shape of a memory address, which is what an addressing mode table is indexed by.
85///
86/// Section 40.9 wants these costed per target and wants the complexity counted per structural
87/// feature, and [`AddrMode::complexity`] is that count. A mode the target does not have is
88/// [`Cycles::INFINITE`] in the table rather than absent from it, so that a pass asking about a mode
89/// gets an answer it can compare instead of an `Option` it has to unwrap into a policy.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
91pub enum AddrMode {
92 /// `[base]`.
93 Base,
94 /// `[base + disp]`.
95 BaseDisp,
96 /// `[base + index]`.
97 BaseIndex,
98 /// `[base + index * scale]`.
99 BaseIndexScale,
100 /// `[base + index * scale + disp]`.
101 BaseIndexScaleDisp,
102}
103
104impl AddrMode {
105 /// Every mode, in order.
106 pub const ALL: [Self; 5] = [
107 Self::Base,
108 Self::BaseDisp,
109 Self::BaseIndex,
110 Self::BaseIndexScale,
111 Self::BaseIndexScaleDisp,
112 ];
113
114 /// Where this mode sits in a mode-indexed field.
115 #[must_use]
116 pub const fn index(self) -> usize {
117 self as usize
118 }
119
120 /// How many structural features this mode has, per section 40.9.
121 ///
122 /// One each for a displacement, an index, and a scale on the index. The base is not counted
123 /// because every mode has one, and a feature every mode has cannot discriminate between them.
124 ///
125 /// This is the absolute count. Section 40.9's refinement, that a feature the target has no
126 /// alternative to should not count against a mode, is applied by [`CostTable::addr_cost`],
127 /// which is where the target is known.
128 #[must_use]
129 pub const fn complexity(self) -> u32 {
130 match self {
131 Self::Base => 0,
132 Self::BaseDisp | Self::BaseIndex => 1,
133 Self::BaseIndexScale => 2,
134 Self::BaseIndexScaleDisp => 3,
135 }
136 }
137
138 /// Whether this mode scales its index.
139 #[must_use]
140 pub const fn scales(self) -> bool {
141 matches!(self, Self::BaseIndexScale | Self::BaseIndexScaleDisp)
142 }
143}
144
145/// Which entries of a field are impossible, for the check that two tables agree about capability.
146///
147/// Section 40.13: "The two tables must differ only in numbers, never in capability, and that is
148/// checkable." A capability is spelled [`Cycles::INFINITE`] in this design, so the check is that
149/// the same entries are infinite in both tables, and that is what this reports. A field that
150/// carries no capability, a count or a size, reports nothing rather than reporting false, so that
151/// a count of 8 for speed and 4 for size is a difference in numbers and passes.
152pub trait Capability {
153 /// One entry per costed lane, saying whether that lane is impossible.
154 fn impossible(&self) -> Vec<bool>;
155}
156
157impl Capability for Cycles {
158 fn impossible(&self) -> Vec<bool> {
159 vec![self.is_infinite()]
160 }
161}
162
163impl<const N: usize> Capability for [Cycles; N] {
164 fn impossible(&self) -> Vec<bool> {
165 self.iter().map(|c| c.is_infinite()).collect()
166 }
167}
168
169impl Capability for u32 {
170 fn impossible(&self) -> Vec<bool> {
171 Vec::new()
172 }
173}
174
175impl Capability for Bytes {
176 fn impossible(&self) -> Vec<bool> {
177 Vec::new()
178 }
179}
180
181/// Declares the cost table, its builder, and the two things that make it checkable.
182///
183/// A macro rather than a hand written struct because the completeness check needs the field list
184/// and a hand written list is one somebody adds a field without updating. The field names exist
185/// three times in the output and once in the source, which is the point.
186macro_rules! cost_table {
187 ($( $(#[$meta:meta])* $name:ident : $ty:ty ),+ $(,)?) => {
188 /// What an operation costs on one target at one optimization goal.
189 ///
190 /// Built through [`Builder`] and no other way, per the module documentation. Every field
191 /// is public to read and none of them can be written after the table exists, because a
192 /// target's costs are data and a pass that adjusts them is a pass keeping a policy
193 /// somewhere nobody can find it.
194 #[derive(Debug, Clone, PartialEq, Eq)]
195 pub struct CostTable {
196 $( $(#[$meta])* pub $name: $ty, )+
197 }
198
199 impl CostTable {
200 /// Every field name, in declaration order.
201 pub const FIELDS: &'static [&'static str] = &[ $( stringify!($name) ),+ ];
202
203 /// Which entries of which fields are impossible, per [`Capability`].
204 #[must_use]
205 pub fn capabilities(&self) -> Vec<(&'static str, Vec<bool>)> {
206 vec![ $( (stringify!($name), Capability::impossible(&self.$name)) ),+ ]
207 }
208 }
209
210 /// The one way to build a [`CostTable`].
211 ///
212 /// Every field starts unset, and [`Builder::build`] refuses to produce a table while any
213 /// of them still is. That is section 40.13's completeness check, moved from a test into
214 /// the constructor so that a target added next year cannot skip it.
215 #[derive(Debug, Clone, Default)]
216 pub struct Builder {
217 $( $name: Option<$ty>, )+
218 }
219
220 impl Builder {
221 /// A table with nothing set yet.
222 #[must_use]
223 pub fn new() -> Self {
224 Self { $( $name: None, )+ }
225 }
226
227 $(
228 $(#[$meta])*
229 // One field is called `add`, so one setter is called `add`, and clippy reads that
230 // as a `std::ops::Add` somebody spelled wrong. The setter is named after the field
231 // and the field is named after the operation, which is the property worth keeping.
232 #[allow(clippy::should_implement_trait)]
233 #[must_use]
234 pub fn $name(mut self, value: $ty) -> Self {
235 self.$name = Some(value);
236 self
237 }
238 )+
239
240 /// The fields nobody has set, in declaration order.
241 ///
242 /// Public so that a test can name them, which turns "the table is incomplete" into
243 /// "the table is missing `branch_cost`" without anybody reading a panic message.
244 #[must_use]
245 pub fn missing(&self) -> Vec<&'static str> {
246 let mut missing = Vec::new();
247 $( if self.$name.is_none() { missing.push(stringify!($name)); } )+
248 missing
249 }
250
251 /// The finished table.
252 ///
253 /// # Panics
254 ///
255 /// If any field was left unset, naming them. A target's cost table is written once and
256 /// is a compile time constant of the compiler in every sense that matters, so this
257 /// fires during the tests of whoever added the target and never in front of a user.
258 #[must_use]
259 pub fn build(self) -> CostTable {
260 let missing = self.missing();
261 assert!(
262 missing.is_empty(),
263 "the cost table is missing {} of its {} fields: {}. \
264 A field left unset would be a zero, and a zero cost makes an operation free.",
265 missing.len(),
266 CostTable::FIELDS.len(),
267 missing.join(", "),
268 );
269 CostTable {
270 $( $name: self.$name.expect("checked just above"), )+
271 }
272 }
273 }
274 };
275}
276
277cost_table! {
278 /// A register to register add, which is the operation [`Cycles::ONE`] is defined as.
279 ///
280 /// It is in the table anyway rather than assumed to be one, because a target where the unit
281 /// operation is not an add should say so instead of having its whole table shifted.
282 add: Cycles,
283
284 /// An address computation that does not touch flags, x86-64's `lea`.
285 ///
286 /// Section 40.3 keeps this separate from `add` because whether it is cheaper is exactly the
287 /// kind of microarchitectural fact that varies between cores of the same target.
288 lea: Cycles,
289
290 /// A shift by an amount known at compile time.
291 shift_const: Cycles,
292
293 /// A shift by an amount in a register, which on x86-64 is the expensive one because of the
294 /// flags dependency and the fixed count register.
295 shift_var: Cycles,
296
297 /// A multiply, indexed by [`Width`].
298 mult: [Cycles; 4],
299
300 /// What each set bit in a constant multiplier adds, for deciding when to expand a multiply by
301 /// a constant into shifts and adds.
302 mult_bit: Cycles,
303
304 /// A divide, indexed by [`Width`]. The most expensive integer operation on every target and
305 /// the reason strength reduction of division is worth doing at all.
306 divide: [Cycles; 4],
307
308 /// A sign extension.
309 movsx: Cycles,
310
311 /// A zero extension, which on x86-64 is free for the 32 to 64 case and is not for the others,
312 /// so this is the cost of the ones that are not free.
313 movzx: Cycles,
314
315 /// A register to register move, as the expression evaluator sees it.
316 reg_move: Cycles,
317
318 /// An integer load, indexed by [`Width`], as the register allocator sees it.
319 move_int_load: [Cycles; 4],
320
321 /// An integer store, indexed by [`Width`], as the register allocator sees it.
322 move_int_store: [Cycles; 4],
323
324 /// A move between two integer registers, as the register allocator sees it.
325 ///
326 /// Separate from `reg_move` on purpose, per section 40.3. The allocator asks what a move it is
327 /// about to insert costs, and the evaluator asks what a move already in the program costs, and
328 /// `gcc/config/i386/i386.h:114` says plainly that the two answers can differ.
329 move_int_reg: Cycles,
330
331 /// A floating point load, for the two widths that exist, single then double.
332 move_fp_load: [Cycles; 2],
333
334 /// A floating point store, single then double.
335 move_fp_store: [Cycles; 2],
336
337 /// A move between two floating point registers.
338 move_fp_reg: Cycles,
339
340 /// A move from a floating point register to an integer one, which goes through memory or a
341 /// dedicated instruction and is never free.
342 move_fp_to_int: Cycles,
343
344 /// A move from an integer register to a floating point one.
345 move_int_to_fp: Cycles,
346
347 /// An address of each shape, indexed by [`AddrMode`], per section 40.9.
348 ///
349 /// A mode the target does not have is [`Cycles::INFINITE`], which is what the check that the
350 /// speed and size tables agree about capability reads.
351 addr: [Cycles; 5],
352
353 /// What an unpredictable branch costs when optimizing for speed, per section 40.5.
354 ///
355 /// Only the unpredictable case is a target number. `BRANCH_COST` at
356 /// `gcc/config/i386/i386.h:2023` makes a predictable branch free and a branch costed for size
357 /// worth 2 on every target, and those two are in [`crate::heuristics`] rather than here
358 /// because they are not facts about the machine.
359 branch_cost: Cycles,
360
361 /// What a mispredicted branch costs, per section 40.10.
362 ///
363 /// The number that decides whether a switch becomes a jump table, because an indirect branch
364 /// with many targets has to be priced as a mispredict and not as a branch.
365 mispredict_penalty: Cycles,
366
367 /// How many scalar moves a block copy may expand to before it becomes a call, per section 40.7.
368 ///
369 /// GCC's `move_ratio`. A count of moves rather than of bytes, because how many moves a copy
370 /// takes depends on the alignment the compiler can prove.
371 move_ratio: u32,
372
373 /// The same for a block fill. GCC's `clear_ratio`.
374 clear_ratio: u32,
375
376 /// The narrowest store worth using, per section 40.7's trimming rule.
377 ///
378 /// A partially dead store is trimmed only to a width at least this wide. Narrowing an 8-byte
379 /// store to a 1-byte store because seven bytes are dead is legal and is usually a store
380 /// forwarding stall, which is the thing this number stops.
381 cheapest_store: Bytes,
382
383 /// How many integer operations the machine issues in parallel, per section 40.8.
384 ///
385 /// The reassociation width. A chain of eight adds becomes a tree only on a machine that can
386 /// execute the tree's independent operations at once, so this is a hardware fact rather than a
387 /// tuning constant, and it defaults to 1 on a new target, meaning no reassociation.
388 reassoc_int: u32,
389
390 /// The same for floating point.
391 ///
392 /// Reassociating floating point needs `-ffast-math` whatever this says, because the
393 /// transformation is not value preserving. This is only how wide the tree may be once that
394 /// question has been answered somewhere else.
395 reassoc_fp: u32,
396}
397
398impl CostTable {
399 /// A fresh builder, which is the only way to a table.
400 #[must_use]
401 pub fn builder() -> Builder {
402 Builder::new()
403 }
404
405 /// What a multiply of this width costs.
406 #[must_use]
407 pub fn mult_of(&self, width: Width) -> Cycles {
408 self.mult[width.index()]
409 }
410
411 /// What a divide of this width costs.
412 #[must_use]
413 pub fn divide_of(&self, width: Width) -> Cycles {
414 self.divide[width.index()]
415 }
416
417 /// What an integer load of this width costs the allocator.
418 #[must_use]
419 pub fn int_load(&self, width: Width) -> Cycles {
420 self.move_int_load[width.index()]
421 }
422
423 /// What an integer store of this width costs the allocator.
424 #[must_use]
425 pub fn int_store(&self, width: Width) -> Cycles {
426 self.move_int_store[width.index()]
427 }
428
429 /// Whether the target has this addressing mode at all.
430 #[must_use]
431 pub fn has_addr(&self, mode: AddrMode) -> bool {
432 !self.addr[mode.index()].is_infinite()
433 }
434
435 /// What an address of this shape costs, with the complexity counted relative to the target.
436 ///
437 /// Section 40.9's refinement, and the comment it comes from at
438 /// `gcc/tree-ssa-loop-ivopts.cc:4799`: "Don't increase the complexity of adding a scaled index
439 /// if it's the only kind of index that the target allows". A feature the target offers no
440 /// alternative to is not a complication, and counting it as one makes every address on that
441 /// target look complicated, which is the same as the tiebreak not working.
442 #[must_use]
443 pub fn addr_cost(&self, mode: AddrMode) -> crate::Cost {
444 let cycles = self.addr[mode.index()];
445 if cycles.is_infinite() {
446 return crate::Cost::INFINITE;
447 }
448 let mut complexity = mode.complexity();
449 // The scale is free to write down if there is no unscaled index mode to write instead.
450 if mode.scales() && !self.has_addr(AddrMode::BaseIndex) {
451 complexity -= 1;
452 }
453 crate::Cost::new(cycles, complexity)
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::{AddrMode, Builder, CostTable, Width};
460 use crate::{Bytes, Cost, Cycles};
461
462 /// A table with every field set to something, for tests about the mechanism rather than the
463 /// numbers. The numbers a real target uses are tested in that target's own module.
464 fn filled() -> Builder {
465 let one = Cycles::ONE;
466 CostTable::builder()
467 .add(one)
468 .lea(one)
469 .shift_const(one)
470 .shift_var(one)
471 .mult([one; 4])
472 .mult_bit(one)
473 .divide([one; 4])
474 .movsx(one)
475 .movzx(one)
476 .reg_move(one)
477 .move_int_load([one; 4])
478 .move_int_store([one; 4])
479 .move_int_reg(one)
480 .move_fp_load([one; 2])
481 .move_fp_store([one; 2])
482 .move_fp_reg(one)
483 .move_fp_to_int(one)
484 .move_int_to_fp(one)
485 .addr([one; 5])
486 .branch_cost(one)
487 .mispredict_penalty(one)
488 .move_ratio(8)
489 .clear_ratio(8)
490 .cheapest_store(Bytes(4))
491 .reassoc_int(1)
492 .reassoc_fp(1)
493 }
494
495 #[test]
496 fn a_full_table_builds() {
497 let table = filled().build();
498 assert_eq!(table.add, Cycles::ONE);
499 assert!(!CostTable::FIELDS.is_empty());
500 }
501
502 #[test]
503 fn an_empty_builder_is_missing_every_field() {
504 assert_eq!(Builder::new().missing(), CostTable::FIELDS);
505 }
506
507 #[test]
508 #[should_panic(expected = "branch_cost")]
509 fn a_table_missing_a_field_does_not_build_and_says_which() {
510 // The failure section 40.13 is about. It has to be impossible to reach a table with a
511 // field nobody set, because that field would read as zero and a zero cost is free.
512 let mut incomplete = filled();
513 incomplete.branch_cost = None;
514 let _ = incomplete.build();
515 }
516
517 #[test]
518 fn setting_a_field_twice_keeps_the_second() {
519 let table = filled().add(Cycles::insns(7)).build();
520 assert_eq!(table.add, Cycles::insns(7));
521 }
522
523 #[test]
524 fn every_field_name_is_distinct() {
525 // The macro would happily declare two fields with the same name and the compiler would
526 // stop it, but the name list is what the completeness message prints, so it is worth
527 // knowing that two entries in it never mean the same field.
528 let mut names = CostTable::FIELDS.to_vec();
529 names.sort_unstable();
530 let before = names.len();
531 names.dedup();
532 assert_eq!(names.len(), before);
533 }
534
535 #[test]
536 fn widths_index_their_own_slots() {
537 for (slot, width) in Width::ALL.iter().enumerate() {
538 assert_eq!(width.index(), slot);
539 assert_eq!(Width::from_bits(width.bits()), Some(*width));
540 }
541 assert_eq!(Width::from_bits(128), None);
542 assert_eq!(Width::from_bits(1), None);
543 }
544
545 #[test]
546 fn an_address_gets_one_point_of_complexity_per_feature() {
547 assert_eq!(AddrMode::Base.complexity(), 0);
548 assert_eq!(AddrMode::BaseDisp.complexity(), 1);
549 assert_eq!(AddrMode::BaseIndex.complexity(), 1);
550 assert_eq!(AddrMode::BaseIndexScale.complexity(), 2);
551 assert_eq!(AddrMode::BaseIndexScaleDisp.complexity(), 3);
552 }
553
554 #[test]
555 fn a_mode_the_target_lacks_is_impossible_rather_than_expensive() {
556 let mut addrs = [Cycles::ONE; 5];
557 addrs[AddrMode::BaseIndexScaleDisp.index()] = Cycles::INFINITE;
558 let table = filled().addr(addrs).build();
559 assert!(!table.has_addr(AddrMode::BaseIndexScaleDisp));
560 assert_eq!(table.addr_cost(AddrMode::BaseIndexScaleDisp), Cost::INFINITE);
561 assert!(table.has_addr(AddrMode::Base));
562 }
563
564 #[test]
565 fn a_scale_the_target_has_no_alternative_to_does_not_count_as_a_complication() {
566 // Section 40.9's refinement. On a machine whose only index mode is scaled, an address
567 // with a scaled index is the plain one, and charging it for the scale would make every
568 // address on that target tie at the same complexity.
569 let mut addrs = [Cycles::ONE; 5];
570 addrs[AddrMode::BaseIndex.index()] = Cycles::INFINITE;
571 let scaled_only = filled().addr(addrs).build();
572 assert_eq!(scaled_only.addr_cost(AddrMode::BaseIndexScale).complexity, 1);
573
574 let both = filled().build();
575 assert_eq!(both.addr_cost(AddrMode::BaseIndexScale).complexity, 2);
576 }
577}