Skip to content

Commit 7fc9965

Browse files
committed
Replace scheduler "last use count" with "live range delta"
The last use count heuristic attempted to reduce register pressure by preferring nodes that terminate as many live ranges as possible, but it didn't take the number of live ranges created by those nodes into account. Instead, compute a "live range delta" for every node that tracks both created and terminated live ranges: positive values mean the node will end up adding live ranges, while negative values mean the node will end up removing live ranges. Most nodes have one output anyway, so this hasn't made a huge difference yet. It should let us do more interesting things in the future, though.
1 parent 6e62b05 commit 7fc9965

23 files changed

Lines changed: 8084 additions & 8161 deletions

crates/codegen/src/schedule.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use ir::{
1717
schedule::{ScheduleContext, schedule_early, schedule_late},
1818
valgraph::{DepValue, Node, ValGraph},
1919
valwalk::{
20-
CfgPreorderInfo, DefUseSuccs, GraphWalkInfo, dataflow_inputs, dataflow_preds,
21-
dataflow_succs, def_use_preds, raw_def_use_succs,
20+
CfgPreorderInfo, DefUseSuccs, GraphWalkInfo, dataflow_inputs, dataflow_outputs,
21+
dataflow_preds, dataflow_succs, def_use_preds, raw_def_use_succs,
2222
},
2323
};
2424
use log::trace;
@@ -419,9 +419,9 @@ impl PartialEq for ReadyNode {
419419
struct BlockNodeData {
420420
/// The number of block-local predecessors of this node that have not yet been scheduled.
421421
unscheduled_preds: u32,
422-
/// The number of deduplicated data inputs for which this node is the last use point in the
423-
/// block.
424-
last_use_count: u32,
422+
/// The number of live ranges forward execution of this instruction is expected to add. May be
423+
/// negative if the instruction terminates more live ranges than it creates.
424+
live_range_delta: i32,
425425
/// A deduplicated list of all data inputs to this node.
426426
unique_inputs: EntityList<DepValue>,
427427
}
@@ -479,7 +479,7 @@ impl<'a> BlockScheduler<'a> {
479479
let node_data = match self.block_node_data.entry(node) {
480480
Entry::Vacant(entry) => entry.insert(BlockNodeData {
481481
unscheduled_preds,
482-
last_use_count: 0,
482+
live_range_delta: 0,
483483
unique_inputs: EntityList::from_iter(
484484
dataflow_inputs(self.graph, node),
485485
&mut self.unique_input_pool,
@@ -498,15 +498,18 @@ impl<'a> BlockScheduler<'a> {
498498
}
499499
}
500500

501-
// Now that all outstanding uses across the block have been counted, bump last use counts
502-
// and enqueue nodes.
501+
// Now that all outstanding uses across the block have been counted, compute live range
502+
// deltas and enqueue nodes.
503503
for &node in nodes {
504504
let node_data = self.block_node_data.get_mut(&node).unwrap();
505+
506+
node_data.live_range_delta = dataflow_outputs(self.graph, node).count() as i32;
507+
505508
for &input in node_data.unique_inputs.as_slice(&self.unique_input_pool) {
506509
if self.block_value_data[&input].outstanding_uses == 1 {
507-
// This node is the only use of the input in the block, bump its last use count
508-
// now.
509-
node_data.last_use_count += 1;
510+
// This node is the only use of the input in the block, so it terminates the
511+
// input's live range.
512+
node_data.live_range_delta -= 1;
510513
}
511514
}
512515
self.enqueue_if_ready(node);
@@ -527,9 +530,9 @@ impl<'a> BlockScheduler<'a> {
527530
}
528531

529532
trace!(
530-
" place: node {} [luc {}, cp {}]",
533+
" place: node {} [lrg delta {}, cp {}]",
531534
node.as_u32(),
532-
self.block_node_data[&node].last_use_count,
535+
self.block_node_data[&node].live_range_delta,
533536
self.node_cp_lengths[node],
534537
);
535538

@@ -574,8 +577,8 @@ impl<'a> BlockScheduler<'a> {
574577
debug_assert!(value_data.outstanding_uses > 0);
575578
value_data.outstanding_uses -= 1;
576579
if value_data.outstanding_uses == 1 {
577-
// The remaining user of this value is now the last in the block, so bump its
578-
// last use count and report it to the caller.
580+
// The remaining user of this value is now the last in the block, so adjust its live
581+
// range delta and report it to the caller.
579582

580583
// This search happens at most once per value during scheduling of the block (and
581584
// only walks edges leading into the block), so the total complexity here is still
@@ -593,7 +596,7 @@ impl<'a> BlockScheduler<'a> {
593596
self.block_node_data
594597
.get_mut(&last_user)
595598
.unwrap()
596-
.last_use_count += 1;
599+
.live_range_delta -= 1;
597600
// Note: this node may be pushed multiple times if several of its inputs are
598601
// last users.
599602
updated_last_users.push(last_user);
@@ -620,9 +623,14 @@ impl<'a> BlockScheduler<'a> {
620623
}
621624

622625
fn node_prio(&self, node: Node) -> u64 {
623-
// Prefer last use count, and fall back to CP length when tied.
624-
((self.block_node_data[&node].last_use_count as u64) << 32)
625-
| self.node_cp_lengths[node] as u64
626+
// We want lower `live_range_delta` values to have higher priority, so we need an unsigned
627+
// value that increases when `live_range_delta` decreases. Accomplish this by centering
628+
// `-live_range_delta` around the middle of the unsigned range.
629+
let biased_delta =
630+
(1u32 << 31).wrapping_sub(self.block_node_data[&node].live_range_delta as u32);
631+
632+
// Prefer reducing live ranges, and fall back to CP length when tied.
633+
((biased_delta as u64) << 32) | self.node_cp_lengths[node] as u64
626634
}
627635

628636
fn is_block_interior_node(&self, node: Node) -> bool {

crates/filetests/cases/codegen/iota.spdr

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@ func @iota(ptr, i64) {
66
# nextln: 000001: 48 89 e5 mov rbp, rsp
77
# nextln: 000004: 33 c0 xor eax, eax
88
# nextln: 000006: 48 85 f6 test rsi, rsi
9-
# nextln: 000009: 0f 84 22 00 00 00 je 0x31
10-
# nextln: 00000f: 48 89 c2 mov rdx, rax
11-
# nextln: 000012: 48 c1 e2 03 shl rdx, 3
12-
# nextln: 000016: 48 03 d7 add rdx, rdi
13-
# nextln: 000019: 48 8d 48 01 lea rcx, [rax + 1]
14-
# nextln: 00001d: 48 89 02 mov qword ptr [rdx], rax
15-
# nextln: 000020: 48 3b ce cmp rcx, rsi
16-
# nextln: 000023: 0f 84 08 00 00 00 je 0x31
17-
# nextln: 000029: 48 89 c8 mov rax, rcx
18-
# nextln: 00002c: e9 de ff ff ff jmp 0xf
19-
# nextln: 000031: 5d pop rbp
20-
# nextln: 000032: c3 ret
9+
# nextln: 000009: 0f 84 1a 00 00 00 je 0x29
10+
# nextln: 00000f: 48 89 c1 mov rcx, rax
11+
# nextln: 000012: 48 c1 e1 03 shl rcx, 3
12+
# nextln: 000016: 48 03 cf add rcx, rdi
13+
# nextln: 000019: 48 89 01 mov qword ptr [rcx], rax
14+
# nextln: 00001c: 48 83 c0 01 add rax, 1
15+
# nextln: 000020: 48 3b c6 cmp rax, rsi
16+
# nextln: 000023: 0f 85 e6 ff ff ff jne 0xf
17+
# nextln: 000029: 5d pop rbp
18+
# nextln: 00002a: c3 ret
2119

2220
%entry_ctrl:ctrl, %arr:ptr, %n:i64 = entry
2321
%zero:i64 = iconst 0

crates/filetests/cases/codegen/sum_loop.spdr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ func @sum_to_n_with_stack:i32(i32) {
4343
# nextln: 000011: 8b 44 24 04 mov eax, dword ptr [rsp + 4]
4444
# nextln: 000015: 85 c0 test eax, eax
4545
# nextln: 000017: 0f 84 1a 00 00 00 je 0x37
46-
# nextln: 00001d: 8b 4c 24 04 mov ecx, dword ptr [rsp + 4]
47-
# nextln: 000021: 89 c8 mov eax, ecx
48-
# nextln: 000023: 83 e8 01 sub eax, 1
46+
# nextln: 00001d: 8b 44 24 04 mov eax, dword ptr [rsp + 4]
47+
# nextln: 000021: 89 c1 mov ecx, eax
48+
# nextln: 000023: 83 e9 01 sub ecx, 1
4949
# nextln: 000026: 8b 14 24 mov edx, dword ptr [rsp]
50-
# nextln: 000029: 03 ca add ecx, edx
51-
# nextln: 00002b: 89 44 24 04 mov dword ptr [rsp + 4], eax
52-
# nextln: 00002f: 89 0c 24 mov dword ptr [rsp], ecx
50+
# nextln: 000029: 89 4c 24 04 mov dword ptr [rsp + 4], ecx
51+
# nextln: 00002d: 03 c2 add eax, edx
52+
# nextln: 00002f: 89 04 24 mov dword ptr [rsp], eax
5353
# nextln: 000032: e9 da ff ff ff jmp 0x11
5454
# nextln: 000037: 8b 04 24 mov eax, dword ptr [rsp]
5555
# nextln: 00003a: 48 83 c4 10 add rsp, 0x10

crates/filetests/cases/codegen/tdn/calc_loop_nest_debug_canon.spdr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ func @"System.Int32 Tests.Program::Test(System.Int32)":i32(i32) {
143143
# nextln: 00020d: 8b 44 24 54 mov eax, dword ptr [rsp + 0x54]
144144
# nextln: 000211: 8b 54 24 0c mov edx, dword ptr [rsp + 0xc]
145145
# nextln: 000215: 8b 5c 24 10 mov ebx, dword ptr [rsp + 0x10]
146-
# nextln: 000219: 03 d3 add edx, ebx
147-
# nextln: 00021b: 03 c2 add eax, edx
146+
# nextln: 000219: 03 da add ebx, edx
147+
# nextln: 00021b: 03 c3 add eax, ebx
148148
# nextln: 00021d: 89 44 24 54 mov dword ptr [rsp + 0x54], eax
149149
# nextln: 000221: 44 89 5c 24 48 mov dword ptr [rsp + 0x48], r11d
150150
# nextln: 000226: 8b 5c 24 48 mov ebx, dword ptr [rsp + 0x48]
@@ -227,8 +227,8 @@ func @"System.Int32 Tests.Program::Test(System.Int32)":i32(i32) {
227227
# nextln: 00031c: 8b 44 24 54 mov eax, dword ptr [rsp + 0x54]
228228
# nextln: 000320: 8b 54 24 20 mov edx, dword ptr [rsp + 0x20]
229229
# nextln: 000324: 8b 5c 24 24 mov ebx, dword ptr [rsp + 0x24]
230-
# nextln: 000328: 03 da add ebx, edx
231-
# nextln: 00032a: 03 c3 add eax, ebx
230+
# nextln: 000328: 03 d3 add edx, ebx
231+
# nextln: 00032a: 03 c2 add eax, edx
232232
# nextln: 00032c: 89 44 24 54 mov dword ptr [rsp + 0x54], eax
233233
# nextln: 000330: 8b 44 24 44 mov eax, dword ptr [rsp + 0x44]
234234
# nextln: 000334: 83 c0 01 add eax, 1

0 commit comments

Comments
 (0)