Skip to content

Commit 50431b4

Browse files
authored
Cranelift: Do not track expression depth in egraph cost function (#12248)
We don't have any rules that try to rebalance trees to make them shallower in the mid-end (we determined it was the wrong place to do that kind of thing, since we don't know register pressure at that point) so there is no need to track expression depth in the cost function.
1 parent 0b80a02 commit 50431b4

1 file changed

Lines changed: 25 additions & 85 deletions

File tree

  • cranelift/codegen/src/egraph

cranelift/codegen/src/egraph/cost.rs

Lines changed: 25 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -30,52 +30,20 @@ use crate::ir::Opcode;
3030
/// `finite()` method.) An infinite cost is used to represent a value
3131
/// that cannot be computed, or otherwise serve as a sentinel when
3232
/// performing search for the lowest-cost representation of a value.
33-
#[derive(Clone, Copy, PartialEq, Eq)]
33+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
3434
pub(crate) struct Cost(u32);
3535

3636
impl core::fmt::Debug for Cost {
3737
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3838
if *self == Cost::infinity() {
3939
write!(f, "Cost::Infinite")
4040
} else {
41-
f.debug_struct("Cost::Finite")
42-
.field("op_cost", &self.op_cost())
43-
.field("depth", &self.depth())
44-
.finish()
41+
f.debug_tuple("Cost::Finite").field(&self.cost()).finish()
4542
}
4643
}
4744
}
4845

