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, Drains, Imm, Inst, InstData, MemInfo, Meta, MetaNode, Param, PlaneNode,
36 Signature, Value,
37};
38use crate::module::{Alias, Datum, Global, Module, Reloc};
39use crate::{Extra, FORMAT_VERSION, Linkage, MemOrder, Opcode, Type, Visibility};
40
41#[derive(Clone, Copy)]
43struct Chain {
44 takes: Option<Value>,
46 gives: bool,
48}
49
50fn without_mem(mut results: Vec<Value>, chain: Chain) -> Vec<Value> {
56 if chain.gives {
57 results.pop();
58 }
59 results
60}
61
62pub(crate) fn implied_result(opcode: Opcode) -> bool {
72 matches!(
73 opcode,
74 Opcode::ICmp
75 | Opcode::FCmp
76 | Opcode::GlobalAddr
77 | Opcode::BlockAddr
78 | Opcode::Alloca
79 | Opcode::Call
80 | Opcode::CallIndirect
81 | Opcode::TailCall
82 | Opcode::CapOf
84 | Opcode::CapLoad
85 | Opcode::CapNull
86 | Opcode::CapNarrow
87 | Opcode::CapRecover
88 )
89}
90
91#[must_use]
93pub fn print(module: &Module, names: &Interner) -> String {
94 let mut printer = Printer::new(module, names);
95 printer.module();
96 printer.finish()
97}
98
99#[must_use]
101pub fn print_func(module: &Module, func: &Func, names: &Interner) -> String {
102 let mut printer = Printer::new(module, names);
103 printer.func(func);
104 printer.finish()
105}
106
107#[derive(Debug)]
109pub struct Printer<'a> {
110 module: &'a Module,
111 names: &'a Interner,
112 out: String,
113 values: Vec<u32>,
117 blocks: Vec<u32>,
118}
119
120impl<'a> Printer<'a> {
121 #[must_use]
123 pub fn new(module: &'a Module, names: &'a Interner) -> Printer<'a> {
124 Printer { module, names, out: String::new(), values: Vec::new(), blocks: Vec::new() }
125 }
126
127 #[must_use]
129 pub fn finish(self) -> String {
130 self.out
131 }
132
133 pub fn module(&mut self) {
135 let module = self.module;
136 let name = self.names.resolve(module.name);
137 let _ = writeln!(self.out, "; ModuleID = '{name}'");
140 let _ = writeln!(self.out, "; format {FORMAT_VERSION}");
141 let _ = writeln!(self.out, "target triple = \"{}\"", module.tuple.to_llvm_string());
142 let _ = writeln!(self.out, "target datalayout = \"{}\"", module.datalayout);
143
144 if module.globals().next().is_some() {
145 self.out.push('\n');
146 for id in module.globals() {
147 self.global(&module[id]);
148 }
149 }
150 if module.aliases().next().is_some() {
151 self.out.push('\n');
152 for id in module.aliases() {
153 self.alias(&module[id]);
154 }
155 }
156 for id in module.funcs() {
157 self.out.push('\n');
158 self.func(&module[id]);
159 }
160 if module.metadata().next().is_some() {
161 self.out.push('\n');
162 for meta in module.metadata() {
163 self.meta_node(meta);
164 }
165 }
166 }
167
168 fn global(&mut self, global: &Global) {
172 let _ = write!(self.out, "global @{} : ", self.names.resolve(global.name));
173 match self.scalar_init(global) {
174 Some((ty, imm)) => {
178 let _ = write!(self.out, "{ty} = ");
179 self.imm(imm, ty);
180 }
181 None => {
182 let _ = write!(self.out, "bytes {}", global.size);
183 if let Some(init) = global.init {
184 let data = &self.module[init];
191 if data.is_empty() {
192 self.out.push_str(" = {}");
193 } else {
194 self.out.push_str(" = { ");
195 for (index, &datum) in data.iter().enumerate() {
196 if index > 0 {
197 self.out.push_str(", ");
198 }
199 self.datum(datum);
200 }
201 self.out.push_str(" }");
202 }
203 }
204 }
205 }
206 let _ = write!(self.out, ", align {}", global.align);
207 self.linkage(global.linkage, global.visibility);
208 if let Some(model) = global.tls {
209 let _ = write!(self.out, ", tls({})", model.name());
210 }
211 if global.constant {
212 self.out.push_str(", constant");
213 }
214 self.section(global.section);
215 self.out.push('\n');
216 }
217
218 fn scalar_init(&self, global: &Global) -> Option<(Type, Imm)> {
220 let init = global.init?;
221 let [datum] = self.module[init] else { return None };
222 let Datum::Scalar { ty, value } = datum else { return None };
223 (datum.size(self.module) == global.size).then(|| (ty, self.module[value]))
224 }
225
226 fn datum(&mut self, datum: Datum) {
228 match datum {
229 Datum::Zero(bytes) => {
230 let _ = write!(self.out, "zero {bytes}");
231 }
232 Datum::Bytes(range) => {
233 self.out.push_str("bytes ");
234 let bytes = &self.module[range];
235 self.string(bytes);
236 }
237 Datum::Scalar { ty, value } => {
238 let _ = write!(self.out, "{ty} ");
239 self.imm(self.module[value], ty);
240 }
241 Datum::Addr(reloc) | Datum::Away(reloc) | Datum::Apart { to: reloc, .. } => {
242 let what = match datum {
246 Datum::Away(_) => "away",
247 Datum::Apart { .. } => "apart",
248 _ => "addr",
249 };
250 let Reloc { symbol, addend, size } = self.module[reloc];
251 let _ = write!(self.out, "{what}.{size} @{}", self.names.resolve(symbol));
252 match addend.signum() {
253 1 => {
254 let _ = write!(self.out, " + {addend}");
255 }
256 -1 => {
257 let _ = match addend.checked_neg() {
261 Some(amount) => write!(self.out, " - {amount}"),
262 None => write!(self.out, " + {addend}"),
263 };
264 }
265 _ => {}
266 }
267 if let Datum::Apart { from, .. } = datum {
268 let _ = write!(self.out, " from @{}", self.names.resolve(from));
269 }
270 }
271 }
272 }
273
274 fn alias(&mut self, alias: &Alias) {
276 let _ = write!(
277 self.out,
278 "{} @{} = @{}",
279 alias.kind.name(),
280 self.names.resolve(alias.name),
281 self.names.resolve(alias.target)
282 );
283 self.linkage(alias.linkage, alias.visibility);
284 self.out.push('\n');
285 }
286
287 pub fn func(&mut self, func: &Func) {
291 self.number(func);
292 let _ = write!(self.out, "func @{}", self.names.resolve(func.name));
293 self.signature(func.signature());
294 self.linkage(func.linkage, func.visibility);
295 if !func.attrs.is_default() {
296 let _ = write!(self.out, ", {}", func.attrs);
297 }
298 self.section(func.section);
299 if let Some(spelled) = func.spelled {
300 self.out.push_str(", spelled ");
301 self.string(self.names.resolve(spelled).as_bytes());
302 }
303 if func.is_declaration() {
304 self.out.push_str(";\n");
305 return;
306 }
307 self.out.push_str(" {\n");
308 for (index, block) in func.blocks().enumerate() {
309 if index > 0 {
310 self.out.push('\n');
311 }
312 self.block(func, block);
313 }
314 self.labels(func);
315 self.facts(func);
316 self.out.push_str("}\n");
317 }
318
319 fn labels(&mut self, func: &Func) {
327 let mut first = true;
328 for (block, name) in func.named_blocks() {
329 if first {
330 self.out.push_str("\nlabels:\n");
331 first = false;
332 }
333 let number = self.blocks[block.index()];
334 let _ = writeln!(self.out, " block{number} = @{}", self.names.resolve(name));
335 }
336 }
337
338 fn facts(&mut self, func: &Func) {
346 let mut first = true;
347 for (value, facts) in func.known() {
348 if first {
349 self.out.push_str("\nfacts:\n");
350 first = false;
351 }
352 self.out.push_str(" ");
353 self.value(value);
354 self.out.push_str(" = ");
355 let mut sep = false;
356 let mut comma = |out: &mut String| {
357 if sep {
358 out.push_str(", ");
359 }
360 sep = true;
361 };
362 if let Some(bounds) = facts.bounds {
363 comma(&mut self.out);
364 self.out.push_str("!bounds(");
365 self.value(bounds.lo);
366 self.out.push_str(", ");
367 self.value(bounds.ext);
368 self.out.push(')');
369 }
370 if facts.live {
371 comma(&mut self.out);
372 self.out.push_str("!live");
373 }
374 if let Some(n) = facts.init {
375 comma(&mut self.out);
376 let _ = write!(self.out, "!init({n})");
377 }
378 if let Some(align) = facts.align {
379 comma(&mut self.out);
380 let _ = write!(self.out, "!aligned({align})");
381 }
382 self.out.push('\n');
383 }
384 }
385
386 fn number(&mut self, func: &Func) {
391 let counts = func.counts();
392 self.values.clear();
393 self.values.resize(counts.values, u32::MAX);
394 self.blocks.clear();
395 self.blocks.resize(counts.blocks, u32::MAX);
396 let mut next = 0;
397 for (index, block) in func.blocks().enumerate() {
398 self.blocks[block.index()] = index as u32;
399 for ¶m in &func[block].params {
400 self.values[param.index()] = next;
401 next += 1;
402 }
403 for inst in func.insts(block) {
404 for result in func[inst].results() {
405 self.values[result.index()] = next;
406 next += 1;
407 }
408 }
409 }
410 }
411
412 fn signature(&mut self, signature: &Signature) {
414 self.out.push('(');
415 for (index, param) in signature.params.iter().enumerate() {
416 if index > 0 {
417 self.out.push_str(", ");
418 }
419 self.param(param);
420 }
421 if signature.variadic {
422 if !signature.params.is_empty() {
423 self.out.push_str(", ");
424 }
425 self.out.push_str("...");
426 }
427 self.out.push(')');
428 match signature.returns.as_slice() {
429 [] => {}
430 [param] => {
431 self.out.push_str(" -> ");
432 self.param(param);
433 }
434 params => {
435 self.out.push_str(" -> (");
436 for (index, param) in params.iter().enumerate() {
437 if index > 0 {
438 self.out.push_str(", ");
439 }
440 self.param(param);
441 }
442 self.out.push(')');
443 }
444 }
445 }
446
447 fn param(&mut self, param: &Param) {
449 let _ = write!(self.out, "{}", param.ty);
450 self.abi(param.abi);
451 }
452
453 fn abi(&mut self, abi: Abi) {
456 let _ = match abi {
457 Abi::Plain => Ok(()),
458 Abi::Sext => write!(self.out, " sext"),
459 Abi::Zext => write!(self.out, " zext"),
460 Abi::ByVal { size, align, drains } => {
461 let drains = match drains {
462 Drains::Nothing => "",
463 Drains::Integers => ", drains integers",
464 Drains::Floats => ", drains floats",
465 };
466 write!(self.out, " byval({size}, align {align}{drains})")
467 }
468 Abi::Sret { size, align } => write!(self.out, " sret({size}, align {align})"),
469 };
470 }
471
472 fn block(&mut self, func: &Func, block: Block) {
474 let _ = write!(self.out, "block{}", self.blocks[block.index()]);
475 let params = &func[block].params;
476 if !params.is_empty() {
477 self.out.push('(');
478 for (index, ¶m) in params.iter().enumerate() {
479 if index > 0 {
480 self.out.push_str(", ");
481 }
482 self.value(param);
483 let _ = write!(self.out, ": {}", func[param].ty);
484 }
485 self.out.push(')');
486 }
487 self.out.push_str(":\n");
488 for inst in func.insts(block) {
489 self.inst(func, inst);
490 }
491 }
492
493 fn inst(&mut self, func: &Func, inst: Inst) {
495 let data = func[inst];
496 let chain = Chain { takes: func.mem_in(inst), gives: func.mem_out(inst).is_some() };
501 self.out.push_str(" ");
502 for (index, result) in data.results().enumerate() {
503 if index > 0 {
504 self.out.push_str(", ");
505 }
506 self.value(result);
507 }
508 if data.results > 0 {
509 self.out.push_str(" = ");
510 }
511 self.out.push_str(data.opcode.name());
512 self.result_types(func, &data, chain);
513 let _ = write!(self.out, "{}", data.flags);
514 self.operands(func, &data, chain);
515 if let Some(mem) = chain.takes {
516 self.out.push_str(" [mem ");
517 self.value(mem);
518 self.out.push(']');
519 }
520 self.out.push('\n');
521 }
522
523 fn result_types(&mut self, func: &Func, data: &InstData, chain: Chain) {
525 let results = without_mem(data.results().collect(), chain);
526 match results.as_slice() {
527 [] => {}
528 _ if implied_result(data.opcode) => {}
529 [result] => {
530 let ty = func[*result].ty;
531 let takes_the_same = func[data.args].first().is_some_and(|&arg| func[arg].ty == ty);
532 if !takes_the_same {
533 let _ = write!(self.out, ".{ty}");
534 }
535 }
536 types => {
539 self.out.push_str(".(");
540 for (index, &result) in types.iter().enumerate() {
541 if index > 0 {
542 self.out.push_str(", ");
543 }
544 let _ = write!(self.out, "{}", func[result].ty);
545 }
546 self.out.push(')');
547 }
548 }
549 }
550
551 fn operands(&mut self, func: &Func, data: &InstData, chain: Chain) {
553 let all = &func[data.args];
554 let args = &all[..all.len() - usize::from(chain.takes.is_some())];
555 match data.extra {
556 Extra::None => self.value_list_spaced(args),
557 Extra::Imm(imm) => {
558 self.out.push(' ');
559 let ty = data.first_result.map_or(Type::VOID, |result| func[result].ty);
560 self.imm(func[imm], ty);
561 }
562 Extra::Symbol(symbol) => {
563 let _ = write!(self.out, " @{}", self.names.resolve(symbol));
564 if !args.is_empty() {
565 self.out.push('(');
566 self.value_list(args);
567 self.out.push(')');
568 }
569 }
570 Extra::IntPred(pred) => {
571 let _ = write!(self.out, " {}", pred.name());
572 self.value_list_spaced(args);
573 }
574 Extra::FloatPred(pred) => {
575 let _ = write!(self.out, " {}", pred.name());
576 self.value_list_spaced(args);
577 }
578 Extra::Mem(mem) => {
579 match (data.opcode, args) {
580 (Opcode::Store | Opcode::AtomicStore, [value, addr]) => {
583 self.out.push(' ');
584 self.value(*value);
585 self.out.push_str(" -> ");
586 self.value(*addr);
587 }
588 _ => self.value_list_spaced(args),
589 }
590 self.mem(func[mem]);
591 }
592 Extra::VaObject(info) => {
593 let info = func[info];
594 self.value_list_spaced(args);
595 self.mem(func[info.mem]);
596 let slots = &func[info.slots];
597 if !slots.is_empty() {
598 self.out.push_str(", in(");
599 for (index, &slot) in slots.iter().enumerate() {
600 if index > 0 {
601 self.out.push_str(", ");
602 }
603 self.slot(slot);
604 }
605 self.out.push(')');
606 }
607 }
608 Extra::Rmw(op, mem) => {
609 let _ = write!(self.out, " {}", op.name());
610 self.value_list_spaced(args);
611 self.mem(func[mem]);
612 }
613 Extra::Class(class) => {
615 self.value_list_spaced(args);
616 let _ = write!(self.out, ", class {}", class.name());
617 }
618 Extra::Owner(owner) => {
619 self.value_list_spaced(args);
620 let _ = write!(self.out, ", to {}", owner.name());
621 }
622 Extra::Node(node) => {
623 self.value_list_spaced(args);
624 let _ = write!(self.out, ", tbaa !{}", node.index());
625 }
626 Extra::Reason(reason) => {
627 self.out.push(' ');
628 self.string(self.names.resolve(reason).as_bytes());
629 }
630 Extra::Order(order) => {
631 let _ = write!(self.out, " {}", order.name());
632 }
633 Extra::Prefetch(hint) => {
634 self.value_list_spaced(args);
635 let _ = write!(self.out, ", {hint}");
636 }
637 Extra::Depth(depth) => {
640 let _ = write!(self.out, " depth {depth}");
641 }
642 Extra::Question(kind) => {
643 self.value_list_spaced(args);
644 let _ = write!(self.out, ", kind {kind}");
645 }
646 Extra::Targets(targets) => {
647 if !args.is_empty() {
650 self.value_list_spaced(args);
651 self.out.push(',');
652 }
653 for (index, &call) in func[targets].iter().enumerate() {
654 self.out.push_str(if index > 0 { ", " } else { " " });
655 self.block_call(func, call);
656 }
657 }
658 Extra::Call(call) => {
659 let info = func[call];
660 let rest = match info.callee {
661 Some(callee) => {
662 let _ = write!(self.out, " @{}", self.names.resolve(callee));
663 args
664 }
665 None => {
668 self.out.push(' ');
669 match args.split_first() {
670 Some((&addr, rest)) => {
671 self.value(addr);
672 rest
673 }
674 None => {
675 self.out.push_str("%?");
676 &[]
677 }
678 }
679 }
680 };
681 self.out.push('(');
682 let named = func[info.signature].params.len();
685 let varargs = &func[info.varargs];
686 for (index, &arg) in rest.iter().enumerate() {
687 if index > 0 {
688 self.out.push_str(", ");
689 }
690 self.value(arg);
691 if let Some(&abi) = index.checked_sub(named).and_then(|at| varargs.get(at)) {
692 self.abi(abi);
693 }
694 }
695 self.out.push_str(") : ");
696 self.signature(&func[info.signature]);
697 }
698 Extra::Switch(switch) => {
699 let info = func[switch];
700 let ty = args.first().map_or(Type::VOID, |&arg| func[arg].ty);
701 self.value_list_spaced(args);
702 if let Some((&default, cases)) = func[info.targets].split_first() {
703 self.out.push_str(", ");
704 self.block_call(func, default);
705 self.out.push_str(", [");
706 for (index, (&case, &value)) in cases.iter().zip(&func[info.cases]).enumerate()
707 {
708 if index > 0 {
709 self.out.push_str(", ");
710 }
711 self.imm(value, ty);
712 self.out.push_str(" => ");
713 self.block_call(func, case);
714 }
715 self.out.push(']');
716 }
717 }
718 Extra::Asm(asm) => {
719 let info = func[asm];
720 self.out.push(' ');
721 self.string(self.names.resolve(info.template).as_bytes());
722 self.out.push_str(", ");
723 self.string(self.names.resolve(info.constraints).as_bytes());
724 self.out.push_str(", ");
725 self.string(self.names.resolve(info.clobbers).as_bytes());
726 self.out.push('(');
727 self.value_list(args);
728 self.out.push(')');
729 if !info.targets.is_empty() {
730 self.out.push_str(", labels [");
731 for (index, &call) in func[info.targets].iter().enumerate() {
732 if index > 0 {
733 self.out.push_str(", ");
734 }
735 self.block_call(func, call);
736 }
737 self.out.push(']');
738 }
739 }
740 }
741 }
742
743 fn value_list_spaced(&mut self, args: &[Value]) {
745 if args.is_empty() {
746 return;
747 }
748 self.out.push(' ');
749 self.value_list(args);
750 }
751
752 fn value_list(&mut self, args: &[Value]) {
754 for (index, &arg) in args.iter().enumerate() {
755 if index > 0 {
756 self.out.push_str(", ");
757 }
758 self.value(arg);
759 }
760 }
761
762 fn block_call(&mut self, func: &Func, call: BlockCall) {
767 let _ = write!(self.out, "block{}", self.blocks[call.block.index()]);
768 let args = &func[call.args];
769 if !args.is_empty() {
770 self.out.push('(');
771 self.value_list(args);
772 self.out.push(')');
773 }
774 if let Some(parts) = call.hint.taken() {
775 let _ = write!(self.out, " taken {parts}");
776 }
777 }
778
779 fn mem(&mut self, info: MemInfo) {
781 if info.size != 0 {
782 let _ = write!(self.out, ", size {}", info.size);
783 }
784 let _ = write!(self.out, ", align {}", info.align);
785 if info.order != MemOrder::NotAtomic {
786 let _ = write!(self.out, ", {}", info.order.name());
787 }
788 if let Some(tbaa) = info.tbaa {
789 let _ = write!(self.out, ", tbaa !{}", tbaa.index());
790 }
791 if info.owns != 0 {
792 let _ = write!(self.out, ", owns {}", info.owns);
793 }
794 if info.restrict.clique != 0 {
795 let _ =
796 write!(self.out, ", restrict({}, {})", info.restrict.clique, info.restrict.base);
797 }
798 }
799
800 fn slot(&mut self, slot: Slot) {
802 match slot {
803 Slot::Integer { offset, size } => {
804 let _ = write!(self.out, "int {size} at {offset}");
805 }
806 Slot::Float { offset, format } => {
807 let _ = write!(self.out, "float {} at {offset}", format.name());
808 }
809 }
810 }
811
812 fn value(&mut self, value: Value) {
814 match self.values.get(value.index()).copied() {
815 Some(number) if number != u32::MAX => {
816 let _ = write!(self.out, "%{number}");
817 }
818 _ => self.out.push_str("%?"),
822 }
823 }
824
825 fn imm(&mut self, imm: Imm, ty: Type) {
827 let scalar = if ty.is_vector() { ty.lane() } else { ty };
828 if scalar.is_float() {
829 let _ = write!(self.out, "{:#x}", imm.bits());
832 } else if scalar.is_int() {
833 let _ = write!(self.out, "{}", imm.signed(scalar));
834 } else {
835 let _ = write!(self.out, "{:#x}", imm.bits());
836 }
837 }
838
839 fn meta_node(&mut self, meta: Meta) {
841 let _ = write!(self.out, "!{} = ", meta.index());
842 match self.module[meta] {
843 MetaNode::Tbaa(node) => {
844 self.out.push_str("tbaa ");
845 self.string(self.names.resolve(node.name).as_bytes());
846 if let Some(parent) = node.parent {
847 let _ = write!(self.out, ", parent !{}", parent.index());
848 }
849 let _ = write!(self.out, ", offset {}", node.offset);
850 }
851 MetaNode::Plane(node) => {
852 self.out.push_str("plane ");
853 let _ = match node {
854 PlaneNode::Type(ty) => write!(self.out, "!{}", ty.index()),
855 PlaneNode::NoType => self.out.write_str("no_type"),
856 PlaneNode::Character => self.out.write_str("character"),
857 PlaneNode::PointerSlot(k) => write!(self.out, "pointer_slot {k}"),
858 };
859 }
860 }
861 self.out.push('\n');
862 }
863
864 fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
866 let _ = write!(self.out, ", linkage({})", linkage.name());
867 if visibility != Visibility::Default {
868 let _ = write!(self.out, ", visibility({})", visibility.name());
869 }
870 }
871
872 fn section(&mut self, section: Option<Symbol>) {
874 if let Some(section) = section {
875 self.out.push_str(", section ");
876 self.string(self.names.resolve(section).as_bytes());
877 }
878 }
879
880 fn string(&mut self, bytes: &[u8]) {
882 self.out.push('"');
883 for &byte in bytes {
884 match byte {
885 b'"' => self.out.push_str("\\\""),
886 b'\\' => self.out.push_str("\\\\"),
887 0x20..=0x7e => self.out.push(byte as char),
888 _ => {
889 let _ = write!(self.out, "\\{byte:02x}");
890 }
891 }
892 }
893 self.out.push('"');
894 }
895}
896
897#[cfg(test)]
898mod tests {
899 use rucc_base::Interner;
900 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
901
902 use super::*;
903 use crate::Restrict;
904 use crate::func::Builder;
905 use crate::inst::{AsmInfo, CallInfo, MetaNode, PlaneNode, SwitchInfo, TbaaNode, VaInfo};
906 use crate::module::{AliasKind, TlsModel};
907 use crate::{
908 AttrSet, Attrs, Bounds, Facts, Flags, FloatPred, FpContract, IntPred, Owner, RmwOp,
909 StorageClass,
910 };
911
912 fn target() -> TargetInfo {
913 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
914 }
915
916 #[test]
917 fn the_example_in_the_spec() {
918 let mut names = Interner::new();
919 let mut module = Module::new(names.intern("example.c"), &target());
920
921 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
922 name: names.intern("omnipotent char"),
923 parent: None,
924 offset: 0,
925 }));
926 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
927 name: names.intern("int"),
928 parent: Some(char_node),
929 offset: 0,
930 }));
931
932 let i32_ = Type::int(32);
933 let zero_bits = module.add_imm(Imm::int(0, i32_));
934 let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
935 let mut counter = Global::new(names.intern("counter"), 4, 4);
936 counter.linkage = Linkage::Internal;
937 counter.init = Some(init);
938 module.add_global(counter);
939
940 let mut func = Func::new(
941 names.intern("sum"),
942 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
943 );
944 func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
945 let entry = func.create_block();
946 let n = func.append_param(entry, i32_);
947 let header = func.create_block();
948 let acc = func.append_param(header, i32_);
949 let i = func.append_param(header, i32_);
950 let exit = func.create_block();
951 let result = func.append_param(exit, i32_);
952
953 let mut b = Builder::new(&mut func, entry);
954 let zero = b.iconst(i32_, 0);
955 let cmp = b.icmp(IntPred::Sle, n, zero);
956 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
957
958 let mut b = Builder::new(&mut func, header);
959 let one = b.iconst(i32_, 1);
960 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
961 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
962 let done = b.icmp(IntPred::Sge, next, n);
963 b.br_if(done, exit, &[total], header, &[total, next]);
964
965 let mut b = Builder::new(&mut func, exit);
966 let address = b.value(
967 InstData {
968 extra: Extra::Symbol(names.intern("counter")),
969 ..InstData::new(Opcode::GlobalAddr)
970 },
971 Type::PTR,
972 );
973 b.store(
974 result,
975 address,
976 MemInfo {
977 size: 0,
978 align: 4,
979 order: MemOrder::NotAtomic,
980 tbaa: Some(int_node),
981 owns: 0,
982 restrict: Restrict::NONE,
983 },
984 Flags::NONE,
985 );
986 b.ret(&[result]);
987 module.add_func(func);
988
989 assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
990 }
991
992 #[test]
993 fn the_memory_safety_instructions() {
994 let mut names = Interner::new();
995 let mut module = Module::new(names.intern("safety.c"), &target());
996 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
997 name: names.intern("int"),
998 parent: None,
999 offset: 0,
1000 }));
1001 let int_plane = module.add_meta(MetaNode::Plane(PlaneNode::Type(int_node)));
1002 let character = module.add_meta(MetaNode::Plane(PlaneNode::Character));
1003 module.add_meta(MetaNode::Plane(PlaneNode::NoType));
1004 module.add_meta(MetaNode::Plane(PlaneNode::PointerSlot(3)));
1005
1006 let i64_ = Type::int(64);
1007 let mut func = Func::new(
1008 names.intern("safety"),
1009 Signature::new().with_params(&[Type::PTR, i64_]).with_returns(&[Type::PTR]),
1010 );
1011 let entry = func.create_block();
1012 let p = func.append_param(entry, Type::PTR);
1013 let off = func.append_param(entry, i64_);
1014
1015 let mut b = Builder::new(&mut func, entry);
1016 let of = b.unary(Opcode::CapOf, p, Type::CAP);
1017 b.inst(InstData::new(Opcode::CapNull), &[Type::CAP]);
1018 b.unary(Opcode::CapRecover, p, Type::CAP);
1019 let args = b.func().push_values(&[of, p, p]);
1020 b.value(InstData { args, ..InstData::new(Opcode::CapLoad) }, Type::CAP);
1021 let len = b.iconst(i64_, 8);
1022 let args = b.func().push_values(&[of, off, len]);
1023 let narrow = b.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1024 let args = b.func().push_values(&[of, p, p, narrow]);
1025 b.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1026 let args = b.func().push_values(&[of, p, off]);
1029 b.value(InstData { args, ..InstData::new(Opcode::CapExtent) }, i64_);
1030
1031 let args = b.func().push_values(&[p, off]);
1032 let derived = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1033 let four = MemInfo {
1034 size: 4,
1035 align: 4,
1036 order: MemOrder::NotAtomic,
1037 tbaa: None,
1038 owns: 0,
1039 restrict: Restrict::NONE,
1040 };
1041 let mut check = |opcode, info: Option<MemInfo>, on: &[Value]| {
1042 let args = b.func().push_values(on);
1043 let extra = match info {
1044 Some(info) => Extra::Mem(b.func().add_mem(info)),
1045 None => Extra::None,
1046 };
1047 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1048 };
1049 check(Opcode::CheckBounds, Some(four), &[of, p]);
1050 check(Opcode::CheckBounds, Some(four), &[of, p, off]);
1053 check(Opcode::CheckLive, None, &[of, p]);
1054 check(Opcode::CheckType, Some(MemInfo { tbaa: Some(int_plane), ..four }), &[of, p]);
1055 check(Opcode::CheckInit, Some(MemInfo { align: 1, ..four }), &[of, p]);
1056 check(Opcode::CheckDeriv, None, &[of, p, derived, len]);
1057 check(Opcode::CheckRace, Some(MemInfo { align: 1, ..four }), &[of, p]);
1058 check(Opcode::CheckFree, None, &[of, p]);
1061 let named = Restrict { clique: 1, base: 2 };
1065 check(Opcode::CheckRestrictRead, Some(MemInfo { restrict: named, ..four }), &[p]);
1066 check(Opcode::CheckRestrictWrite, Some(MemInfo { restrict: named, ..four }), &[p]);
1067
1068 let mut plane = |opcode, extra| {
1069 let args = b.func().push_values(&[p, off]);
1070 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1071 };
1072 plane(Opcode::MetaBegin, Extra::Class(StorageClass::Allocated));
1073 plane(Opcode::MetaType, Extra::Node(character));
1074 plane(Opcode::MetaInit, Extra::None);
1075 plane(Opcode::MetaEpoch, Extra::None);
1076 plane(Opcode::MetaTransfer, Extra::Owner(Owner::Device));
1077 plane(Opcode::MetaEnd, Extra::None);
1078
1079 let args = b.func().push_values(&[p, derived, off]);
1082 b.inst(InstData { args, ..InstData::new(Opcode::MetaTypeCopy) }, &[]);
1083 let args = b.func().push_values(&[p, derived, off]);
1084 b.inst(InstData { args, ..InstData::new(Opcode::MetaInitCopy) }, &[]);
1085 let args = b.func().push_values(&[p, derived, off]);
1086 b.inst(InstData { args, ..InstData::new(Opcode::CapCopy) }, &[]);
1087
1088 let mut edge = |opcode| {
1091 let args = b.func().push_values(&[p]);
1092 b.inst(InstData { args, ..InstData::new(opcode) }, &[]);
1093 };
1094 edge(Opcode::MetaRelease);
1095 edge(Opcode::MetaAcquire);
1096
1097 b.inst(InstData::new(Opcode::MetaFenceRelease), &[]);
1100 b.inst(InstData::new(Opcode::MetaFenceAcquire), &[]);
1101
1102 let reason = names.intern("hand written assembly, checked by review");
1103 b.inst(
1104 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
1105 &[],
1106 );
1107 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
1108
1109 let scope = MemInfo {
1113 size: 112,
1114 align: 8,
1115 order: MemOrder::NotAtomic,
1116 tbaa: None,
1117 owns: 0,
1118 restrict: Restrict { clique: 1, base: 2 },
1119 };
1120 let args = b.func().push_values(&[p]);
1121 let extra = Extra::Mem(b.func().add_mem(scope));
1122 b.inst(InstData { args, extra, ..InstData::new(Opcode::RestrictEnter) }, &[]);
1123 let args = b.func().push_values(&[p]);
1124 b.inst(InstData { args, ..InstData::new(Opcode::RestrictLeave) }, &[]);
1125 b.ret(&[p]);
1126
1127 func.set_facts(
1128 p,
1129 Facts {
1130 bounds: Some(Bounds { lo: p, ext: off }),
1131 init: Some(4),
1132 align: Some(8),
1133 live: true,
1134 },
1135 );
1136 func.set_facts(derived, Facts { align: Some(4), ..Facts::NONE });
1137 module.add_func(func);
1138
1139 assert_eq!(print(&module, &names), crate::fixtures::SAFETY);
1140 }
1141
1142 #[test]
1143 fn one_of_almost_everything() {
1144 let mut names = Interner::new();
1145 let mut module = Module::new(names.intern("zoo.c"), &target());
1146 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1147 name: names.intern("int"),
1148 parent: None,
1149 offset: 0,
1150 }));
1151
1152 let i32_ = Type::int(32);
1153 let i64_ = Type::int(64);
1154 let f64_ = Type::float(crate::Float::F64);
1155 let mut func = Func::new(
1156 names.intern("zoo"),
1157 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
1158 );
1159 let entry = func.create_block();
1160 let n = func.append_param(entry, i32_);
1161 let p = func.append_param(entry, Type::PTR);
1162 let middle = func.create_block();
1163 let other = func.create_block();
1164 let exit = func.create_block();
1165 let taken = func.append_param(exit, i32_);
1166 let arrival = func.create_block();
1167
1168 let mut b = Builder::new(&mut func, entry);
1169 let minus_one = b.iconst(i64_, -1);
1170 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
1171 let seven = b.func().add_imm(Imm::int(7, i32_));
1172 let vector = b.value(
1173 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
1174 Type::vector(i32_, 4),
1175 );
1176 let stack = b.func().add_mem(MemInfo {
1177 size: 16,
1178 align: 8,
1179 order: MemOrder::NotAtomic,
1180 tbaa: None,
1181 owns: 0,
1182 restrict: Restrict::NONE,
1183 });
1184 let slot = b.value(
1185 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
1186 Type::PTR,
1187 );
1188 let args = b.func().push_values(&[slot, minus_one]);
1189 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1190 let plain = MemInfo {
1191 size: 0,
1192 align: 4,
1193 order: MemOrder::NotAtomic,
1194 tbaa: Some(int_node),
1195 owns: 0,
1196 restrict: Restrict::NONE,
1197 };
1198 let loaded = b.load(i32_, addr, plain, Flags::NONE);
1199 b.store(loaded, addr, plain, Flags::VOLATILE);
1200
1201 let atomic = b.func().add_mem(MemInfo {
1202 size: 0,
1203 align: 4,
1204 order: MemOrder::SeqCst,
1205 tbaa: None,
1206 owns: 0,
1207 restrict: Restrict::NONE,
1208 });
1209 let args = b.func().push_values(&[addr, n]);
1210 let old = b.value(
1211 InstData {
1212 args,
1213 extra: Extra::Rmw(RmwOp::Add, atomic),
1214 ..InstData::new(Opcode::AtomicRmw)
1215 },
1216 i32_,
1217 );
1218 let args = b.func().push_values(&[addr, old, n]);
1219 b.inst(
1220 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
1221 &[i32_, Type::I1],
1222 );
1223 b.inst(
1224 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
1225 &[],
1226 );
1227 b.unary(Opcode::SExt, n, i64_);
1228 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
1229 let args = b.func().push_values(&[n, n]);
1230 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
1231 let puts = b.func().add_signature(
1232 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1233 );
1234 b.call_varargs(
1235 names.intern("puts"),
1236 puts,
1237 &[p, slot],
1238 &[Abi::ByVal { size: 16, align: 8, drains: Drains::Nothing }],
1239 );
1240 let indirect =
1241 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
1242 let varargs = b.func().push_abis(&[]);
1243 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
1244 let args = b.func().push_values(&[p, n]);
1245 b.value(
1246 InstData {
1247 args,
1248 extra: Extra::Call(info),
1249 flags: Flags::NOFREE,
1250 ..InstData::new(Opcode::CallIndirect)
1251 },
1252 i32_,
1253 );
1254 let copy = b.func().add_mem(MemInfo {
1255 size: 16,
1256 align: 8,
1257 order: MemOrder::NotAtomic,
1258 tbaa: None,
1259 owns: 0,
1260 restrict: Restrict::NONE,
1261 });
1262 let args = b.func().push_values(&[slot, p]);
1263 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
1264 let asm = b.func().add_asm(AsmInfo {
1265 template: names.intern("pause"),
1266 constraints: names.intern(""),
1267 clobbers: names.intern("memory"),
1268 targets: crate::inst::BlockCallList::EMPTY,
1269 });
1270 b.inst(
1271 InstData {
1272 flags: Flags::VOLATILE,
1273 extra: Extra::Asm(asm),
1274 ..InstData::new(Opcode::InlineAsm)
1275 },
1276 &[],
1277 );
1278 let object = b.func().add_mem(MemInfo {
1279 size: 16,
1280 align: 8,
1281 order: MemOrder::NotAtomic,
1282 tbaa: None,
1283 owns: 0,
1284 restrict: Restrict::NONE,
1285 });
1286 let slots = b.func().push_slots(&[
1287 Slot::Integer { offset: 0, size: 8 },
1288 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
1289 ]);
1290 let read = b.func().add_va_object(VaInfo { mem: object, slots });
1291 let args = b.func().push_values(&[p]);
1292 b.value(
1293 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
1294 Type::PTR,
1295 );
1296 let args = b.func().push_values(&[vector]);
1297 b.value(
1298 InstData {
1299 args,
1300 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
1301 ..InstData::new(Opcode::TargetIntrinsic)
1302 },
1303 i32_,
1304 );
1305 b.jump(middle, &[]);
1306
1307 let mut b = Builder::new(&mut func, middle);
1308 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
1309 let default = BlockCall::to(other);
1310 let first = BlockCall::new(exit, b.func().push_values(&[n]));
1311 let second = BlockCall::to(other);
1312 let targets = b.func().push_block_calls(&[default, first, second]);
1313 let switch = b.func().add_switch(SwitchInfo { targets, cases });
1314 let args = b.func().push_values(&[n]);
1315 b.inst(
1316 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
1317 &[],
1318 );
1319
1320 let mut b = Builder::new(&mut func, other);
1321 let address = b.block_addr(arrival);
1322 b.indirect_br(address, &[arrival]);
1323
1324 let mut b = Builder::new(&mut func, exit);
1325 b.ret(&[taken]);
1326
1327 let mut b = Builder::new(&mut func, arrival);
1328 let call = BlockCall::new(exit, b.func().push_values(&[n]));
1329 let targets = b.func().push_block_calls(&[call]);
1330 let goto = b.func().add_asm(AsmInfo {
1331 template: names.intern("jmp %l0"),
1332 constraints: names.intern(""),
1333 clobbers: names.intern(""),
1334 targets,
1335 });
1336 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
1337
1338 module.add_func(func);
1339
1340 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
1341 }
1342
1343 #[test]
1344 fn the_shapes_a_symbol_comes_in() {
1345 let mut names = Interner::new();
1346 let mut module = Module::new(names.intern("data.c"), &target());
1347
1348 let i32_ = Type::int(32);
1349 let text = module.push_bytes(b"hi\x00\xff\"\\");
1350 let entry_name = names.intern("hi.str");
1351 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
1352 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
1353 let far = module.add_reloc(Reloc { symbol: entry_name, addend: 1, size: 4 });
1354 let seven = module.add_imm(Imm::int(7, i32_));
1355 let image = module.push_data(&[
1356 Datum::Bytes(text),
1357 Datum::Zero(2),
1358 Datum::Scalar { ty: i32_, value: seven },
1359 Datum::Addr(forward),
1360 Datum::Addr(backward),
1361 Datum::Away(far),
1362 ]);
1363 let mut table = Global::new(names.intern("table"), 32, 8);
1364 table.init = Some(image);
1365 table.constant = true;
1366 table.section = Some(names.intern(".rodata.rel"));
1367 module.add_global(table);
1368
1369 let mut errno = Global::new(names.intern("errno"), 4, 4);
1370 errno.tls = Some(TlsModel::InitialExec);
1371 errno.visibility = Visibility::Hidden;
1372 module.add_global(errno);
1373
1374 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1379 nothing.init = Some(module.push_data(&[]));
1380 nothing.linkage = Linkage::Internal;
1381 module.add_global(nothing);
1382
1383 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1384 alias.linkage = Linkage::Weak;
1385 module.add_alias(alias);
1386 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1387 memcpy.kind = AliasKind::IFunc;
1388 memcpy.visibility = Visibility::Protected;
1389 module.add_alias(memcpy);
1390
1391 let mut puts = Func::new(
1392 names.intern("puts"),
1393 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1394 );
1395 puts.linkage = Linkage::External;
1396 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1397 module.add_func(puts);
1398
1399 let mut helper =
1400 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1401 helper.linkage = Linkage::Internal;
1402 helper.section = Some(names.intern(".text.hot"));
1403 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1404 let block = helper.create_block();
1405 let mut b = Builder::new(&mut helper, block);
1406 let one = b.iconst(i32_, 1);
1407 b.ret(&[one, one]);
1408 module.add_func(helper);
1409
1410 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1411 }
1412
1413 #[test]
1414 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1415 let mut names = Interner::new();
1416 let module = Module::new(names.intern("abi.c"), &target());
1417 let mut func = Func::new(
1418 names.intern("f"),
1419 Signature::new()
1420 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1421 .and_param(Param::with_abi(
1422 Type::PTR,
1423 Abi::ByVal { size: 16, align: 8, drains: Drains::Nothing },
1424 ))
1425 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1426 .and_param(Param::new(Type::int(32))),
1427 );
1428 let entry = func.create_block();
1429 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1430 func.append_param(entry, param);
1431 }
1432 let mut b = Builder::new(&mut func, entry);
1433 b.ret(&[]);
1434
1435 assert_eq!(
1436 print_func(&module, &func, &names),
1437 "\
1438func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1439block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1440 return
1441}
1442"
1443 );
1444 }
1445
1446 #[test]
1447 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1448 let mut names = Interner::new();
1449 let module = Module::new(names.intern("varargs.c"), &target());
1450 let i32_ = Type::int(32);
1451 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1452 let entry = func.create_block();
1453 let p = func.append_param(entry, Type::PTR);
1454 let mut b = Builder::new(&mut func, entry);
1455 let sig = b.func().add_signature(
1456 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1457 );
1458 let one = b.iconst(i32_, 1);
1459 b.call_varargs(
1460 names.intern("printf"),
1461 sig,
1462 &[p, one, p],
1463 &[Abi::Plain, Abi::ByVal { size: 24, align: 8, drains: Drains::Floats }],
1464 );
1465 b.ret(&[]);
1466
1467 assert_eq!(
1468 print_func(&module, &func, &names),
1469 "\
1470func @f(ptr), linkage(external) {
1471block0(%0: ptr):
1472 %1 = iconst.i32 1
1473 %2 = call @printf(%0, %1, %0 byval(24, align 8, drains floats)) : (ptr, ...) -> i32
1474 return
1475}
1476"
1477 );
1478 }
1479
1480 #[test]
1481 fn numbering_follows_the_text_and_not_the_tables() {
1482 let mut names = Interner::new();
1487 let mut module = Module::new(names.intern("order.c"), &target());
1488 let i32_ = Type::int(32);
1489 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1490 let entry = func.create_block();
1491 let middle = func.create_block();
1492 let exit = func.create_block();
1493 let arrived = func.append_param(exit, i32_);
1494
1495 let mut b = Builder::new(&mut func, exit);
1496 b.ret(&[arrived]);
1497 let mut b = Builder::new(&mut func, middle);
1498 let two = b.iconst(i32_, 2);
1499 b.jump(exit, &[two]);
1500 let mut b = Builder::new(&mut func, entry);
1501 b.jump(middle, &[]);
1502 module.add_func(func);
1503
1504 assert_eq!(
1505 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1506 "\
1507func @f() -> i32, linkage(external) {
1508block0:
1509 jump block1
1510
1511block1:
1512 %0 = iconst.i32 2
1513 jump block2(%0)
1514
1515block2(%1: i32):
1516 return %1
1517}
1518"
1519 );
1520 }
1521}