1use std::fmt::Write as _;
29
30use rucc_base::{Interner, Symbol};
31use rucc_target::Slot;
32
33use crate::func::Func;
34use crate::inst::{
35 Abi, Block, BlockCall, Imm, Inst, InstData, MemInfo, Meta, Param, Signature, Value,
36};
37use crate::module::{Alias, Datum, Global, Module, Reloc};
38use crate::{Extra, FORMAT_VERSION, Linkage, MemOrder, Opcode, Type, Visibility};
39
40pub(crate) fn implied_result(opcode: Opcode) -> bool {
50 matches!(
51 opcode,
52 Opcode::ICmp
53 | Opcode::FCmp
54 | Opcode::GlobalAddr
55 | Opcode::BlockAddr
56 | Opcode::Alloca
57 | Opcode::Call
58 | Opcode::CallIndirect
59 | Opcode::TailCall
60 )
61}
62
63#[must_use]
65pub fn print(module: &Module, names: &Interner) -> String {
66 let mut printer = Printer::new(module, names);
67 printer.module();
68 printer.finish()
69}
70
71#[must_use]
73pub fn print_func(module: &Module, func: &Func, names: &Interner) -> String {
74 let mut printer = Printer::new(module, names);
75 printer.func(func);
76 printer.finish()
77}
78
79#[derive(Debug)]
81pub struct Printer<'a> {
82 module: &'a Module,
83 names: &'a Interner,
84 out: String,
85 values: Vec<u32>,
89 blocks: Vec<u32>,
90}
91
92impl<'a> Printer<'a> {
93 #[must_use]
95 pub fn new(module: &'a Module, names: &'a Interner) -> Printer<'a> {
96 Printer { module, names, out: String::new(), values: Vec::new(), blocks: Vec::new() }
97 }
98
99 #[must_use]
101 pub fn finish(self) -> String {
102 self.out
103 }
104
105 pub fn module(&mut self) {
107 let module = self.module;
108 let name = self.names.resolve(module.name);
109 let _ = writeln!(self.out, "; ModuleID = '{name}'");
112 let _ = writeln!(self.out, "; format {FORMAT_VERSION}");
113 let _ = writeln!(self.out, "target triple = \"{}\"", module.triple);
114 let _ = writeln!(self.out, "target datalayout = \"{}\"", module.datalayout);
115
116 if module.globals().next().is_some() {
117 self.out.push('\n');
118 for id in module.globals() {
119 self.global(&module[id]);
120 }
121 }
122 if module.aliases().next().is_some() {
123 self.out.push('\n');
124 for id in module.aliases() {
125 self.alias(&module[id]);
126 }
127 }
128 for id in module.funcs() {
129 self.out.push('\n');
130 self.func(&module[id]);
131 }
132 if module.metadata().next().is_some() {
133 self.out.push('\n');
134 for meta in module.metadata() {
135 self.meta_node(meta);
136 }
137 }
138 }
139
140 fn global(&mut self, global: &Global) {
144 let _ = write!(self.out, "global @{} : ", self.names.resolve(global.name));
145 match self.scalar_init(global) {
146 Some((ty, imm)) => {
150 let _ = write!(self.out, "{ty} = ");
151 self.imm(imm, ty);
152 }
153 None => {
154 let _ = write!(self.out, "bytes {}", global.size);
155 if let Some(init) = global.init {
156 let data = &self.module[init];
163 if data.is_empty() {
164 self.out.push_str(" = {}");
165 } else {
166 self.out.push_str(" = { ");
167 for (index, &datum) in data.iter().enumerate() {
168 if index > 0 {
169 self.out.push_str(", ");
170 }
171 self.datum(datum);
172 }
173 self.out.push_str(" }");
174 }
175 }
176 }
177 }
178 let _ = write!(self.out, ", align {}", global.align);
179 self.linkage(global.linkage, global.visibility);
180 if let Some(model) = global.tls {
181 let _ = write!(self.out, ", tls({})", model.name());
182 }
183 if global.constant {
184 self.out.push_str(", constant");
185 }
186 self.section(global.section);
187 self.out.push('\n');
188 }
189
190 fn scalar_init(&self, global: &Global) -> Option<(Type, Imm)> {
192 let init = global.init?;
193 let [datum] = self.module[init] else { return None };
194 let Datum::Scalar { ty, value } = datum else { return None };
195 (datum.size(self.module) == global.size).then(|| (ty, self.module[value]))
196 }
197
198 fn datum(&mut self, datum: Datum) {
200 match datum {
201 Datum::Zero(bytes) => {
202 let _ = write!(self.out, "zero {bytes}");
203 }
204 Datum::Bytes(range) => {
205 self.out.push_str("bytes ");
206 let bytes = &self.module[range];
207 self.string(bytes);
208 }
209 Datum::Scalar { ty, value } => {
210 let _ = write!(self.out, "{ty} ");
211 self.imm(self.module[value], ty);
212 }
213 Datum::Addr(reloc) => {
214 let Reloc { symbol, addend, size } = self.module[reloc];
215 let _ = write!(self.out, "addr.{size} @{}", self.names.resolve(symbol));
216 match addend.signum() {
217 1 => {
218 let _ = write!(self.out, " + {addend}");
219 }
220 -1 => {
221 let _ = match addend.checked_neg() {
225 Some(amount) => write!(self.out, " - {amount}"),
226 None => write!(self.out, " + {addend}"),
227 };
228 }
229 _ => {}
230 }
231 }
232 }
233 }
234
235 fn alias(&mut self, alias: &Alias) {
237 let _ = write!(
238 self.out,
239 "{} @{} = @{}",
240 alias.kind.name(),
241 self.names.resolve(alias.name),
242 self.names.resolve(alias.target)
243 );
244 self.linkage(alias.linkage, alias.visibility);
245 self.out.push('\n');
246 }
247
248 pub fn func(&mut self, func: &Func) {
252 self.number(func);
253 let _ = write!(self.out, "func @{}", self.names.resolve(func.name));
254 self.signature(func.signature());
255 self.linkage(func.linkage, func.visibility);
256 if !func.attrs.is_default() {
257 let _ = write!(self.out, ", {}", func.attrs);
258 }
259 self.section(func.section);
260 if func.is_declaration() {
261 self.out.push_str(";\n");
262 return;
263 }
264 self.out.push_str(" {\n");
265 for (index, block) in func.blocks().enumerate() {
266 if index > 0 {
267 self.out.push('\n');
268 }
269 self.block(func, block);
270 }
271 self.out.push_str("}\n");
272 }
273
274 fn number(&mut self, func: &Func) {
279 let counts = func.counts();
280 self.values.clear();
281 self.values.resize(counts.values, u32::MAX);
282 self.blocks.clear();
283 self.blocks.resize(counts.blocks, u32::MAX);
284 let mut next = 0;
285 for (index, block) in func.blocks().enumerate() {
286 self.blocks[block.index()] = index as u32;
287 for ¶m in &func[block].params {
288 self.values[param.index()] = next;
289 next += 1;
290 }
291 for inst in func.insts(block) {
292 for result in func[inst].results() {
293 self.values[result.index()] = next;
294 next += 1;
295 }
296 }
297 }
298 }
299
300 fn signature(&mut self, signature: &Signature) {
302 self.out.push('(');
303 for (index, param) in signature.params.iter().enumerate() {
304 if index > 0 {
305 self.out.push_str(", ");
306 }
307 self.param(param);
308 }
309 if signature.variadic {
310 if !signature.params.is_empty() {
311 self.out.push_str(", ");
312 }
313 self.out.push_str("...");
314 }
315 self.out.push(')');
316 match signature.returns.as_slice() {
317 [] => {}
318 [param] => {
319 self.out.push_str(" -> ");
320 self.param(param);
321 }
322 params => {
323 self.out.push_str(" -> (");
324 for (index, param) in params.iter().enumerate() {
325 if index > 0 {
326 self.out.push_str(", ");
327 }
328 self.param(param);
329 }
330 self.out.push(')');
331 }
332 }
333 }
334
335 fn param(&mut self, param: &Param) {
337 let _ = write!(self.out, "{}", param.ty);
338 self.abi(param.abi);
339 }
340
341 fn abi(&mut self, abi: Abi) {
344 let _ = match abi {
345 Abi::Plain => Ok(()),
346 Abi::Sext => write!(self.out, " sext"),
347 Abi::Zext => write!(self.out, " zext"),
348 Abi::ByVal { size, align } => write!(self.out, " byval({size}, align {align})"),
349 Abi::Sret { size, align } => write!(self.out, " sret({size}, align {align})"),
350 };
351 }
352
353 fn block(&mut self, func: &Func, block: Block) {
355 let _ = write!(self.out, "block{}", self.blocks[block.index()]);
356 let params = &func[block].params;
357 if !params.is_empty() {
358 self.out.push('(');
359 for (index, ¶m) in params.iter().enumerate() {
360 if index > 0 {
361 self.out.push_str(", ");
362 }
363 self.value(param);
364 let _ = write!(self.out, ": {}", func[param].ty);
365 }
366 self.out.push(')');
367 }
368 self.out.push_str(":\n");
369 for inst in func.insts(block) {
370 self.inst(func, inst);
371 }
372 }
373
374 fn inst(&mut self, func: &Func, inst: Inst) {
376 let data = func[inst];
377 self.out.push_str(" ");
378 for (index, result) in data.results().enumerate() {
379 if index > 0 {
380 self.out.push_str(", ");
381 }
382 self.value(result);
383 }
384 if data.results > 0 {
385 self.out.push_str(" = ");
386 }
387 self.out.push_str(data.opcode.name());
388 self.result_types(func, &data);
389 let _ = write!(self.out, "{}", data.flags);
390 self.operands(func, &data);
391 self.out.push('\n');
392 }
393
394 fn result_types(&mut self, func: &Func, data: &InstData) {
396 let results: Vec<Value> = data.results().collect();
397 match results.as_slice() {
398 [] => {}
399 _ if implied_result(data.opcode) => {}
400 [result] => {
401 let ty = func[*result].ty;
402 let takes_the_same = func[data.args].first().is_some_and(|&arg| func[arg].ty == ty);
403 if !takes_the_same {
404 let _ = write!(self.out, ".{ty}");
405 }
406 }
407 types => {
410 self.out.push_str(".(");
411 for (index, &result) in types.iter().enumerate() {
412 if index > 0 {
413 self.out.push_str(", ");
414 }
415 let _ = write!(self.out, "{}", func[result].ty);
416 }
417 self.out.push(')');
418 }
419 }
420 }
421
422 fn operands(&mut self, func: &Func, data: &InstData) {
424 let args = &func[data.args];
425 match data.extra {
426 Extra::None => self.value_list_spaced(args),
427 Extra::Imm(imm) => {
428 self.out.push(' ');
429 let ty = data.first_result.map_or(Type::VOID, |result| func[result].ty);
430 self.imm(func[imm], ty);
431 }
432 Extra::Symbol(symbol) => {
433 let _ = write!(self.out, " @{}", self.names.resolve(symbol));
434 if !args.is_empty() {
435 self.out.push('(');
436 self.value_list(args);
437 self.out.push(')');
438 }
439 }
440 Extra::IntPred(pred) => {
441 let _ = write!(self.out, " {}", pred.name());
442 self.value_list_spaced(args);
443 }
444 Extra::FloatPred(pred) => {
445 let _ = write!(self.out, " {}", pred.name());
446 self.value_list_spaced(args);
447 }
448 Extra::Mem(mem) => {
449 match (data.opcode, args) {
450 (Opcode::Store | Opcode::AtomicStore, [value, addr]) => {
453 self.out.push(' ');
454 self.value(*value);
455 self.out.push_str(" -> ");
456 self.value(*addr);
457 }
458 _ => self.value_list_spaced(args),
459 }
460 self.mem(func[mem]);
461 }
462 Extra::VaObject(info) => {
463 let info = func[info];
464 self.value_list_spaced(args);
465 self.mem(func[info.mem]);
466 let slots = &func[info.slots];
467 if !slots.is_empty() {
468 self.out.push_str(", in(");
469 for (index, &slot) in slots.iter().enumerate() {
470 if index > 0 {
471 self.out.push_str(", ");
472 }
473 self.slot(slot);
474 }
475 self.out.push(')');
476 }
477 }
478 Extra::Rmw(op, mem) => {
479 let _ = write!(self.out, " {}", op.name());
480 self.value_list_spaced(args);
481 self.mem(func[mem]);
482 }
483 Extra::Order(order) => {
484 let _ = write!(self.out, " {}", order.name());
485 }
486 Extra::Targets(targets) => {
487 if !args.is_empty() {
490 self.value_list_spaced(args);
491 self.out.push(',');
492 }
493 for (index, &call) in func[targets].iter().enumerate() {
494 self.out.push_str(if index > 0 { ", " } else { " " });
495 self.block_call(func, call);
496 }
497 }
498 Extra::Call(call) => {
499 let info = func[call];
500 let rest = match info.callee {
501 Some(callee) => {
502 let _ = write!(self.out, " @{}", self.names.resolve(callee));
503 args
504 }
505 None => {
508 self.out.push(' ');
509 match args.split_first() {
510 Some((&addr, rest)) => {
511 self.value(addr);
512 rest
513 }
514 None => {
515 self.out.push_str("%?");
516 &[]
517 }
518 }
519 }
520 };
521 self.out.push('(');
522 let named = func[info.signature].params.len();
525 let varargs = &func[info.varargs];
526 for (index, &arg) in rest.iter().enumerate() {
527 if index > 0 {
528 self.out.push_str(", ");
529 }
530 self.value(arg);
531 if let Some(&abi) = index.checked_sub(named).and_then(|at| varargs.get(at)) {
532 self.abi(abi);
533 }
534 }
535 self.out.push_str(") : ");
536 self.signature(&func[info.signature]);
537 }
538 Extra::Switch(switch) => {
539 let info = func[switch];
540 let ty = args.first().map_or(Type::VOID, |&arg| func[arg].ty);
541 self.value_list_spaced(args);
542 if let Some((&default, cases)) = func[info.targets].split_first() {
543 self.out.push_str(", ");
544 self.block_call(func, default);
545 self.out.push_str(", [");
546 for (index, (&case, &value)) in cases.iter().zip(&func[info.cases]).enumerate()
547 {
548 if index > 0 {
549 self.out.push_str(", ");
550 }
551 self.imm(value, ty);
552 self.out.push_str(" => ");
553 self.block_call(func, case);
554 }
555 self.out.push(']');
556 }
557 }
558 Extra::Asm(asm) => {
559 let info = func[asm];
560 self.out.push(' ');
561 self.string(self.names.resolve(info.template).as_bytes());
562 self.out.push_str(", ");
563 self.string(self.names.resolve(info.constraints).as_bytes());
564 self.out.push_str(", ");
565 self.string(self.names.resolve(info.clobbers).as_bytes());
566 self.out.push('(');
567 self.value_list(args);
568 self.out.push(')');
569 if !info.targets.is_empty() {
570 self.out.push_str(", labels [");
571 for (index, &call) in func[info.targets].iter().enumerate() {
572 if index > 0 {
573 self.out.push_str(", ");
574 }
575 self.block_call(func, call);
576 }
577 self.out.push(']');
578 }
579 }
580 }
581 }
582
583 fn value_list_spaced(&mut self, args: &[Value]) {
585 if args.is_empty() {
586 return;
587 }
588 self.out.push(' ');
589 self.value_list(args);
590 }
591
592 fn value_list(&mut self, args: &[Value]) {
594 for (index, &arg) in args.iter().enumerate() {
595 if index > 0 {
596 self.out.push_str(", ");
597 }
598 self.value(arg);
599 }
600 }
601
602 fn block_call(&mut self, func: &Func, call: BlockCall) {
604 let _ = write!(self.out, "block{}", self.blocks[call.block.index()]);
605 let args = &func[call.args];
606 if !args.is_empty() {
607 self.out.push('(');
608 self.value_list(args);
609 self.out.push(')');
610 }
611 }
612
613 fn mem(&mut self, info: MemInfo) {
615 if info.size != 0 {
616 let _ = write!(self.out, ", size {}", info.size);
617 }
618 let _ = write!(self.out, ", align {}", info.align);
619 if info.order != MemOrder::NotAtomic {
620 let _ = write!(self.out, ", {}", info.order.name());
621 }
622 if let Some(tbaa) = info.tbaa {
623 let _ = write!(self.out, ", tbaa !{}", tbaa.index());
624 }
625 }
626
627 fn slot(&mut self, slot: Slot) {
629 match slot {
630 Slot::Integer { offset, size } => {
631 let _ = write!(self.out, "int {size} at {offset}");
632 }
633 Slot::Float { offset, format } => {
634 let _ = write!(self.out, "float {} at {offset}", format.name());
635 }
636 }
637 }
638
639 fn value(&mut self, value: Value) {
641 match self.values.get(value.index()).copied() {
642 Some(number) if number != u32::MAX => {
643 let _ = write!(self.out, "%{number}");
644 }
645 _ => self.out.push_str("%?"),
649 }
650 }
651
652 fn imm(&mut self, imm: Imm, ty: Type) {
654 let scalar = if ty.is_vector() { ty.lane() } else { ty };
655 if scalar.is_float() {
656 let _ = write!(self.out, "{:#x}", imm.bits());
659 } else if scalar.is_int() {
660 let _ = write!(self.out, "{}", imm.signed(scalar));
661 } else {
662 let _ = write!(self.out, "{:#x}", imm.bits());
663 }
664 }
665
666 fn meta_node(&mut self, meta: Meta) {
668 let node = self.module[meta];
669 let _ = write!(self.out, "!{} = tbaa ", meta.index());
670 self.string(self.names.resolve(node.name).as_bytes());
671 if let Some(parent) = node.parent {
672 let _ = write!(self.out, ", parent !{}", parent.index());
673 }
674 let _ = write!(self.out, ", offset {}", node.offset);
675 self.out.push('\n');
676 }
677
678 fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
680 let _ = write!(self.out, ", linkage({})", linkage.name());
681 if visibility != Visibility::Default {
682 let _ = write!(self.out, ", visibility({})", visibility.name());
683 }
684 }
685
686 fn section(&mut self, section: Option<Symbol>) {
688 if let Some(section) = section {
689 self.out.push_str(", section ");
690 self.string(self.names.resolve(section).as_bytes());
691 }
692 }
693
694 fn string(&mut self, bytes: &[u8]) {
696 self.out.push('"');
697 for &byte in bytes {
698 match byte {
699 b'"' => self.out.push_str("\\\""),
700 b'\\' => self.out.push_str("\\\\"),
701 0x20..=0x7e => self.out.push(byte as char),
702 _ => {
703 let _ = write!(self.out, "\\{byte:02x}");
704 }
705 }
706 }
707 self.out.push('"');
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use rucc_base::Interner;
714 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
715
716 use super::*;
717 use crate::func::Builder;
718 use crate::inst::{AsmInfo, CallInfo, MetaNode, SwitchInfo, VaInfo};
719 use crate::module::{AliasKind, TlsModel};
720 use crate::{AttrSet, Attrs, Flags, FloatPred, FpContract, IntPred, RmwOp};
721
722 fn target() -> TargetInfo {
723 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
724 }
725
726 #[test]
727 fn the_example_in_the_spec() {
728 let mut names = Interner::new();
729 let mut module = Module::new(names.intern("example.c"), &target());
730
731 let char_node = module.add_meta(MetaNode {
732 name: names.intern("omnipotent char"),
733 parent: None,
734 offset: 0,
735 });
736 let int_node = module.add_meta(MetaNode {
737 name: names.intern("int"),
738 parent: Some(char_node),
739 offset: 0,
740 });
741
742 let i32_ = Type::int(32);
743 let zero_bits = module.add_imm(Imm::int(0, i32_));
744 let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
745 let mut counter = Global::new(names.intern("counter"), 4, 4);
746 counter.linkage = Linkage::Internal;
747 counter.init = Some(init);
748 module.add_global(counter);
749
750 let mut func = Func::new(
751 names.intern("sum"),
752 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
753 );
754 func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
755 let entry = func.create_block();
756 let n = func.append_param(entry, i32_);
757 let header = func.create_block();
758 let acc = func.append_param(header, i32_);
759 let i = func.append_param(header, i32_);
760 let exit = func.create_block();
761 let result = func.append_param(exit, i32_);
762
763 let mut b = Builder::new(&mut func, entry);
764 let zero = b.iconst(i32_, 0);
765 let cmp = b.icmp(IntPred::Sle, n, zero);
766 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
767
768 let mut b = Builder::new(&mut func, header);
769 let one = b.iconst(i32_, 1);
770 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
771 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
772 let done = b.icmp(IntPred::Sge, next, n);
773 b.br_if(done, exit, &[total], header, &[total, next]);
774
775 let mut b = Builder::new(&mut func, exit);
776 let address = b.value(
777 InstData {
778 extra: Extra::Symbol(names.intern("counter")),
779 ..InstData::new(Opcode::GlobalAddr)
780 },
781 Type::PTR,
782 );
783 b.store(
784 result,
785 address,
786 MemInfo { size: 0, align: 4, order: MemOrder::NotAtomic, tbaa: Some(int_node) },
787 Flags::NONE,
788 );
789 b.ret(&[result]);
790 module.add_func(func);
791
792 assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
793 }
794
795 #[test]
796 fn one_of_almost_everything() {
797 let mut names = Interner::new();
798 let mut module = Module::new(names.intern("zoo.c"), &target());
799 let int_node =
800 module.add_meta(MetaNode { name: names.intern("int"), parent: None, offset: 0 });
801
802 let i32_ = Type::int(32);
803 let i64_ = Type::int(64);
804 let f64_ = Type::float(crate::Float::F64);
805 let mut func = Func::new(
806 names.intern("zoo"),
807 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
808 );
809 let entry = func.create_block();
810 let n = func.append_param(entry, i32_);
811 let p = func.append_param(entry, Type::PTR);
812 let middle = func.create_block();
813 let other = func.create_block();
814 let exit = func.create_block();
815 let taken = func.append_param(exit, i32_);
816 let arrival = func.create_block();
817
818 let mut b = Builder::new(&mut func, entry);
819 let minus_one = b.iconst(i64_, -1);
820 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
821 let seven = b.func().add_imm(Imm::int(7, i32_));
822 let vector = b.value(
823 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
824 Type::vector(i32_, 4),
825 );
826 let stack = b.func().add_mem(MemInfo {
827 size: 16,
828 align: 8,
829 order: MemOrder::NotAtomic,
830 tbaa: None,
831 });
832 let slot = b.value(
833 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
834 Type::PTR,
835 );
836 let args = b.func().push_values(&[slot, minus_one]);
837 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
838 let plain = MemInfo { size: 0, align: 4, order: MemOrder::NotAtomic, tbaa: Some(int_node) };
839 let loaded = b.load(i32_, addr, plain, Flags::NONE);
840 b.store(loaded, addr, plain, Flags::VOLATILE);
841
842 let atomic =
843 b.func().add_mem(MemInfo { size: 0, align: 4, order: MemOrder::SeqCst, tbaa: None });
844 let args = b.func().push_values(&[addr, n]);
845 let old = b.value(
846 InstData {
847 args,
848 extra: Extra::Rmw(RmwOp::Add, atomic),
849 ..InstData::new(Opcode::AtomicRmw)
850 },
851 i32_,
852 );
853 let args = b.func().push_values(&[addr, old, n]);
854 b.inst(
855 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
856 &[i32_, Type::I1],
857 );
858 b.inst(
859 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
860 &[],
861 );
862 b.unary(Opcode::SExt, n, i64_);
863 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
864 let args = b.func().push_values(&[n, n]);
865 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
866 let puts = b.func().add_signature(
867 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
868 );
869 b.call_varargs(
870 names.intern("puts"),
871 puts,
872 &[p, slot],
873 &[Abi::ByVal { size: 16, align: 8 }],
874 );
875 let indirect =
876 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
877 let varargs = b.func().push_abis(&[]);
878 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
879 let args = b.func().push_values(&[p, n]);
880 b.value(
881 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
882 i32_,
883 );
884 let copy = b.func().add_mem(MemInfo {
885 size: 16,
886 align: 8,
887 order: MemOrder::NotAtomic,
888 tbaa: None,
889 });
890 let args = b.func().push_values(&[slot, p]);
891 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
892 let asm = b.func().add_asm(AsmInfo {
893 template: names.intern("pause"),
894 constraints: names.intern(""),
895 clobbers: names.intern("memory"),
896 targets: crate::inst::BlockCallList::EMPTY,
897 });
898 b.inst(
899 InstData {
900 flags: Flags::VOLATILE,
901 extra: Extra::Asm(asm),
902 ..InstData::new(Opcode::InlineAsm)
903 },
904 &[],
905 );
906 let object = b.func().add_mem(MemInfo {
907 size: 16,
908 align: 8,
909 order: MemOrder::NotAtomic,
910 tbaa: None,
911 });
912 let slots = b.func().push_slots(&[
913 Slot::Integer { offset: 0, size: 8 },
914 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
915 ]);
916 let read = b.func().add_va_object(VaInfo { mem: object, slots });
917 let args = b.func().push_values(&[p]);
918 b.value(
919 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
920 Type::PTR,
921 );
922 let args = b.func().push_values(&[vector]);
923 b.value(
924 InstData {
925 args,
926 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
927 ..InstData::new(Opcode::TargetIntrinsic)
928 },
929 i32_,
930 );
931 b.jump(middle, &[]);
932
933 let mut b = Builder::new(&mut func, middle);
934 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
935 let default = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
936 let first = BlockCall { block: exit, args: b.func().push_values(&[n]) };
937 let second = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
938 let targets = b.func().push_block_calls(&[default, first, second]);
939 let switch = b.func().add_switch(SwitchInfo { targets, cases });
940 let args = b.func().push_values(&[n]);
941 b.inst(
942 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
943 &[],
944 );
945
946 let mut b = Builder::new(&mut func, other);
947 let address = b.block_addr(arrival);
948 b.indirect_br(address, &[arrival]);
949
950 let mut b = Builder::new(&mut func, exit);
951 b.ret(&[taken]);
952
953 let mut b = Builder::new(&mut func, arrival);
954 let call = BlockCall { block: exit, args: b.func().push_values(&[n]) };
955 let targets = b.func().push_block_calls(&[call]);
956 let goto = b.func().add_asm(AsmInfo {
957 template: names.intern("jmp %l0"),
958 constraints: names.intern(""),
959 clobbers: names.intern(""),
960 targets,
961 });
962 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
963
964 module.add_func(func);
965
966 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
967 }
968
969 #[test]
970 fn the_shapes_a_symbol_comes_in() {
971 let mut names = Interner::new();
972 let mut module = Module::new(names.intern("data.c"), &target());
973
974 let i32_ = Type::int(32);
975 let text = module.push_bytes(b"hi\x00\xff\"\\");
976 let entry_name = names.intern("hi.str");
977 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
978 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
979 let seven = module.add_imm(Imm::int(7, i32_));
980 let image = module.push_data(&[
981 Datum::Bytes(text),
982 Datum::Zero(2),
983 Datum::Scalar { ty: i32_, value: seven },
984 Datum::Addr(forward),
985 Datum::Addr(backward),
986 ]);
987 let mut table = Global::new(names.intern("table"), 28, 8);
988 table.init = Some(image);
989 table.constant = true;
990 table.section = Some(names.intern(".rodata.rel"));
991 module.add_global(table);
992
993 let mut errno = Global::new(names.intern("errno"), 4, 4);
994 errno.tls = Some(TlsModel::InitialExec);
995 errno.visibility = Visibility::Hidden;
996 module.add_global(errno);
997
998 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1003 nothing.init = Some(module.push_data(&[]));
1004 nothing.linkage = Linkage::Internal;
1005 module.add_global(nothing);
1006
1007 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1008 alias.linkage = Linkage::Weak;
1009 module.add_alias(alias);
1010 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1011 memcpy.kind = AliasKind::IFunc;
1012 memcpy.visibility = Visibility::Protected;
1013 module.add_alias(memcpy);
1014
1015 let mut puts = Func::new(
1016 names.intern("puts"),
1017 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1018 );
1019 puts.linkage = Linkage::External;
1020 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1021 module.add_func(puts);
1022
1023 let mut helper =
1024 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1025 helper.linkage = Linkage::Internal;
1026 helper.section = Some(names.intern(".text.hot"));
1027 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1028 let block = helper.create_block();
1029 let mut b = Builder::new(&mut helper, block);
1030 let one = b.iconst(i32_, 1);
1031 b.ret(&[one, one]);
1032 module.add_func(helper);
1033
1034 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1035 }
1036
1037 #[test]
1038 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1039 let mut names = Interner::new();
1040 let module = Module::new(names.intern("abi.c"), &target());
1041 let mut func = Func::new(
1042 names.intern("f"),
1043 Signature::new()
1044 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1045 .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
1046 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1047 .and_param(Param::new(Type::int(32))),
1048 );
1049 let entry = func.create_block();
1050 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1051 func.append_param(entry, param);
1052 }
1053 let mut b = Builder::new(&mut func, entry);
1054 b.ret(&[]);
1055
1056 assert_eq!(
1057 print_func(&module, &func, &names),
1058 "\
1059func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1060block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1061 return
1062}
1063"
1064 );
1065 }
1066
1067 #[test]
1068 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1069 let mut names = Interner::new();
1070 let module = Module::new(names.intern("varargs.c"), &target());
1071 let i32_ = Type::int(32);
1072 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1073 let entry = func.create_block();
1074 let p = func.append_param(entry, Type::PTR);
1075 let mut b = Builder::new(&mut func, entry);
1076 let sig = b.func().add_signature(
1077 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1078 );
1079 let one = b.iconst(i32_, 1);
1080 b.call_varargs(
1081 names.intern("printf"),
1082 sig,
1083 &[p, one, p],
1084 &[Abi::Plain, Abi::ByVal { size: 24, align: 8 }],
1085 );
1086 b.ret(&[]);
1087
1088 assert_eq!(
1089 print_func(&module, &func, &names),
1090 "\
1091func @f(ptr), linkage(external) {
1092block0(%0: ptr):
1093 %1 = iconst.i32 1
1094 %2 = call @printf(%0, %1, %0 byval(24, align 8)) : (ptr, ...) -> i32
1095 return
1096}
1097"
1098 );
1099 }
1100
1101 #[test]
1102 fn numbering_follows_the_text_and_not_the_tables() {
1103 let mut names = Interner::new();
1108 let mut module = Module::new(names.intern("order.c"), &target());
1109 let i32_ = Type::int(32);
1110 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1111 let entry = func.create_block();
1112 let middle = func.create_block();
1113 let exit = func.create_block();
1114 let arrived = func.append_param(exit, i32_);
1115
1116 let mut b = Builder::new(&mut func, exit);
1117 b.ret(&[arrived]);
1118 let mut b = Builder::new(&mut func, middle);
1119 let two = b.iconst(i32_, 2);
1120 b.jump(exit, &[two]);
1121 let mut b = Builder::new(&mut func, entry);
1122 b.jump(middle, &[]);
1123 module.add_func(func);
1124
1125 assert_eq!(
1126 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1127 "\
1128func @f() -> i32, linkage(external) {
1129block0:
1130 jump block1
1131
1132block1:
1133 %0 = iconst.i32 2
1134 jump block2(%0)
1135
1136block2(%1: i32):
1137 return %1
1138}
1139"
1140 );
1141 }
1142}