49-
impl Ord for Cost {
50-
#[inline]
51-
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
52-
// We make sure that the high bits are the op cost and the low bits are
53-
// the depth. This means that we can use normal integer comparison to
54-
// order by op cost and then depth.
55-
//
56-
// We want to break op cost ties with depth (rather than the other way
57-
// around). When the op cost is the same, we prefer shallow and wide
58-
// expressions to narrow and deep expressions and breaking ties with
59-
// `depth` gives us that. For example, `(a + b) + (c + d)` is preferred
60-
// to `((a + b) + c) + d`. This is beneficial because it exposes more
61-
// instruction-level parallelism and shortens live ranges.
62-
self.0.cmp(&other.0)
63-
}
64-
}
65-
66-
impl PartialOrd for Cost {
67-
#[inline]
68-
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
69-
Some(self.cmp(other))
70-
}
71-
}
72-
7346
impl Cost {
74-
const DEPTH_BITS: u8 = 8;
75-
const DEPTH_MASK: u32 = (1 << Self::DEPTH_BITS) - 1;
76-
const OP_COST_MASK: u32 = !Self::DEPTH_MASK;
77-
const MAX_OP_COST: u32 = Self::OP_COST_MASK >> Self::DEPTH_BITS;
78-
7947
pub(crate) fn infinity() -> Cost {
8048
// 2^32 - 1 is, uh, pretty close to infinite... (we use `Cost`
8149
// only for heuristics and always saturate so this suffices!)
@@ -86,39 +54,27 @@ impl Cost {
8654
Cost(0)
8755
}
8856

89-
/// Construct a new `Cost` from the given parts.
90-
///
91-
/// If the opcode cost is greater than or equal to the maximum representable
92-
/// opcode cost, then the resulting `Cost` saturates to infinity.
93-
fn new(opcode_cost: u32, depth: u8) -> Cost {
94-
if opcode_cost >= Self::MAX_OP_COST {
95-
Self::infinity()
96-
} else {
97-
Cost(opcode_cost << Self::DEPTH_BITS | u32::from(depth))
98-
}
99-
}
100-
101-
fn depth(&self) -> u8 {
102-
let depth = self.0 & Self::DEPTH_MASK;
103-
u8::try_from(depth).unwrap()
57+
/// Construct a new `Cost`.
58+
fn new(cost: u32) -> Cost {
59+
Cost(cost)
10460
}
10561

106-
fn op_cost(&self) -> u32 {
107-
(self.0 & Self::OP_COST_MASK) >> Self::DEPTH_BITS
62+
fn cost(&self) -> u32 {
63+
self.0
10864
}
10965

11066
/// Return the cost of an opcode.
11167
fn of_opcode(op: Opcode) -> Cost {
11268
match op {
11369
// Constants.
114-
Opcode::Iconst | Opcode::F32const | Opcode::F64const => Cost::new(1, 0),
70+
Opcode::Iconst | Opcode::F32const | Opcode::F64const => Cost::new(1),
11571

11672
// Extends/reduces.
11773
Opcode::Uextend
11874
| Opcode::Sextend
11975
| Opcode::Ireduce
12076
| Opcode::Iconcat
121-
| Opcode::Isplit => Cost::new(1, 0),
77+
| Opcode::Isplit => Cost::new(1),
12278

12379
// "Simple" arithmetic.
12480
Opcode::Iadd
@@ -129,27 +85,27 @@ impl Cost {
12985
| Opcode::Bnot
13086
| Opcode::Ishl
13187
| Opcode::Ushr
132-
| Opcode::Sshr => Cost::new(3, 0),
88+
| Opcode::Sshr => Cost::new(3),
13389

13490
// "Expensive" arithmetic.
135-
Opcode::Imul => Cost::new(10, 0),
91+
Opcode::Imul => Cost::new(10),
13692

13793
// Everything else.
13894
_ => {
13995
// By default, be slightly more expensive than "simple"
14096
// arithmetic.
141-
let mut c = Cost::new(4, 0);
97+
let mut c = Cost::new(4);
14298

14399
// And then get more expensive as the opcode does more side
144100
// effects.
145101
if op.can_trap() || op.other_side_effects() {
146-
c = c + Cost::new(10, 0);
102+
c = c + Cost::new(10);
147103
}
148104
if op.can_load() {
149-
c = c + Cost::new(20, 0);
105+
c = c + Cost::new(20);
150106
}
151107
if op.can_store() {
152-
c = c + Cost::new(50, 0);
108+
c = c + Cost::new(50);
153109
}
154110

155111
c
@@ -163,12 +119,12 @@ impl Cost {
163119
/// that satisfies `inst_predicates::is_pure_for_egraph()`.
164120
pub(crate) fn of_pure_op(op: Opcode, operand_costs: impl IntoIterator<Item = Self>) -> Self {
165121
let c = Self::of_opcode(op) + operand_costs.into_iter().sum();
166-
Cost::new(c.op_cost(), c.depth().saturating_add(1))
122+
Cost::new(c.cost())
167123
}
168124

169125
/// Compute the cost of an operation in the side-effectful skeleton.
170126
pub(crate) fn of_skeleton_op(op: Opcode, arity: usize) -> Self {
171-
Cost::of_opcode(op) + Cost::new(u32::try_from(arity).unwrap(), (arity != 0) as _)
127+
Cost::of_opcode(op) + Cost::new(u32::try_from(arity).unwrap())
172128
}
173129
}
174130

@@ -188,9 +144,7 @@ impl core::ops::Add<Cost> for Cost {
188144
type Output = Cost;
189145

190146
fn add(self, other: Cost) -> Cost {
191-
let op_cost = self.op_cost().saturating_add(other.op_cost());
192-
let depth = core::cmp::max(self.depth(), other.depth());
193-
Cost::new(op_cost, depth)
147+
Cost::new(self.cost().saturating_add(other.cost()))
194148
}
195149
}
196150

@@ -200,39 +154,25 @@ mod tests {
200154

201155
#[test]
202156
fn add_cost() {
203-
let a = Cost::new(5, 2);
204-
let b = Cost::new(37, 3);
205-
assert_eq!(a + b, Cost::new(42, 3));
206-
assert_eq!(b + a, Cost::new(42, 3));
157+
let a = Cost::new(5);
158+
let b = Cost::new(37);
159+
assert_eq!(a + b, Cost::new(42));
160+
assert_eq!(b + a, Cost::new(42));
207161
}
208162

209163
#[test]
210164
fn add_infinity() {
211-
let a = Cost::new(5, 2);
165+
let a = Cost::new(5);
212166
let b = Cost::infinity();
213167
assert_eq!(a + b, Cost::infinity());
214168
assert_eq!(b + a, Cost::infinity());
215169
}
216170

217171
#[test]
218172
fn op_cost_saturates_to_infinity() {
219-
let a = Cost::new(Cost::MAX_OP_COST - 10, 2);
220-
let b = Cost::new(11, 2);
173+
let a = Cost::new(u32::MAX - 10);
174+
let b = Cost::new(11);
221175
assert_eq!(a + b, Cost::infinity());
222176
assert_eq!(b + a, Cost::infinity());
223177
}
224-
225-
#[test]
226-
fn depth_saturates_to_max_depth() {
227-
let a = Cost::new(10, u8::MAX);
228-
let b = Cost::new(10, 1);
229-
assert_eq!(
230-
Cost::of_pure_op(Opcode::Iconst, [a, b]),
231-
Cost::new(21, u8::MAX)
232-
);
233-
assert_eq!(
234-
Cost::of_pure_op(Opcode::Iconst, [b, a]),
235-
Cost::new(21, u8::MAX)
236-
);
237-
}
238178
}

0 commit comments

Comments
 (0)