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(
93 source,
94 names,
95 &crate::select::x86_64::SELECTOR,
96 &SYSV,
97 &Elsewhere::default(),
98 )
99 .expect("it lowers");
100 let lower::Lowered { mut func, blocks, .. } = out;
101 carry(source, &blocks, &mut func);
102 func
103 }
104
105 fn weights(func: &mir::Func) -> Vec<u64> {
108 func.blocks().map(|block| func[block].weight.raw()).collect()
109 }
110
111 #[test]
112 fn a_function_with_no_branch_in_it_runs_every_block_once() {
113 let mut names = Interner::new();
114 let mut source = Func::new(names.intern("f"), Signature::new());
115 let entry = source.create_block();
116 Builder::new(&mut source, entry).ret(&[]);
117
118 let func = lowered(&mut source, &mut names);
119
120 assert_eq!(weights(&func), [mir::Weight::ONCE.raw()]);
121 }
122
123 #[test]
124 fn the_arms_of_a_branch_add_up_to_the_block_they_leave() {
125 let int = Type::int(32);
126 let mut names = Interner::new();
127 let mut source =
128 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
129 let entry = source.create_block();
130 let cond = source.append_param(entry, Type::int(1));
131 let yes = source.create_block();
132 let no = source.create_block();
133 Builder::new(&mut source, entry).br_if(cond, yes, &[], no, &[]);
134 let mut build = Builder::new(&mut source, yes);
135 let one = build.iconst(int, 1);
136 build.ret(&[one]);
137 let mut build = Builder::new(&mut source, no);
138 let two = build.iconst(int, 2);
139 build.ret(&[two]);
140
141 let func = lowered(&mut source, &mut names);
142
143 let head = func.blocks().next().expect("an entry");
144 let arms: Vec<u64> = func[head].succs.iter().map(|call| call.weight.raw()).collect();
145 assert_eq!(arms.len(), 2);
146 assert_eq!(arms.iter().sum::<u64>(), mir::Weight::ONCE.raw());
147 }
148
149 #[test]
150 fn a_loop_body_runs_more_often_than_the_block_that_follows_it() {
151 let int = Type::int(32);
152 let mut names = Interner::new();
153 let mut source =
154 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
155 let entry = source.create_block();
156 let cond = source.append_param(entry, Type::int(1));
157 let head = source.create_block();
158 let body = source.create_block();
159 let out = source.create_block();
160 Builder::new(&mut source, entry).jump(head, &[]);
161 Builder::new(&mut source, head).br_if(cond, body, &[], out, &[]);
162 Builder::new(&mut source, body).jump(head, &[]);
163 let mut build = Builder::new(&mut source, out);
164 let zero = build.iconst(int, 0);
165 build.ret(&[zero]);
166
167 let func = lowered(&mut source, &mut names);
168
169 let made: Vec<mir::Block> = func.blocks().collect();
170 let weight = |at: usize| func[made[at]].weight.raw();
171 assert!(weight(2) > weight(3), "the body {} the exit {}", weight(2), weight(3));
172 assert!(weight(3).abs_diff(mir::Weight::ONCE.raw()) <= 1, "the exit {}", weight(3));
175 }
176
177 #[test]
178 fn the_arm_control_does_not_come_back_from_is_the_colder_one() {
179 let int = Type::int(32);
180 let mut names = Interner::new();
181 let mut source =
182 Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
183 let entry = source.create_block();
184 let cond = source.append_param(entry, Type::int(1));
185 let yes = source.create_block();
186 let no = source.create_block();
187 Builder::new(&mut source, entry).br_if(cond, yes, &[], no, &[]);
188 Builder::new(&mut source, yes).inst(ir::InstData::new(Opcode::Unreachable), &[]);
192 let mut build = Builder::new(&mut source, no);
193 let zero = build.iconst(int, 0);
194 build.ret(&[zero]);
195
196 let func = lowered(&mut source, &mut names);
197
198 let head = func.blocks().next().expect("an entry");
199 let arms: Vec<u64> = func[head].succs.iter().map(|call| call.weight.raw()).collect();
200 assert!(arms[0] < arms[1], "{arms:?}");
201 }
202}