1use rucc_ir as ir;
33use rucc_mir as mir;
34use rucc_opt::{Callees, Cfg, Dominators, Frequencies, Loops};
35
36pub fn carry(source: &ir::Func, blocks: &[Option<mir::Block>], func: &mut mir::Func) {
43 let cfg = Cfg::new(source);
44 if cfg.entry().is_none() {
45 return;
46 }
47 let doms = Dominators::new(&cfg);
48 let loops = Loops::new(&cfg, &doms);
49 let freqs = Frequencies::of(source, &cfg, &loops, &Callees::nothing());
55 for block in source.blocks() {
56 let Some(&Some(out)) = blocks.get(block.index()) else { continue };
57 let weight = freqs.get(block);
58 func.set_weight(out, mir::Weight::parts(weight.raw()));
59 let Some(term) = source.terminator(block) else { continue };
60 let arms: Vec<ir::Block> = source.successors(term).map(|call| call.block).collect();
61 if arms.len() != func[out].succs.len() {
62 continue;
63 }
64 for (index, arm) in arms.iter().enumerate() {
65 let Some(at) = cfg.successors(block).iter().position(|succ| succ == arm) else {
72 continue;
73 };
74 func.succs_mut(out)[index].weight =
75 mir::Weight::parts(weight.along(freqs.taken(block, at)).raw());
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use rucc_base::Interner;
83 use rucc_ir::{Builder, Func, Opcode, Signature, Type};
84 use rucc_target::x86_64::SYSV;
85
86 use super::*;
87 use crate::elsewhere::Elsewhere;
88 use crate::lower;
89
90 fn lowered(source: &mut Func, names: &mut Interner) -> mir::Func {
92 let out = lower::func(source, names, &SYSV, &Elsewhere::default()).expect("it lowers");
93 let lower::Lowered { mut func, blocks, .. } = out;
94 carry(source, &blocks, &mut func);
95 func
96 }
97
98 fn weights(func: &mir::Func) -> Vec<u64> {
101 func.blocks().map(|block| func[block].weight.raw()).collect()
102 }
103
104 #[test]
105 fn a_function_with_no_branch_in_it_runs_every_block_once() {
106 let mut names = Interner::new();
107 let mut source = Func::new(names.intern("f"), Signature::new());
108 let entry = source.create_block();
109 Builder::new(&mut source, entry).ret(&[]);
110
111 let func = lowered(&mut source, &mut names);
112
113 assert_eq!(weights(&func), [mir::Weight::ONCE.raw()]);
114 }
115
116 #[test]
117 fn the_arms_of_a_branch_add_up_to_the_block_they_leave() {
118 let int = Type::int(32);
119 let mut names = Interner::new();
120 let mut source =
121 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
122 let entry = source.create_block();
123 let cond = source.append_param(entry, Type::int(1));
124 let yes = source.create_block();
125 let no = source.create_block();
126 Builder::new(&mut source, entry).br_if(cond, yes, &[], no, &[]);
127 let mut build = Builder::new(&mut source, yes);
128 let one = build.iconst(int, 1);
129 build.ret(&[one]);
130 let mut build = Builder::new(&mut source, no);
131 let two = build.iconst(int, 2);
132 build.ret(&[two]);
133
134 let func = lowered(&mut source, &mut names);
135
136 let head = func.blocks().next().expect("an entry");
137 let arms: Vec<u64> = func[head].succs.iter().map(|call| call.weight.raw()).collect();
138 assert_eq!(arms.len(), 2);
139 assert_eq!(arms.iter().sum::<u64>(), mir::Weight::ONCE.raw());
140 }
141
142 #[test]
143 fn a_loop_body_runs_more_often_than_the_block_that_follows_it() {
144 let int = Type::int(32);
145 let mut names = Interner::new();
146 let mut source =
147 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
148 let entry = source.create_block();
149 let cond = source.append_param(entry, Type::int(1));
150 let head = source.create_block();
151 let body = source.create_block();
152 let out = source.create_block();
153 Builder::new(&mut source, entry).jump(head, &[]);
154 Builder::new(&mut source, head).br_if(cond, body, &[], out, &[]);
155 Builder::new(&mut source, body).jump(head, &[]);
156 let mut build = Builder::new(&mut source, out);
157 let zero = build.iconst(int, 0);
158 build.ret(&[zero]);
159
160 let func = lowered(&mut source, &mut names);
161
162 let made: Vec<mir::Block> = func.blocks().collect();
163 let weight = |at: usize| func[made[at]].weight.raw();
164 assert!(weight(2) > weight(3), "the body {} the exit {}", weight(2), weight(3));
165 assert!(weight(3).abs_diff(mir::Weight::ONCE.raw()) <= 1, "the exit {}", weight(3));
168 }
169
170 #[test]
171 fn the_arm_control_does_not_come_back_from_is_the_colder_one() {
172 let int = Type::int(32);
173 let mut names = Interner::new();
174 let mut source =
175 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
176 let entry = source.create_block();
177 let cond = source.append_param(entry, Type::int(1));
178 let yes = source.create_block();
179 let no = source.create_block();
180 Builder::new(&mut source, entry).br_if(cond, yes, &[], no, &[]);
181 Builder::new(&mut source, yes).inst(ir::InstData::new(Opcode::Unreachable), &[]);
185 let mut build = Builder::new(&mut source, no);
186 let zero = build.iconst(int, 0);
187 build.ret(&[zero]);
188
189 let func = lowered(&mut source, &mut names);
190
191 let head = func.blocks().next().expect("an entry");
192 let arms: Vec<u64> = func[head].succs.iter().map(|call| call.weight.raw()).collect();
193 assert!(arms[0] < arms[1], "{arms:?}");
194 }
195}