1use std::fmt;
39
40use rucc_base::Interner;
41use rucc_ir::{Block, Def, Func, Inst, Opcode, Value};
42use rucc_mir as mir;
43use rucc_target::RegClass;
44use rucc_target::x86_64;
45
46use crate::select::{Match, Piece, Rule, Table};
47use crate::term::{MAX_ARGS, PLAIN, Plan, Shown, Term, Terms};
48
49const PREFIX: &str = "x64.";
52
53#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct Unsupported {
59 pub inst: Inst,
61 pub term: Option<&'static str>,
64}
65
66impl fmt::Display for Unsupported {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self.term {
69 Some(term) => write!(f, "no rule lowers `{term}`"),
70 None => f.write_str("no rule lowers this instruction"),
71 }
72 }
73}
74
75impl std::error::Error for Unsupported {}
76
77pub fn func(source: &Func, names: &mut Interner) -> Result<mir::Func, Unsupported> {
84 Lowering::new(source, names).run()
85}
86
87struct Lowering<'a> {
89 source: &'a Func,
90 names: &'a mut Interner,
91 out: mir::Func,
92 regs: Vec<Option<mir::Reg>>,
94 uses: Vec<u32>,
97 at: Option<mir::Block>,
99 gpr: RegClass,
101}
102
103impl<'a> Lowering<'a> {
104 fn new(source: &'a Func, names: &'a mut Interner) -> Self {
105 let counts = source.counts();
106 let name = source.name;
107 let mut uses = vec![0; counts.values];
108 for block in source.blocks() {
109 for inst in source.insts(block) {
110 for &arg in &source[source[inst].args] {
111 uses[arg.index()] += 1;
112 }
113 for call in source.successors(inst) {
114 for &arg in &source[call.args] {
115 uses[arg.index()] += 1;
116 }
117 }
118 }
119 }
120 Self {
121 source,
122 names,
123 out: mir::Func::new(name),
124 regs: vec![None; counts.values],
125 uses,
126 at: None,
127 gpr: x86_64::GPR,
128 }
129 }
130
131 fn run(mut self) -> Result<mir::Func, Unsupported> {
132 for block in self.source.blocks() {
133 self.block(block)?;
134 }
135 Ok(self.out)
136 }
137
138 fn block(&mut self, block: Block) -> Result<(), Unsupported> {
140 let out = self.out.create_block();
141 self.at = Some(out);
142 for ¶m in self.source[block].params.iter() {
143 let reg = self.out.append_param(out, self.gpr);
144 self.regs[param.index()] = Some(reg);
145 }
146
147 let insts: Vec<Inst> = self.source.insts(block).collect();
153 let mut found: Vec<Option<Match<Term>>> = (0..insts.len()).map(|_| None).collect();
154 let mut folded: Vec<Inst> = Vec::new();
155 for (index, &inst) in insts.iter().enumerate().rev() {
156 if folded.contains(&inst) {
157 continue;
158 }
159 if let Some((plan, matched)) = self.select(inst) {
160 folded.extend(self.folds(inst, plan));
161 found[index] = Some(matched);
162 }
163 }
164
165 for (&inst, matched) in insts.iter().zip(found) {
166 if folded.contains(&inst) || self.source[inst].opcode == Opcode::IConst {
167 continue;
168 }
169 let matched = matched.ok_or_else(|| self.unsupported(inst))?;
170 self.emit(inst, &matched)?;
171 }
172 Ok(())
173 }
174
175 fn select(&self, inst: Inst) -> Option<(Plan, Match<Term>)> {
181 for plan in self.plans(inst) {
182 let terms = Terms::new(self.source, inst, plan);
183 if let Some(matched) = TABLE.find(&terms, Term::Root) {
184 return Some((plan, matched));
185 }
186 }
187 None
188 }
189
190 fn plans(&self, inst: Inst) -> Vec<Plan> {
192 let args = &self.source[self.source[inst].args];
193 let mut plans = vec![PLAIN];
194 for (index, &arg) in args.iter().enumerate().take(MAX_ARGS) {
195 let mut ways = Vec::new();
196 if self.foldable(inst, arg) {
197 ways.push(Shown::Expand);
198 }
199 if Terms::new(self.source, inst, PLAIN).constant(arg).is_some() {
200 ways.push(Shown::Const);
201 }
202 ways.push(Shown::Reg);
203 plans = plans
204 .into_iter()
205 .flat_map(|plan| {
206 ways.iter().map(move |&way| {
207 let mut next = plan;
208 next[index] = way;
209 next
210 })
211 })
212 .collect();
213 }
214 plans
215 }
216
217 fn foldable(&self, into: Inst, value: Value) -> bool {
225 let Def::Result { inst, .. } = self.source[value].def else { return false };
226 if self.source[inst].opcode == Opcode::IConst || self.uses[value.index()] != 1 {
227 return false;
228 }
229 self.source.block_of(inst).is_some()
230 && self.source.block_of(inst) == self.source.block_of(into)
231 }
232
233 fn folds(&self, inst: Inst, plan: Plan) -> Vec<Inst> {
240 let args = &self.source[self.source[inst].args];
241 args.iter()
242 .take(MAX_ARGS)
243 .enumerate()
244 .filter(|&(index, _)| plan[index] == Shown::Expand)
245 .filter_map(|(_, &arg)| match self.source[arg].def {
246 Def::Result { inst, .. } => Some(inst),
247 Def::Param { .. } => None,
248 })
249 .collect()
250 }
251
252 fn emit(&mut self, inst: Inst, matched: &Match<Term>) -> Result<(), Unsupported> {
254 let rule: &Rule = TABLE.rule(matched);
255 let pieces = rule.replacement;
256 let Some(Piece::App { head, arity }) = pieces.first() else {
257 return Err(self.unsupported(inst));
258 };
259 let opcode = head.strip_prefix(PREFIX).ok_or_else(|| self.unsupported(inst))?;
260 let form = x86_64::form(opcode).ok_or_else(|| self.unsupported(inst))?;
261
262 let mut read = Read::default();
263 let mut at = 1;
264 for _ in 0..*arity {
265 at = self.read(inst, pieces, at, &matched.bindings, &mut read)?;
266 }
267
268 let descs = form.operands();
269 let writes = descs.iter().take_while(|desc| desc.role.is_def()).count();
270 if descs.len() - writes != read.regs.len() {
271 return Err(self.unsupported(inst));
272 }
273
274 let result = self.source[inst].first_result.ok_or_else(|| self.unsupported(inst))?;
278 let dest = self.new_reg(result);
279 let mut regs = vec![dest];
280 regs.extend((1..writes).map(|_| self.out.new_vreg(self.gpr)));
281 regs.extend(read.regs.iter().copied());
282
283 let block = self.at.expect("a block is being filled");
284 let opcode = mir::Opcode::new(self.names.intern(head));
285 let mut build = self.out.build(block, opcode).at(self.source.span(inst));
286 for (desc, reg) in descs.iter().zip(regs) {
287 let operand = mir::Operand {
288 reg,
289 class: desc.class,
290 role: desc.role,
291 constraint: desc.constraint,
292 };
293 build = build.operand(operand);
294 }
295 if let Some(mem) = read.mem {
296 build = build.mem(mem);
297 }
298 if let Some(imm) = read.imm {
299 build = build.imm(imm);
300 }
301 build.finish();
302 Ok(())
303 }
304
305 fn read(
310 &mut self,
311 inst: Inst,
312 pieces: &'static [Piece],
313 at: usize,
314 bindings: &[Term],
315 out: &mut Read,
316 ) -> Result<usize, Unsupported> {
317 match pieces.get(at) {
318 Some(Piece::Int(value)) => {
319 out.imm = i64::try_from(*value).ok();
320 Ok(at + 1)
321 }
322 Some(Piece::Var { index, .. }) => {
323 match bindings.get(*index) {
324 Some(&Term::Reg(value)) => {
325 let reg = self.reg_of(value)?;
326 out.regs.push(reg);
327 }
328 Some(&Term::Num(value)) => out.imm = i64::try_from(value).ok(),
329 _ => return Err(self.unsupported(inst)),
332 }
333 Ok(at + 1)
334 }
335 Some(Piece::App { head, arity }) => {
336 let kind = x86_64::address(head).ok_or_else(|| self.unsupported(inst))?;
337 let mut inner = Read::default();
338 let mut next = at + 1;
339 for _ in 0..*arity {
340 next = self.read(inst, pieces, next, bindings, &mut inner)?;
341 }
342 let mem = address(kind, &inner, self.gpr).ok_or_else(|| self.unsupported(inst))?;
343 out.mem = Some(mem);
344 Ok(next)
345 }
346 None => Err(self.unsupported(inst)),
347 }
348 }
349
350 fn reg_of(&mut self, value: Value) -> Result<mir::Reg, Unsupported> {
353 if let Some(reg) = self.regs[value.index()] {
354 return Ok(reg);
355 }
356 let constant = match self.source[value].def {
357 Def::Result { inst, .. } => {
358 (self.source[inst].opcode == Opcode::IConst).then_some(inst)
359 }
360 Def::Param { .. } => None,
361 };
362 if let Some(inst) = constant {
363 let matched = self
364 .select(inst)
365 .map(|(_, matched)| matched)
366 .ok_or_else(|| self.unsupported(inst))?;
367 self.emit(inst, &matched)?;
368 return Ok(self.regs[value.index()].expect("a constant is written into a register"));
369 }
370 Ok(self.new_reg(value))
371 }
372
373 fn new_reg(&mut self, value: Value) -> mir::Reg {
375 if let Some(reg) = self.regs[value.index()] {
376 return reg;
377 }
378 let reg = self.out.new_vreg(self.gpr);
379 self.regs[value.index()] = Some(reg);
380 reg
381 }
382
383 fn unsupported(&self, inst: Inst) -> Unsupported {
384 Unsupported { inst, term: Terms::new(self.source, inst, PLAIN).name(inst) }
385 }
386}
387
388#[derive(Debug, Default)]
390struct Read {
391 regs: Vec<mir::Reg>,
392 imm: Option<i64>,
393 mem: Option<mir::Mem>,
394}
395
396fn address(kind: x86_64::Address, read: &Read, gpr: RegClass) -> Option<mir::Mem> {
398 let scale = u8::try_from(read.imm?).ok()?;
399 let mut regs = read.regs.iter().copied();
400 let first = mir::Operand::read(regs.next()?, gpr);
401 if kind.has_base() {
402 let index = mir::Operand::read(regs.next()?, gpr);
403 return Some(mir::Mem::at(first).indexed(index, scale));
404 }
405 Some(mir::Mem { base: None, index: Some(first), scale, disp: 0, symbol: None })
406}
407
408static TABLE: &Table = &crate::select::x86_64::TABLE;
414
415#[cfg(test)]
416mod tests {
417 use rucc_ir::{Builder, Flags, Signature, Type};
418 use rucc_target::x86_64::REGS;
419
420 use super::*;
421
422 fn blank(params: &[Type]) -> (Interner, Func, Block, Vec<Value>) {
424 let mut names = Interner::new();
425 let mut func = Func::new(names.intern("f"), Signature::new());
426 let block = func.create_block();
427 let values = params.iter().map(|&ty| func.append_param(block, ty)).collect();
428 (names, func, block, values)
429 }
430
431 fn lower(names: &mut Interner, source: &Func) -> String {
433 let out = func(source, names).expect("every instruction has a rule");
434 mir::print_func(&out, names, ®S)
435 }
436
437 #[test]
438 fn an_addition_of_two_registers_is_one_instruction() {
439 let i32 = Type::int(32);
440 let (mut names, mut func, block, args) = blank(&[i32, i32]);
441 let mut build = Builder::new(&mut func, block);
442 build.binary(Opcode::Add, args[0], args[1], Flags::default());
443
444 assert_eq!(
445 lower(&mut names, &func),
446 "mfunc @f {\nblock0(%0:gpr, %1:gpr):\n \
447 %2:gpr(reuse 1) = x64.add_rr_32 %0, %1\n}\n"
448 );
449 }
450
451 #[test]
452 fn a_constant_operand_becomes_an_immediate() {
453 let i32 = Type::int(32);
454 let (mut names, mut func, block, args) = blank(&[i32]);
455 let mut build = Builder::new(&mut func, block);
456 let seven = build.iconst(i32, 7);
457 build.binary(Opcode::Add, args[0], seven, Flags::default());
458
459 assert_eq!(
462 lower(&mut names, &func),
463 "mfunc @f {\nblock0(%0:gpr):\n %1:gpr(reuse 1) = x64.add_ri_32 %0, 7\n}\n"
464 );
465 }
466
467 #[test]
468 fn a_constant_too_wide_for_an_immediate_goes_into_a_register() {
469 let i64 = Type::int(64);
470 let (mut names, mut func, block, args) = blank(&[i64]);
471 let mut build = Builder::new(&mut func, block);
472 let big = build.iconst(i64, i128::from(i32::MAX) + 1);
473 build.binary(Opcode::Add, args[0], big, Flags::default());
474
475 assert_eq!(
479 lower(&mut names, &func),
480 "mfunc @f {\nblock0(%0:gpr):\n %1:gpr = x64.mov_ri_64 2147483648\n \
481 %2:gpr(reuse 1) = x64.add_rr_64 %0, %1\n}\n"
482 );
483 }
484
485 #[test]
486 fn an_index_calculation_folds_into_an_address() {
487 let i64 = Type::int(64);
488 let (mut names, mut func, block, args) = blank(&[i64, i64]);
489 let mut build = Builder::new(&mut func, block);
490 let four = build.iconst(i64, 4);
491 let scaled = build.binary(Opcode::Mul, args[1], four, Flags::default());
492 build.binary(Opcode::Add, args[0], scaled, Flags::default());
493
494 assert_eq!(
497 lower(&mut names, &func),
498 "mfunc @f {\nblock0(%0:gpr, %1:gpr):\n %2:gpr = x64.lea_64 [%0 + %1*4]\n}\n"
499 );
500 }
501
502 #[test]
503 fn an_instruction_read_twice_is_not_folded_into_either_reader() {
504 let i64 = Type::int(64);
505 let (mut names, mut func, block, args) = blank(&[i64, i64]);
506 let mut build = Builder::new(&mut func, block);
507 let four = build.iconst(i64, 4);
508 let scaled = build.binary(Opcode::Mul, args[1], four, Flags::default());
509 let first = build.binary(Opcode::Add, args[0], scaled, Flags::default());
510 build.binary(Opcode::Add, first, scaled, Flags::default());
511
512 let text = lower(&mut names, &func);
515 assert!(text.contains("x64.lea_64 [%1*4]"), "{text}");
516 assert_eq!(text.matches("x64.add_rr_64").count(), 2, "{text}");
517 }
518
519 #[test]
520 fn a_shift_by_a_register_asks_for_it_in_cl() {
521 let i32 = Type::int(32);
522 let (mut names, mut func, block, args) = blank(&[i32, i32]);
523 let mut build = Builder::new(&mut func, block);
524 build.binary(Opcode::Shl, args[0], args[1], Flags::default());
525
526 let text = lower(&mut names, &func);
529 assert!(text.contains("x64.shl_rcl_32 %0, %1($rcx)"), "{text}");
530 }
531
532 #[test]
533 fn a_division_names_the_registers_and_the_register_it_destroys() {
534 let i32 = Type::int(32);
535 let (mut names, mut func, block, args) = blank(&[i32, i32]);
536 let mut build = Builder::new(&mut func, block);
537 build.binary(Opcode::SDiv, args[0], args[1], Flags::default());
538
539 let text = lower(&mut names, &func);
542 assert!(
543 text.contains("%2:gpr($rax), early %3:gpr($rdx) = x64.idiv_quo_32 %0($rax), %1"),
544 "{text}"
545 );
546 }
547
548 #[test]
549 fn an_instruction_no_rule_covers_is_reported() {
550 let i64 = Type::int(64);
551 let (mut names, mut source, block, args) = blank(&[i64]);
552 let mut build = Builder::new(&mut source, block);
553 build.ret(&[args[0]]);
554
555 let failed = func(&source, &mut names).expect_err("nothing lowers a return yet");
556 assert_eq!(failed.to_string(), "no rule lowers this instruction");
557 }
558}