1use std::collections::{HashMap, HashSet};
58
59use rucc_base::Interner;
60use rucc_mir as mir;
61use rucc_target::{FrameInsts, Role};
62
63pub fn addresses(
72 func: &mut mir::Func,
73 insts: &FrameInsts,
74 names: &mut Interner,
75 waiting: &HashSet<mir::Inst>,
76) -> usize {
77 let lea = mir::Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.lea)));
78 let reads = reads(func);
79 let mut folded = 0;
80 for block in func.blocks().collect::<Vec<_>>() {
81 let mut open: HashMap<mir::Reg, mir::Inst> = HashMap::new();
84 for inst in func.insts(block).collect::<Vec<_>>() {
85 if let Some(folding) = candidate(func, &open, inst) {
86 let operands = func.push_operands(&folding.operands);
87 let mem = func.add_amode(folding.amode);
88 func[inst].operands = operands;
89 func[inst].mem = Some(mem);
90 open.remove(&folding.base);
91 func.remove_inst(folding.from);
92 folded += 1;
93 }
94 for written in written(func, inst) {
95 open.retain(|reg, &mut held| *reg != written && !touches(func, held, written));
96 }
97 if func[inst].opcode == lea && !waiting.contains(&inst) {
98 if let Some(reg) = written_once(func, &reads, inst) {
99 open.insert(reg, inst);
100 }
101 }
102 }
103 }
104 folded
105}
106
107fn reads(func: &mir::Func) -> HashMap<mir::Reg, usize> {
114 let mut counts = HashMap::new();
115 for block in func.blocks() {
116 for inst in func.insts(block) {
117 for operand in &func[func[inst].operands] {
118 if operand.role == Role::Use {
119 *counts.entry(operand.reg).or_insert(0) += 1;
120 }
121 }
122 }
123 for call in &func[block].succs {
124 for &arg in &call.args {
125 *counts.entry(arg).or_insert(0) += 1;
126 }
127 }
128 }
129 counts
130}
131
132fn written_once(
135 func: &mir::Func,
136 reads: &HashMap<mir::Reg, usize>,
137 inst: mir::Inst,
138) -> Option<mir::Reg> {
139 let operands = &func[func[inst].operands];
140 let mut defs = operands.iter().filter(|operand| operand.role != Role::Use);
141 let def = defs.next()?;
142 if defs.next().is_some() || !def.reg.is_virtual() || reads.get(&def.reg) != Some(&1) {
143 return None;
144 }
145 Some(def.reg)
146}
147
148fn written(func: &mir::Func, inst: mir::Inst) -> Vec<mir::Reg> {
150 func[func[inst].operands]
151 .iter()
152 .filter(|operand| operand.role != Role::Use)
153 .map(|operand| operand.reg)
154 .collect()
155}
156
157fn touches(func: &mir::Func, inst: mir::Inst, reg: mir::Reg) -> bool {
160 let Some(mem) = func[inst].mem else { return false };
161 let amode = func[mem];
162 let operands = &func[func[inst].operands];
163 [amode.base, amode.index]
164 .into_iter()
165 .flatten()
166 .filter_map(|at| operands.get(usize::from(at)))
167 .any(|operand| operand.reg == reg)
168}
169
170fn base_reg(func: &mir::Func, inst: mir::Inst) -> Option<mir::Reg> {
176 let amode = func[func[inst].mem?];
177 if amode.index.is_some() || amode.symbol.is_some() || amode.got {
178 return None;
179 }
180 Some(func[func[inst].operands].get(usize::from(amode.base?))?.reg)
181}
182
183struct Folding {
189 from: mir::Inst,
191 base: mir::Reg,
193 operands: Vec<mir::Operand>,
195 amode: mir::Amode,
197}
198
199fn candidate(
208 func: &mir::Func,
209 open: &HashMap<mir::Reg, mir::Inst>,
210 inst: mir::Inst,
211) -> Option<Folding> {
212 let base = base_reg(func, inst)?;
213 let from = *open.get(&base)?;
214 let address = func[func[from].mem?];
215 let disp = i64::from(address.disp) + i64::from(func[func[inst].mem?].disp);
220 let mut amode = mir::Amode { disp: i32::try_from(disp).ok()?, ..address };
221
222 let taken = &func[func[from].operands];
223 let reader = &func[func[inst].operands];
224 let mut operands = reader.get(..reader.len().checked_sub(1)?)?.to_vec();
225 for (at, into) in [(address.base, &mut amode.base), (address.index, &mut amode.index)] {
226 let Some(at) = at else { continue };
227 operands.push(*taken.get(usize::from(at))?);
228 *into = Some(u8::try_from(operands.len() - 1).ok()?);
229 }
230 Some(Folding { from, base, operands, amode })
231}
232
233#[cfg(test)]
234mod tests {
235 use rucc_target::x86_64::{FRAME, GPR, RDI};
236
237 use super::*;
238
239 fn empty() -> (Interner, mir::Func, mir::Block) {
241 let mut names = Interner::new();
242 let mut func = mir::Func::new(names.intern("f"));
243 let block = func.create_block();
244 (names, func, block)
245 }
246
247 fn op(names: &mut Interner, name: &str) -> mir::Opcode {
249 mir::Opcode::new(names.intern(&format!("{}{name}", FRAME.prefix)))
250 }
251
252 fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<(String, mir::Amode)> {
254 func.insts(block)
255 .map(|inst| {
256 let amode = func[inst].mem.map_or(mir::Amode::NOTHING, |mem| func[mem]);
257 (names.resolve(func[inst].opcode.name()).to_owned(), amode)
258 })
259 .collect()
260 }
261
262 fn address_regs(func: &mir::Func, inst: mir::Inst) -> Vec<mir::Reg> {
264 let amode = func[func[inst].mem.expect("a memory operand")];
265 let operands = &func[func[inst].operands];
266 [amode.base, amode.index]
267 .into_iter()
268 .flatten()
269 .map(|at| operands[usize::from(at)].reg)
270 .collect()
271 }
272
273 #[test]
276 fn an_address_a_load_reads_once_becomes_the_load_s_own_addressing_mode() {
277 let (mut names, mut func, block) = empty();
278 let array = func.new_vreg(GPR);
279 let index = func.new_vreg(GPR);
280 let address = func.new_vreg(GPR);
281 let value = func.new_vreg(GPR);
282 let lea = op(&mut names, FRAME.lea);
283 let load = op(&mut names, "mov_rm_32");
284 func.build(block, lea)
285 .def(address, GPR)
286 .mem(
287 mir::Mem::at(mir::Operand::read(array, GPR))
288 .indexed(mir::Operand::read(index, GPR), 4),
289 )
290 .finish();
291 func.build(block, load)
292 .def(value, GPR)
293 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
294 .finish();
295
296 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 1);
297
298 let left = shape(&func, &names, block);
299 assert_eq!(left.len(), 1, "the address is worked out twice: {left:?}");
300 assert_eq!(left[0].0, format!("{}mov_rm_32", FRAME.prefix));
301 assert_eq!(left[0].1.scale, 4);
302 assert_eq!(left[0].1.disp, 0);
303 let inst = func.insts(block).next().expect("the load is still there");
304 assert_eq!(address_regs(&func, inst), vec![array, index], "the load reads the wrong pair");
305 }
306
307 #[test]
310 fn the_displacements_of_the_two_addresses_are_added() {
311 let (mut names, mut func, block) = empty();
312 let array = func.new_vreg(GPR);
313 let address = func.new_vreg(GPR);
314 let value = func.new_vreg(GPR);
315 let lea = op(&mut names, FRAME.lea);
316 let load = op(&mut names, "mov_rm_32");
317 func.build(block, lea)
318 .def(address, GPR)
319 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
320 .finish();
321 func.build(block, load)
322 .def(value, GPR)
323 .mem(mir::Mem::at(mir::Operand::read(address, GPR)).plus(8))
324 .finish();
325
326 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 1);
327
328 let left = shape(&func, &names, block);
329 assert_eq!(left.len(), 1);
330 assert_eq!(left[0].1.disp, 24, "the field is at the sum of the two offsets or nowhere");
331 }
332
333 #[test]
336 fn a_store_keeps_the_value_it_is_storing() {
337 let (mut names, mut func, block) = empty();
338 let array = func.new_vreg(GPR);
339 let index = func.new_vreg(GPR);
340 let address = func.new_vreg(GPR);
341 let value = func.new_vreg(GPR);
342 let lea = op(&mut names, FRAME.lea);
343 let store = op(&mut names, "mov_mr_32");
344 func.build(block, lea)
345 .def(address, GPR)
346 .mem(
347 mir::Mem::at(mir::Operand::read(array, GPR))
348 .indexed(mir::Operand::read(index, GPR), 8),
349 )
350 .finish();
351 func.build(block, store)
352 .uses(value, GPR)
353 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
354 .finish();
355
356 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 1);
357
358 let inst = func.insts(block).next().expect("the store is still there");
359 let regs: Vec<mir::Reg> = func[func[inst].operands].iter().map(|op| op.reg).collect();
360 assert_eq!(regs, vec![value, array, index], "the value the store writes went missing");
361 assert_eq!(func[func[inst].mem.expect("a memory operand")].scale, 8);
362 }
363
364 #[test]
367 fn an_address_two_instructions_read_is_left_where_it_is() {
368 let (mut names, mut func, block) = empty();
369 let array = func.new_vreg(GPR);
370 let address = func.new_vreg(GPR);
371 let lea = op(&mut names, FRAME.lea);
372 let load = op(&mut names, "mov_rm_32");
373 func.build(block, lea)
374 .def(address, GPR)
375 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
376 .finish();
377 for _ in 0..2 {
378 let value = func.new_vreg(GPR);
379 func.build(block, load)
380 .def(value, GPR)
381 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
382 .finish();
383 }
384
385 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
386 assert_eq!(shape(&func, &names, block).len(), 3);
387 }
388
389 #[test]
392 fn a_reader_that_already_has_an_index_is_left_alone() {
393 let (mut names, mut func, block) = empty();
394 let array = func.new_vreg(GPR);
395 let index = func.new_vreg(GPR);
396 let address = func.new_vreg(GPR);
397 let value = func.new_vreg(GPR);
398 let lea = op(&mut names, FRAME.lea);
399 let load = op(&mut names, "mov_rm_32");
400 func.build(block, lea)
401 .def(address, GPR)
402 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
403 .finish();
404 func.build(block, load)
405 .def(value, GPR)
406 .mem(
407 mir::Mem::at(mir::Operand::read(address, GPR))
408 .indexed(mir::Operand::read(index, GPR), 4),
409 )
410 .finish();
411
412 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
413 assert_eq!(shape(&func, &names, block).len(), 2);
414 }
415
416 #[test]
420 fn two_displacements_that_do_not_fit_together_are_not_put_together() {
421 let (mut names, mut func, block) = empty();
422 let array = func.new_vreg(GPR);
423 let address = func.new_vreg(GPR);
424 let value = func.new_vreg(GPR);
425 let lea = op(&mut names, FRAME.lea);
426 let load = op(&mut names, "mov_rm_32");
427 func.build(block, lea)
428 .def(address, GPR)
429 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(i32::MAX))
430 .finish();
431 func.build(block, load)
432 .def(value, GPR)
433 .mem(mir::Mem::at(mir::Operand::read(address, GPR)).plus(1))
434 .finish();
435
436 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
437 assert_eq!(shape(&func, &names, block).len(), 2);
438 }
439
440 #[test]
443 fn a_register_the_address_reads_being_written_in_between_ends_the_chance() {
444 let (mut names, mut func, block) = empty();
445 let array = mir::Reg::physical(RDI);
446 let address = func.new_vreg(GPR);
447 let value = func.new_vreg(GPR);
448 let lea = op(&mut names, FRAME.lea);
449 let load = op(&mut names, "mov_rm_32");
450 let put = op(&mut names, "mov_ri_64");
451 func.build(block, lea)
452 .def(address, GPR)
453 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
454 .finish();
455 func.build(block, put).def(array, GPR).imm(7).finish();
456 func.build(block, load)
457 .def(value, GPR)
458 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
459 .finish();
460
461 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
462 assert_eq!(shape(&func, &names, block).len(), 3);
463 }
464
465 #[test]
468 fn a_reader_in_another_block_is_not_one_this_folds_into() {
469 let (mut names, mut func, block) = empty();
470 let next = func.create_block();
471 let array = func.new_vreg(GPR);
472 let address = func.new_vreg(GPR);
473 let value = func.new_vreg(GPR);
474 let lea = op(&mut names, FRAME.lea);
475 let load = op(&mut names, "mov_rm_32");
476 func.build(block, lea)
477 .def(address, GPR)
478 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
479 .finish();
480 *func.succs_mut(block) = vec![mir::BlockCall::to(next)];
481 func.build(next, load)
482 .def(value, GPR)
483 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
484 .finish();
485
486 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
487 }
488
489 #[test]
493 fn a_chain_of_two_addresses_is_folded_the_whole_way_in_one_pass() {
494 let (mut names, mut func, block) = empty();
495 let array = func.new_vreg(GPR);
496 let index = func.new_vreg(GPR);
497 let element = func.new_vreg(GPR);
498 let field = func.new_vreg(GPR);
499 let value = func.new_vreg(GPR);
500 let lea = op(&mut names, FRAME.lea);
501 let load = op(&mut names, "mov_rm_32");
502 func.build(block, lea)
503 .def(element, GPR)
504 .mem(
505 mir::Mem::at(mir::Operand::read(array, GPR))
506 .indexed(mir::Operand::read(index, GPR), 8),
507 )
508 .finish();
509 func.build(block, lea)
510 .def(field, GPR)
511 .mem(mir::Mem::at(mir::Operand::read(element, GPR)).plus(4))
512 .finish();
513 func.build(block, load)
514 .def(value, GPR)
515 .mem(mir::Mem::at(mir::Operand::read(field, GPR)))
516 .finish();
517
518 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 2);
519
520 let left = shape(&func, &names, block);
521 assert_eq!(left.len(), 1, "one of the two addresses is still its own instruction");
522 assert_eq!(left[0].1.scale, 8);
523 assert_eq!(left[0].1.disp, 4);
524 let inst = func.insts(block).next().expect("the load is still there");
525 assert_eq!(address_regs(&func, inst), vec![array, index]);
526 }
527
528 #[test]
532 fn an_address_of_a_global_folds_into_the_reader_symbol_and_all() {
533 let (mut names, mut func, block) = empty();
534 let global = names.intern("counters");
535 let address = func.new_vreg(GPR);
536 let value = func.new_vreg(GPR);
537 let lea = op(&mut names, FRAME.lea);
538 let load = op(&mut names, "mov_rm_32");
539 func.build(block, lea).def(address, GPR).mem(mir::Mem::of(global)).finish();
540 func.build(block, load)
541 .def(value, GPR)
542 .mem(mir::Mem::at(mir::Operand::read(address, GPR)).plus(12))
543 .finish();
544
545 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 1);
546
547 let left = shape(&func, &names, block);
548 assert_eq!(left.len(), 1);
549 assert_eq!(left[0].1.symbol, Some(global));
550 assert_eq!(left[0].1.disp, 12);
551 }
552
553 #[test]
557 fn an_address_whose_displacement_is_still_to_be_written_is_left_where_it_is() {
558 let (mut names, mut func, block) = empty();
559 let sp = mir::Reg::physical(RDI);
560 let address = func.new_vreg(GPR);
561 let value = func.new_vreg(GPR);
562 let lea = op(&mut names, FRAME.lea);
563 let load = op(&mut names, "mov_rm_32");
564 let local = func
565 .build(block, lea)
566 .def(address, GPR)
567 .mem(mir::Mem::at(mir::Operand::read(sp, GPR)))
568 .finish();
569 func.build(block, load)
570 .def(value, GPR)
571 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
572 .finish();
573
574 let waiting = HashSet::from([local]);
575 assert_eq!(addresses(&mut func, &FRAME, &mut names, &waiting), 0);
576 assert_eq!(shape(&func, &names, block).len(), 2);
577
578 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 1);
581 }
582
583 #[test]
587 fn only_the_target_s_address_instruction_is_one_this_folds() {
588 let (mut names, mut func, block) = empty();
589 let array = func.new_vreg(GPR);
590 let address = func.new_vreg(GPR);
591 let value = func.new_vreg(GPR);
592 let load = op(&mut names, "mov_rm_64");
593 let read = op(&mut names, "mov_rm_32");
594 func.build(block, load)
595 .def(address, GPR)
596 .mem(mir::Mem::at(mir::Operand::read(array, GPR)).plus(16))
597 .finish();
598 func.build(block, read)
599 .def(value, GPR)
600 .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
601 .finish();
602
603 assert_eq!(addresses(&mut func, &FRAME, &mut names, &HashSet::new()), 0);
604 assert_eq!(shape(&func, &names, block).len(), 2);
605 }
606}